当前位置: 首页 > article >正文

C# 实现 OPCClient(使用 OPCDAAuto.dll)

OPC Client 简介

OPC(OLE for Process Control)是一种工业自动化通信标准,旨在实现不同制造和自动化设备之间的数据交换。通过定义一组标准化接口,OPC使得工业应用能够从不同厂商的设备中读取和写入数据,从而提高了设备、系统和应用程序的互操作性,增强了自动化系统的灵活性和可扩展性。

OPC Client 是实现 OPC 通信协议的应用程序,负责与 OPC 服务器交互,获取或写入数据。其核心功能包括连接 OPC 服务器,读取过程数据(如温度、压力、流量等),并将数据传递给其他应用(如监控系统、控制系统或数据库)进行进一步处理和显示。

OPCAutomation.dll 简介

OPCAutomation.dll 是一个动态链接库(DLL),用于实现 OPC 客户端与 OPC 服务器之间的自动化交互。它简化了程序员与 OPC 系统的交互,特别是在采用基于 COM(组件对象模型)技术的 OPC 协议时。OPCAutomation.dll 使得 OPC 客户端可以更加便捷地访问 OPC 服务器提供的数据。

实现步骤

首先,我们需要将 OPCAutomation.dll 复制并注册到系统中(如果你尚未获得该文件,可以在文末的下载资源中获取)。以下是 setup64.bat 文件的内容:

@ECHO ON
set systemdir=C:\Windows\SysWOW64
@ECHO Copying OPCDAAuto.dll to system directory...

copy OPCDAAuto.dll %systemdir%
cd %systemdir%
@ECHO Registering OPCDAAuto.dll...

REGSVR32 /s OPCDAAuto.dll
aprxdist.exe
opcenum /regserver

注意:上述脚本适用于64位操作系统。如果你使用的是32位操作系统,请将路径修改为 C:\Windows\System32

完成注册后,在项目中引用 COM 组件 OPC DA Automation Wrapper 2.02,即可开始开发 OPC 客户端。

界面展示

本程序实现了从 OPC 服务器读取和写入数据点的功能。下图展示了程序的界面效果。

代码实现

以下是实现的代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Collections;
using OPCAutomation;

namespace OPCClient
{
    public partial class MainFrom : Form
    {
        public MainFrom()
        {
            InitializeComponent(); // 初始化窗体组件
        }

        #region 私有变量
        /// <summary>
        /// OPCServer Object, 用于连接到OPC服务器
        /// </summary>
        OPCServer KepServer;
        /// <summary>
        /// OPCGroups Object, 用于管理OPC组
        /// </summary>
        OPCGroups KepGroups;
        /// <summary>
        /// OPCGroup Object, 用于表示一个OPC组
        /// </summary>
        OPCGroup KepGroup;
        /// <summary>
        /// OPCItems Object, 用于管理OPC项
        /// </summary>
        OPCItems KepItems;
        /// <summary>
        /// OPCItem Object, 表示一个OPC项
        /// </summary>
        OPCItem KepItem;
        /// <summary>
        /// 主机IP地址
        /// </summary>
        string strHostIP = "";
        /// <summary>
        /// 主机名称
        /// </summary>
        string strHostName = "";
        /// <summary>
        /// 连接状态标志
        /// </summary>
        bool opc_connected = false;
        /// <summary>
        /// 客户端句柄
        /// </summary>
        int itmHandleClient = 0;
        /// <summary>
        /// 服务端句柄
        /// </summary>
        int itmHandleServer = 0;
        #endregion

