关于OPC-UA客户端调用服务端方法CallMethod节点的问题

news2025/1/13 7:52:31

在OpcUaClient中可以通过CallMethodByNodeId调用方法节点

        //
        // 摘要:
        //     call a server method
        //
        // 参数:
        //   tagParent:
        //     方法的父节点tag
        //
        //   tag:
        //     方法的节点tag
        //
        //   args:
        //     传递的参数
        //
        // 返回结果:
        //     输出的结果值
        public object[] CallMethodByNodeId(string tagParent, string tag, params object[] args);

注意,调用方法节点时,必须传入指定的参数类型的值,不能传入可以隐式转化的实参

比如在OpcServer服务器上有个方法节点,方法名为readJob,需要传入两个参数 (byte sourceNumber, short jobNo),返回一个工作名称string jobName
       方法整体描述为 string readJob(byte sourceNumber, short jobNo)

传入的实参 new object[]{1,23};会抛出异常,因1和23在C#中是Int32类型,不是byte,short类型

如果需要调用成功,传入的实参必须是new object[]{(byte)1, (short)23};

一、新建Winform应用程序OpcUaCallMethodDemo,将默认的Form1修改为FormOpcUaCallMethod。

并添加Opc客户端类库的引用以及其他必须相关类库文件。

OpcUaHelper.dll

Opc.Ua.Core.dll

Opc.Ua.Client.dll

二、新建类GumOpcUaClientUtil,用于连接Opc服务已经读写标签,调用Opc方法等

GumOpcUaClientUtil.cs源代码下:

using Opc.Ua;
using OpcUaHelper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace OpcUaCallMethodDemo
{
    /// <summary>
    /// 涂胶OPC客户端:连接OPC服务端,读、写标签操作
    /// </summary>
    public class GumOpcUaClientUtil
    {
        /// <summary>
        /// 定义一个操作OPC的客户端对象
        /// </summary>
        private static OpcUaClient m_OpcUaClient = new OpcUaClient();

        /// <summary>
        /// 连接到Opc服务端
        /// </summary>
        /// <param name="serverAddress"></param>
        /// <param name="enabledAnonymousLogin"></param>
        /// <param name="loginUserName"></param>
        /// <param name="loginPassword"></param>
        /// <returns></returns>
        public static bool ConnectOpcServer(string serverAddress, bool enabledAnonymousLogin, string loginUserName, string loginPassword, Action<string> LogToShow)
        {
            bool isConnected = false;
            if (enabledAnonymousLogin)
            {
                //匿名登录
                m_OpcUaClient.UserIdentity = new UserIdentity(new AnonymousIdentityToken());
            }
            else
            {
                //用户名密码登录
                m_OpcUaClient.UserIdentity = new UserIdentity(loginUserName, loginPassword);
            }
            try
            {
                LogToShow($"准备连接OPC服务端【{serverAddress}】,匿名登录【{enabledAnonymousLogin}】,用户名【{loginUserName}】,密码【{loginPassword}】");
                Task task = m_OpcUaClient.ConnectServer(serverAddress);
                task.Wait(5000);
                isConnected = m_OpcUaClient.Connected;
                //HansCommon.SysMsg.MySystemMsg.LogToShow($"获取到 连接OPC服务端【{serverAddress}】结果:{isConnected}", true);
            }
            catch (Exception ex)
            {
                string exMsg = $"连接OPC服务【{serverAddress}】失败,错误原因:{ex.Message}";
                LogToShow(exMsg);
                MessageBox.Show(exMsg, "错误");
            }
            return isConnected;
        }

        /// <summary>
        /// 断开OPC服务
        /// </summary>
        public static void Disconnect()
        {
            m_OpcUaClient.RemoveAllSubscription();
            m_OpcUaClient.Disconnect();
        }

        /// <summary>
        /// 调用OPC的方法
        /// </summary>
        /// <param name="tagParent">方法的父标签路径</param>
        /// <param name="methodName">方法的标签全路径</param>
        /// <param name="args">参数列表,没有参数请输入null</param>
        /// <returns></returns>
        public static object[] CallMethodNode(Action<string> LogToShow, string tagParent, string methodName, params object[] args) 
        {
            try
            {
                object[] methodResult = m_OpcUaClient.CallMethodByNodeId(tagParent, methodName, args);
                return methodResult;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                LogToShow($"调用OPC的方法时出错{ex.Message}");
                return null;
            }
        }

        /// <summary>
        /// 读取某一个标签
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tagName"></param>
        /// <returns></returns>
        public static bool ReadTagNode<T>(string tagName, Action<string> LogToShow, out T tResult)
        {
            tResult = default(T);
            DataValue dataValue = m_OpcUaClient.ReadNode(new NodeId(tagName));
            if (dataValue == null || dataValue.WrappedValue == Variant.Null)
            {
                string exMsg = $"[{tagName}]项读取失败,可能是①未连接的opc服务或者连接已断开.②标签路径或数据类型设置错误,注意:标签名区分大小写.③没有权限读取数据";
                LogToShow(exMsg);
                return false;
            }
            else
            {
                object objValue = dataValue.WrappedValue.Value;
                tResult = (T)objValue;
            }
            return true;
        }

        public static object ReadTagNode(string tagName, Action<string> LogToShow)
        {
            DataValue dataValue = m_OpcUaClient.ReadNode(new NodeId(tagName));
            if (dataValue == null || dataValue.WrappedValue == Variant.Null)
            {
                string exMsg = $"[{tagName}]项读取失败,可能是①未连接的opc服务或者连接已断开.②标签路径或数据类型设置错误,注意:标签名区分大小写.③没有权限读取数据";
                LogToShow(exMsg);
                return null;
            }
            else
            {
                object objValue = dataValue.WrappedValue.Value;
                return objValue;
            }
        }


        /// <summary>
        /// 批量读取
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tagNames"></param>
        /// <returns></returns>
        public static List<T> ReadNodeList<T>(string[] tagNames, Action<string> LogToShow)
        {
            try
            {
                List<T> list = m_OpcUaClient.ReadNodes<T>(tagNames);
                return list;
            }
            catch (Exception ex)
            {
                string exMsg = $"批量读取节点集合出错【{string.Join(",", tagNames)}】,可能是①未连接的opc服务或者连接已断开.②存在标签路径或数据类型设置错误,注意:标签名区分大小写.③没有权限读取数据.异常信息:{ex.Message}";
                LogToShow(exMsg);
                throw new Exception(exMsg);
            }
        }

        /// <summary>
        /// 为指定的标签写入指定类型的值。注意:【写入的标签名 和 写入的值的类型一定要和服务端保持一致】
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tagName"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool WriteTagNode<T>(string tagName, Action<string> LogToShow, T value)
        {
            try
            {
                return m_OpcUaClient.WriteNode(tagName, value);
            }
            catch (Exception ex)
            {
                string exMsg = $"【{tagName}】项写入【{value}】失败,【写入的标签名 和 写入的值的类型一定要和服务端保持一致】.异常信息:{ex.Message}";
                LogToShow(exMsg);
                throw new Exception(exMsg);
            }
        }
    }
}

