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