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

在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服务端】

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

斯内科

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值