三、窗体FormOpcUaCallMethod设计器代码如下:

文件FormOpcUaCallMethod.Designer.cs


namespace OpcUaCallMethodDemo
{
    partial class FormOpcUaCallMethod
    {
        /// <summary>
        /// 必需的设计器变量。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 清理所有正在使用的资源。
        /// </summary>
        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows 窗体设计器生成的代码

        /// <summary>
        /// 设计器支持所需的方法 - 不要修改
        /// 使用代码编辑器修改此方法的内容。
        /// </summary>
        private void InitializeComponent()
        {
            this.rtxtMessage = new System.Windows.Forms.RichTextBox();
            this.btnConnect = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // rtxtMessage
            // 
            this.rtxtMessage.Location = new System.Drawing.Point(249, 12);
            this.rtxtMessage.Name = "rtxtMessage";
            this.rtxtMessage.ReadOnly = true;
            this.rtxtMessage.Size = new System.Drawing.Size(700, 495);
            this.rtxtMessage.TabIndex = 0;
            this.rtxtMessage.Text = "";
            // 
            // btnConnect
            // 
            this.btnConnect.Location = new System.Drawing.Point(27, 72);
            this.btnConnect.Name = "btnConnect";
            this.btnConnect.Size = new System.Drawing.Size(152, 36);
            this.btnConnect.TabIndex = 1;
            this.btnConnect.Text = "连接Opc服务端并测试";
            this.btnConnect.UseVisualStyleBackColor = true;
            this.btnConnect.Click += new System.EventHandler(this.btnConnect_Click);
            // 
            // FormOpcUaCallMethod
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(1018, 531);
            this.Controls.Add(this.btnConnect);
            this.Controls.Add(this.rtxtMessage);
            this.Name = "FormOpcUaCallMethod";
            this.Text = "使用OpcUaCallMethod调用OpcServer的方法节点";
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.RichTextBox rtxtMessage;
        private System.Windows.Forms.Button btnConnect;
    }
}

窗体FormOpcUaCallMethod代码如下:

文件FormOpcUaCallMethod.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace OpcUaCallMethodDemo
{
    public partial class FormOpcUaCallMethod : Form
    {
        public FormOpcUaCallMethod()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 显示推送消息
        /// </summary>
        /// <param name="msg"></param>
        private void DisplayMessage(string msg)
        {
            this.BeginInvoke(new Action(() =>
            {
                if (rtxtMessage.TextLength > 409600)
                {
                    rtxtMessage.Clear();
                }
                rtxtMessage.AppendText($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}->{msg}\n");
                rtxtMessage.ScrollToCaret();
            }));
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            //连接OPC服务端
            string serverAddress = "opc.tcp://192.168.1.20:48030";
            bool isRun = GumOpcUaClientUtil.ConnectOpcServer(serverAddress, false, "admin", "password123456", DisplayMessage);
            DisplayMessage($"连接OPC服务端【{serverAddress}】:{(isRun ? "成功" : "失败")}");
            if (!isRun)
            {
                DisplayMessage($"连接OPC服务端【{serverAddress}】失败,无法启动");
                return;
            }
            string parentNodeTag = "ns=2;s=A.B.C.ParentTag";
            string methodNode = "ns=2;s=A.B.C.ParentTag.readJob";

            //在OpcServer服务器上有个方法节点,方法名为readJob,需要传入两个参数 (byte sourceNumber, short jobNo),返回一个工作名称string jobName
            //方法整体描述为 string readJob(byte sourceNumber, short jobNo)

            short jobNo = 12;
            object[] resultArray = GumOpcUaClientUtil.CallMethodNode(DisplayMessage, parentNodeTag, methodNode, new object[] { (byte)1, jobNo });
            if (resultArray == null || resultArray.Length < 1)
            {
                DisplayMessage($"读取readJob节点出错,低于1个元素,当前工作编号为【{jobNo}】");
            }
            else
            {
                DisplayMessage($"读取readJob节点成功,返回信息【{resultArray[0]}】,当前工作编号为【{jobNo}】");
            }
        }
    }
}

四、测试如图:

【没有连接Opc服务端】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1519661.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

淘宝商品详情数据采集(商品属性,规格,价格,详情图等)

淘宝商品详情数据采集是一个涉及多个步骤的过程&#xff0c;主要目的是获取商品的各种详细信息&#xff0c;如商品属性、规格、价格、详情图等。以下是一个基本的采集流程&#xff1a; 确定采集目标&#xff1a;首先&#xff0c;需要明确要采集的淘宝商品范围&#xff0c;例如…

Filter实现请求日志记录

将锁有得外部访问都记录在日志文件里面&#xff0c;设计这个功能是为了&#xff08;为什么&#xff09;&#xff1a; 1. 在不引入Promentheus进行接口监控时&#xff0c;基于日志文件就可以实现整个项目得监控。 2. 当出现问题时&#xff0c;可以基于此进行流量重放。 效果如…

数据结构知识点汇总(持续更新版)

数据结构 一、绪论 检测知识&#xff1a; 1.1基本概念 以前的计算机 弹道计算机 现如今 主要运用于非数值的计算 基本概念和术语 数据&#xff1a;是信息的载体&#xff0c;描述客观事物属性的值&#xff0c;字符以及所有能输入到计算机中并被计算机程序识别和处理的符号的…

如何发布新闻稿?如何让媒体记者报道自己的企业?

目前&#xff0c;很多企业和个人创业者都知道用新闻稿或软文来做宣传&#xff0c;但是一般只可以发布到自己的微信公众号、百家号等自媒体平台&#xff0c;往往收效甚微。有企业找到小马识途营销顾问咨询原因&#xff0c;小马识途营销顾问分析自媒体上发软文效果不明显的原因主…

NFT交易市场(二)

编写脚本文件进行测试 nft合约代码 // SPDX-License-Identifier: MIT pragma solidity ^0.8.24;import "openzeppelin/contracts/interfaces/IERC20.sol"; import "openzeppelin/contracts/interfaces/IERC721.sol";contract Market {//将erc20初始化为一…

C++进阶之路---手把手带你学习AVL树

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、AVL树的概念 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支树&#…

2024年腾讯云新用户优惠活动4核8G12M配置15个月和一年费用

腾讯云轻量4核8G12M服务器配置446元一年&#xff0c;646元12个月&#xff0c;腾讯云轻量应用服务器具有100%CPU性能&#xff0c;系统盘为180GB SSD盘&#xff0c;12M带宽下载速度1536KB/秒&#xff0c;月流量2000GB&#xff0c;折合每天66.6GB流量&#xff0c;超出月流量包的流…

访问者模式(Visitor Pattern)

访问者模式 说明 访问者模式&#xff08;Visitor Pattern&#xff09;属于行为型模式&#xff0c;表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。 该模式是将数据结构与数据操作分离的设计模式&#xff0c;是…

蓝牙系列十三:协议栈L2CAP层

L2CAP 全称为逻辑链路控制与适配协议(Logical Link Control and Adaptation Protocol)&#xff0c;位于基带层之上&#xff0c;将基带层的数据分组交换为便于高层应用的数据分组格式&#xff0c;并提供协议复用和服务质量交换等功能。 该层属于主机的内容&#xff0c;位于HCI层…

3.3 ss-sp寄存器,栈的push和pop指令

汇编语言 1. 栈 栈是一种具有特殊的访问方式的存储空间它的特殊性就在于&#xff0c;最后进入这个空间的数据&#xff0c;最先出去。即先进后出 1.1 栈的基本操作 入栈&#xff1a;入栈就是将一个新的元素放到栈顶出栈&#xff1a;出栈就是从栈顶取出一个元素栈顶的元素总是…

汽车电子零部件(4):行泊一体ADAS

前言: 现阶段智能汽车行业正在大规模力推无限接近于L3的L2++或L2.9自动驾驶量产落地,类似于当初智能手机替换传统手机的行业机会期。智能汽车常见的智能驾驶功能包括: 行车场景:自适应巡航控制ACC;自动变道辅助ALC;交通拥堵辅助TJA;车道居中LCC;领航辅助NOA; 泊车场…

如何在代理的IP被封后立刻换下一个IP继续任务

目录 前言 1. IP池准备 2. 使用代理IP进行网络请求 3. 处理IP被封的情况 4. 完整代码示例 总结 前言 当进行某些网络操作时&#xff0c;使用代理服务器可以帮助我们隐藏真实IP地址以保护隐私&#xff0c;或者绕过一些限制。然而&#xff0c;经常遇到的问题是代理的IP可能…

AI新工具(20240313) 用户输入提示词创建任何GIF; 将任意人脸图片转换为另一幅图像的模型

✨ 1: GifShift 用户输入提示词创建任何GIF gifshift是一种工具&#xff0c;可以帮助用户创建任何GIF的新版本。使用gifshift的步骤如下&#xff1a; 上传一个GIF文件或者使用库中的一个GIF。 提供您想要的场景描述&#xff0c;最好选择一些具有代表性的角色&#xff0c;并进…

Android cmdline tools安装

打开AS 进入SDK Tools 看到了吗?那个打着勾的就是

从零开始搭建医保购药APP:技术选择与开发流程

医保购药APP作为一种创新的医疗服务工具&#xff0c;为用户提供了便捷的医保购药流程&#xff0c;同时也为医疗机构提供了更高效的管理和服务方式。今天小编将为大家讲解如何从零开始搭建一款医保购药APP&#xff0c;包括技术选择和开发流程。 一、技术选择 在搭建医保购药APP…

DS进阶:二叉搜索树

创作不易&#xff0c;感谢三连&#xff01; 一、二叉搜索树的概念 思考&#xff1a; 为什么二叉搜索树也叫做二叉查找树和二叉排序树呢&#xff1f;&#xff1f; 1、 本身树形结构用来存储数据相比顺序表和链表来说并不占有优势&#xff0c;他的最大优势就在于查找优势&…

Python面向对象构造函数:手把手教你如何玩转对象初始化

我们都知道&#xff0c;Python是一个面向对象的语言&#xff0c;这意味着我们可以用类来定义对象的属性和方法。而构造函数&#xff0c;就是当我们创建一个新的对象时&#xff0c;会自动调用的特殊方法。那么&#xff0c;如何玩转这个构造函数呢&#xff1f; 首先&#xff0c;…

DOM事件event/冒泡/委派/取消默认行为/dataset属性

1DOM获取CSS样式表里的样式: <head><meta charset"UTF-8"><title>Title</title><link rel"stylesheet" href"css/style.css"><style>body{color: red;}h1::after{content: hello;color: red;}</style&g…

微博热搜榜单采集,微博热搜榜单爬虫,微博热搜榜单解析,完整代码(话题榜+热搜榜+文娱榜和要闻榜)

文章目录 代码1. 话题榜2. 热搜榜3. 文娱榜和要闻榜 过程1. 话题榜2. 热搜榜3. 文娱榜和要闻榜 代码 1. 话题榜 import requests import pandas as pd import urllib from urllib import parse headers { authority: weibo.com, accept: application/json, text/pl…