        #region 方法
        /// <summary>
        /// 枚举并列出本地OPC服务器
        /// </summary>
        private void GetLocalServer()
        {
            // 获取本地计算机的IP地址和计算机名称
            IPHostEntry IPHost = Dns.Resolve(Environment.MachineName);
            if (IPHost.AddressList.Length > 0)
            {
                strHostIP = IPHost.AddressList[0].ToString(); // 获取第一个IP地址
            }
            else
            {
                return;
            }
            // 通过IP地址获取计算机名称,可用于局域网环境
            IPHostEntry ipHostEntry = Dns.GetHostByAddress(strHostIP);
            strHostName = ipHostEntry.HostName.ToString(); // 获取计算机名称

            // 获取本地计算机上的OPC服务器列表
            try
            {
                KepServer = new OPCServer(); // 创建OPCServer实例
                object serverList = KepServer.GetOPCServers(strHostName); // 获取OPC服务器列表

                foreach (string turn in (Array)serverList)
                {
                    cmbServerName.Items.Add(turn); // 将OPC服务器名称添加到下拉列表
                }

                cmbServerName.SelectedIndex = 0; // 默认选中第一个OPC服务器
                btnConnServer.Enabled = true; // 启用连接按钮
            }
            catch(Exception err)
            {
                // 显示错误信息
                MessageBox.Show("枚举本地OPC服务器出错:" + err.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }

        /// <summary>
        /// 创建一个新的OPC组
        /// </summary>
        private bool CreateGroup()
        {
            try
            {
                // 获取OPC服务器中的所有组
                KepGroups = KepServer.OPCGroups;
                // 创建新组,并设置名称
                KepGroup = KepGroups.Add("OPCDOTNETGROUP");
                SetGroupProperty(); // 设置组的属性

                // 注册数据变化事件和写入完成事件
                KepGroup.DataChange += new DIOPCGroupEvent_DataChangeEventHandler(KepGroup_DataChange);
                KepGroup.AsyncWriteComplete += new DIOPCGroupEvent_AsyncWriteCompleteEventHandler(KepGroup_AsyncWriteComplete);

                KepItems = KepGroup.OPCItems; // 获取组中的所有项
            }
            catch (Exception err)
            {
                MessageBox.Show("创建组出现错误:" + err.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return false; // 创建组失败
            }
            return true; // 创建组成功
        }

        /// <summary>
        /// 设置OPC组的属性
        /// </summary>
        private void SetGroupProperty()
        {
            KepServer.OPCGroups.DefaultGroupIsActive = Convert.ToBoolean(txtGroupIsActive.Text); // 设置组的活动状态
            KepServer.OPCGroups.DefaultGroupDeadband = Convert.ToInt32(txtGroupDeadband.Text); // 设置死区值
            KepGroup.UpdateRate = Convert.ToInt32(txtUpdateRate.Text); // 设置更新频率
            KepGroup.IsActive = Convert.ToBoolean(txtIsActive.Text); // 设置组是否活动
            KepGroup.IsSubscribed = Convert.ToBoolean(txtIsSubscribed.Text); // 设置是否订阅
        }

        /// <summary>
        /// 列出OPC服务器中所有节点
        /// </summary>
        /// <param name="oPCBrowser">OPC浏览器对象,用于浏览服务器中的节点</param>
        private void RecurBrowse(OPCBrowser oPCBrowser)
        {
            oPCBrowser.ShowBranches(); // 展开分支
            oPCBrowser.ShowLeafs(true); // 展开叶子节点
            foreach (object turn in oPCBrowser)
            {
                listBox1.Items.Add(turn.ToString()); // 将每个节点添加到列表框
            }
        }

        /// <summary>
        /// 获取OPC服务器信息并显示在窗体状态栏
        /// </summary>
        private void GetServerInfo()
        {
            // 显示OPC服务器的启动时间和版本信息
            tsslServerStartTime.Text = "开始时间:" + KepServer.StartTime.ToString() + "    ";
            tsslversion.Text = "版本:" + KepServer.MajorVersion.ToString() + "." + KepServer.MinorVersion.ToString() + "." + KepServer.BuildNumber.ToString();
        }

        /// <summary>
        /// 连接到远程OPC服务器
        /// </summary>
        /// <param name="remoteServerIP">远程OPC服务器IP地址</param>
        /// <param name="remoteServerName">远程OPC服务器名称</param>
        private bool ConnectRemoteServer(string remoteServerIP, string remoteServerName)
        {
            try
            {
                // 连接到远程OPC服务器
                KepServer.Connect(remoteServerName, remoteServerIP);

                // 检查连接状态
                if (KepServer.ServerState == (int)OPCServerState.OPCRunning)
                {
                    tsslServerState.Text = "已连接到-" + KepServer.ServerName + "   "; // 显示连接成功信息
                }
                else
                {
                    tsslServerState.Text = "状态:" + KepServer.ServerState.ToString() + "   "; // 显示当前服务器状态
                }
            }
            catch (Exception err)
            {
                // 如果连接失败,显示错误信息
                MessageBox.Show("连接远程服务器出现错误:" + err.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return false; // 返回连接失败
            }
            return true; // 返回连接成功
        }
        #endregion

        #region 事件
        /// <summary>
        /// 写入TAG值完成后的回调事件
        /// </summary>
        /// <param name="TransactionID">事务ID</param>
        /// <param name="NumItems">写入项数量</param>
        /// <param name="ClientHandles">客户端句柄数组</param>
        /// <param name="Errors">错误信息数组</param>
        void KepGroup_AsyncWriteComplete(int TransactionID, int NumItems, ref Array ClientHandles, ref Array Errors)
        {
            lblState.Text = ""; // 清空状态标签
            for (int i = 1; i <= NumItems; i++)
            {
                // 显示每个写入项的事务ID、客户端句柄和错误信息
                lblState.Text += "Tran:" + TransactionID.ToString() + "   CH:" + ClientHandles.GetValue(i).ToString() + "   Error:" + Errors.GetValue(i).ToString();
            }
        }

        /// <summary>
        /// 每当OPC项数据发生变化时触发的事件
        /// </summary>
        /// <param name="TransactionID">事务ID</param>
        /// <param name="NumItems">项的数量</param>
        /// <param name="ClientHandles">客户端句柄数组</param>
        /// <param name="ItemValues">项的当前值</param>
        /// <param name="Qualities">项的质量信息</param>
        /// <param name="TimeStamps">项的时间戳</param>
        void KepGroup_DataChange(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)
        {
            // 更新显示的TAG值、品质和时间戳
            for (int i = 1; i <= NumItems; i++)
            {
                this.txtTagValue.Text = ItemValues.GetValue(i).ToString();
this.txtQualities.Text = Qualities.GetValue(i).ToString(); // 显示项的质量
                this.txtTimeStamp.Text = TimeStamps.GetValue(i).ToString(); // 显示项的时间戳
            }
        }
        #endregion

        #region 窗体事件
        /// <summary>
        /// 窗体加载事件
        /// </summary>
        private void MainForm_Load(object sender, EventArgs e)
        {
            // 获取本地计算机上的OPC服务器列表
            GetLocalServer();
        }

        /// <summary>
        /// 连接到所选的OPC服务器
        /// </summary>
        private void btnConnServer_Click(object sender, EventArgs e)
        {
            if (opc_connected)
            {
                MessageBox.Show("已经连接到OPC服务器", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }

            try
            {
                // 连接到所选的OPC服务器
                string selectedServer = cmbServerName.SelectedItem.ToString();
                if (ConnectRemoteServer(strHostIP, selectedServer))
                {
                    // 连接成功后创建组
                    if (CreateGroup())
                    {
                        opc_connected = true; // 设置连接状态为已连接
                        MessageBox.Show("连接成功", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                }
            }
            catch (Exception err)
            {
                // 连接失败,弹出错误信息
                MessageBox.Show("连接失败: " + err.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// 断开与OPC服务器的连接
        /// </summary>
        private void btnDisconnectServer_Click(object sender, EventArgs e)
        {
            try
            {
                if (opc_connected)
                {
                    KepServer.Disconnect(); // 断开与服务器的连接
                    opc_connected = false; // 设置连接状态为未连接
                    MessageBox.Show("断开连接成功", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    tsslServerState.Text = "未连接"; // 更新状态栏
                }
                else
                {
                    MessageBox.Show("未连接到OPC服务器", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
            catch (Exception err)
            {
                // 断开连接时发生错误
                MessageBox.Show("断开连接失败: " + err.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        #endregion
    }
}

/// <summary>
/// 【按钮】设置OPC组属性
/// </summary>
private void btnSetGroupPro_Click(object sender, EventArgs e)
{
    // 调用设置OPC组属性的方法
    SetGroupProperty();
}

/// <summary>
/// 【按钮】连接到远程OPC服务器
/// </summary>
private void btnConnLocalServer_Click(object sender, EventArgs e)
{
    try
    {
        // 使用文本框中的远程服务器IP和下拉框中的服务器名称连接到远程OPC服务器
        if (!ConnectRemoteServer(txtRemoteServerIP.Text, cmbServerName.Text))
        {
            // 连接失败则退出
            return;
        }

        // 连接成功后,启用设置组属性的按钮
        btnSetGroupPro.Enabled = true;

        // 设置OPC连接状态为已连接
        opc_connected = true;

        // 获取服务器信息
        GetServerInfo();

        // 创建OPC浏览器并递归浏览服务器中的项
        RecurBrowse(KepServer.CreateBrowser());

        // 创建OPC组
        if (!CreateGroup())
        {
            // 如果创建组失败,则退出
            return;
        }
    }
    catch (Exception err)
    {
        // 如果发生异常,弹出提示框显示错误信息
        MessageBox.Show("初始化出错:" + err.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);
    }
}

/// <summary>
/// 【按钮】将数据写入OPC项
/// </summary>
private void btnWrite_Click(object sender, EventArgs e)
{
    // 获取指定OPC项
    OPCItem bItem = KepItems.GetOPCItem(itmHandleServer);

    // 创建一个包含服务器句柄的整数数组
    int[] temp = new int[2] { 0, bItem.ServerHandle };
    Array serverHandles = (Array)temp;

    // 创建一个包含要写入值的数组
    object[] valueTemp = new object[2] { "", txtWriteTagValue.Text };
    Array values = (Array)valueTemp;

    // 定义一个错误数组,用于接收写入操作的错误信息
    Array Errors;

    // 定义一个取消ID,用于异步操作的取消
    int cancelID;

    // 异步写入数据到OPC组
    KepGroup.AsyncWrite(1, ref serverHandles, ref values, out Errors, 2009, out cancelID);

    // 这行代码也可以触发写入,但它不会触发写入事件
    // KepItem.Write(txtWriteTagValue.Text);

    // 强制进行垃圾回收,释放内存
    GC.Collect();
}

注释说明:

窗体事件:

  • MainForm_Load:窗体加载时,调用 GetLocalServer 方法获取本地计算机上的 OPC 服务器列表,并将其显示在下拉框中。
  • btnConnServer_Click:点击“连接服务器”按钮时,连接到选中的 OPC 服务器,并创建 OPC 组。
  • btnDisconnectServer_Click:点击“断开连接”按钮时,断开与 OPC 服务器的连接。
  • btnSetGroupPro_Click:点击按钮时,调用 SetGroupProperty() 方法设置组的属性。
  • btnConnLocalServer_Click:连接远程 OPC 服务器,使用输入的 IP 和服务器名称进行连接。如果连接成功,启用设置组属性按钮,获取服务器信息并浏览 OPC 服务器的内容。最后,尝试创建 OPC 组。
  • btnWrite_Click:点击时,读取 txtWriteTagValue.Text 中的文本并将其写入指定的 OPC 项。使用异步写入方法 AsyncWrite 向 OPC 服务器写入数据,同时进行垃圾回收释放内存。

OPC 相关方法:

  • GetLocalServer:枚举本地计算机上的 OPC 服务器列表,并将其显示在界面上。
  • CreateGroup:创建新的 OPC 组,并设置相关属性。
  • SetGroupProperty:设置 OPC 组的属性,如活动状态、死区值和更新频率等。
  • RecurBrowse:通过 OPC 浏览器递归列出 OPC 服务器中的所有节点(此函数定义了但未在代码中使用)。
  • GetServerInfo:获取 OPC 服务器的启动时间和版本,并在状态栏显示。
  • ConnectRemoteServer:通过 IP 和服务器名称连接到远程 OPC 服务器。

OPC 事件:

  • KepGroup_AsyncWriteComplete:在写入 OPC 项数据完成时触发,显示相关信息,如事务 ID、客户端句柄和错误信息等。
  • KepGroup_DataChange:当 OPC 组内的数据发生变化时触发,更新界面上的数据显示(如 TAG 值、质量、时间戳等)。

错误处理:

每个方法中都加入了错误处理,捕获异常并通过消息框显示错误信息,确保程序在出错时能够正确提示用户。


总结

本文介绍了如何使用 C# 实现一个 OPC Client,借助 OPCAutomation.dll 实现与 OPC 服务器的通信。通过简单的界面操作,程序可以连接 OPC 服务器、读取和写入数据,并能够响应 OPC 事件。实现过程中加入了错误处理,确保了程序的稳定性。

源码地址:https://download.csdn.net/download/weixin_44643352/90043680?spm=1001.2014.3001.5501


http://www.kler.cn/a/428834.html

相关文章:

  • 2024人工智能AI+制造业应用落地研究报告汇总PDF洞察(附原数据表)
  • 蓝桥杯3525 公因数匹配 | 枚举+数学
  • Nginx:通过upstream进行代理转发
  • 郑州大学2022级大三期末复习总结(数据库,传感器,嵌入式,人工智能,移动终端开发,计算机英语)
  • [STM32 HAL库]串口空闲中断+DMA接收不定长数据
  • 力扣 有效的括号
  • E217 PHP+MYSQL+LW+摄影工作室网站的设计与实现 源代码 配置文档 全套资料
  • Ubuntu 24上设置DNS服务器
  • 神经网络入门实战:(十八)Argmax函数的详细介绍,可以用来计算模型训练准确率
  • Java的Stream流:文件处理、排序与串并行流的全面指南
  • 智能方法求解-圆环内传感器节点最大最小距离分布
  • 后端返回前端的数据量过大解决方案
  • 最新基于R语言森林生态系统结构、功能与稳定性分析与可视化实践高级应用
  • 低级爬虫实现-记录HCIP云架构考试
  • 数字图像处理(15):图像平移
  • Fiddler 5.21.0 使用指南:过滤浏览器HTTP(S)流量下(四)
  • 基于gitlab API刷新MR的commit的指定status
  • 【Unity高级】如何动态调整物体透明度
  • Linux-Regmap实验
  • Go database/sql包源码分析
  • ShardingSphere 数据库中间件
  • k8s 为什么需要Pod?
  • 高级java每日一道面试题-2024年12月05日-JVM篇-什么是TLAB?
  • 计算机键盘的演变 | 键盘键名称及其功能 | 键盘指法
  • 软件无线电安全之GNU Radio基础(下)
  • 英文论文翻译成中文,怎样翻译更地道?