> 技术文档 > 在 Unity 中使用 OpcUa_利用opcua和unity,已经有opcua服务器,请给出一个简单的实控虚项目案例。

在 Unity 中使用 OpcUa_利用opcua和unity,已经有opcua服务器,请给出一个简单的实控虚项目案例。


文章目录

  • 前言
    • UaExpert,用来查看OpcUa数据
    • OpcUaHelper,二次封装的opc ua客户端类库
    • OPC库文件
    • 测试用模拟服务工具
  • 一、OpcUaHelper,下载、解压、导入
  • 二、OPC库文件,下载、导入
  • 三、启动模拟服务
  • 四、代码示例

前言

2025年3月25日

Unity 2022.3.54f1

opcuahelper.2.1.3


UaExpert,用来查看OpcUa数据

OpcUa 通常是现场的工控系统提供的对外服务,原则上是有用户名密码的。
但是其提供匿名访问的逻辑,所以只要服务器允许匿名访问,一般直接用匿名访问即可。

UaExpert 下载地址:https://www.unified-automation.com/downloads/opc-ua-clients.html

OPC UA客户端工具 UaExpert 使用:https://blog.csdn.net/yinjl123456/article/details/122073781


OpcUaHelper,二次封装的opc ua客户端类库

一个通用的二次封装的opc ua客户端类库,基于.net 4.6.1创建,基于官方opc ua基金会跨平台库创建,方便的实现和OPC Server进行数据交互。本类库每个几个月就同步官方的类库。

项目地址:https://github.com/dathlin/OpcUaHelper


OPC库文件

https://github.com/OPCFoundation/UA-.NETStandard

The packaged library (OPC Foundation UA .NET Standard) for the Unity’s package manager.
https://github.com/e-s-unity/org.opc-foundation.ua.net-standard


测试用模拟服务工具

Prosys OPC UA模拟服务器,可注册下载免费试用版本:https://www.prosysopc.cn/products/opc-ua-simulation-server/

但是,
请您完整地填写所有的信息栏,包括电话号码和以您公司名为后缀的邮箱地址。您提供的要求和信息需要经历一个验证过程。
请注意,如果您填写的邮箱不是公司或学校的邮箱,或者其后缀与您填写的公司或学校的网址后缀不一致,您的下载要求将被拒绝。

博主分享的:https://blog.csdn.net/qq_43445867/article/details/131464711

一、OpcUaHelper,下载、解压、导入

下载依赖包含 .NETStandard 2.1 的 opcuahelper.2.1.3.nupkg。

将下载好的文件后缀改为 zip ,并解压。

根据 Unity 中设置的 API 兼容性级别 ,找到对应的 lib 文件夹下的 dll 放到 Plugins 文件夹中。

F:\\opcuahelper.2.1.3\\lib\\netstandard2.1\\OpcUaHelper.dll

此时会有如下报错,因为 opc库文件 没有导入,第二步就是去导入各种依赖。Assembly \'Assets/Plugins/OpcUaHelper.dll\' will not be loaded due to errors:Unable to resolve reference \'Opc.Ua.Client\'. Is the assembly missing or incompatible with the current platform?Reference validation can be disabled in the Plugin Inspector.Unable to resolve reference \'Opc.Ua.Core\'. Is the assembly missing or incompatible with the current platform?Reference validation can be disabled in the Plugin Inspector.

二、OPC库文件,下载、导入

使用 OPC UA .NET Standard 库,下载对应的 dll 及 所有的依赖(很多很多)。

幸好有大佬整理好放到了 Github , https://github.com/e-s-unity/org.opc-foundation.ua.net-standard ,去这里下载即可。

下载好的文件目录结构如下:
foundation.ua.net-standard
└───OPCFoundation.NetStandard
└───package.json

在 Unity 中 ,Add package from disk,选 package.json ,导入到引擎。

导入后,unity 中会报错说找不到巴拉巴拉,能 clear 掉就不用管他。
DirectoryNotFoundException: Could not find a part of the path “D:\\org.opc-foundation.ua.net-standard-main\\OPCFoundation.NetStandard\\Packages\\Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.2.1.3\\lib\\netstandard2.0\\Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.dll”

此时已经可以运行了。

三、启动模拟服务

opc ua simulation server

注意,代码只改了 ReadNode 测试,其他的是用别的项目测试的

在这里插入图片描述


四、代码示例

using UnityEngine;using OpcUaHelper;using Opc.Ua;using System;using System.Threading.Tasks;using System.Linq;using Opc.Ua.Client;public class OpcUaManager : MonoBehaviour{ public string serverUrl = \"opc.tcp://DESKTOP-HUBTO6G:53530/OPCUA/SimulationServer\"; public string nodeId = \"ns=5;s=Random1\"; public NodeId[] nodeIds = new NodeId[] { new NodeId(20857,1), //new NodeId(20858,1), //new NodeId(20866,1), new NodeId(\"ns=1;i=20415\"), }; OpcUaClient m_OpcUaClient; async void Start() { m_OpcUaClient = new OpcUaClient(); m_OpcUaClient.UserIdentity = new UserIdentity(new AnonymousIdentityToken()); try { await m_OpcUaClient.ConnectServer(serverUrl); } catch (Exception ex) { Debug.LogException(ex); } //Browse(20870, 1); //SingleSubscription(\"AA\", 20857, 1); //MultySubscription(\"AA\", nodeIds); //History(); } private void History() { var nodeId = new NodeId(20857, 1); var startTime = DateTime.Now.AddHours(-1); var endTime = DateTime.Now; var result = m_OpcUaClient.ReadHistoryRawDataValues<double>($\"{nodeId}\", startTime, endTime); Debug.Log($\"count:[{result.Count()}]--------------------------\"); foreach (var item in result) { Debug.Log(item); } Debug.Log($\"--------------------------\"); } private void MultySubscription(string key, NodeId[] ids) { var tags = ids.Select(x => $\"{x}\").ToArray(); m_OpcUaClient.AddSubscription(key, tags, SubCallback); } private void SingleSubscription(string key, uint value, ushort namespaceIndex) { var nodeId = new NodeId(value, namespaceIndex); Debug.Log($\"AddSubscription-->[{key}]:[{nodeId}]\"); m_OpcUaClient.AddSubscription(key, $\"{nodeId}\", SubCallback); } private void SubCallback(string key, MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs args) { Debug.Log($\"SubCallback-->[{key}]\"); if (key == \"AA\") { MonitoredItemNotification notification = args.NotificationValue as MonitoredItemNotification; if (notification != null) { Debug.Log($\"[{monitoredItem.StartNodeId}],[{notification.Value.WrappedValue.Value}]\"); } } } private void Browse(uint value, ushort namespaceIndex) { var nodeId = new NodeId(value, namespaceIndex); Debug.Log($\"Browse:[{nodeId}]\"); var results = m_OpcUaClient.BrowseNodeReference($\"{nodeId}\"); foreach (var item in results) { Debug.Log($\"{item.NodeClass},{item.BrowseName},{item.DisplayName},{item.NodeId}\"); } var list = results .Select(x => new NodeId($\"{x.NodeId}\")) .ToArray(); ReadNodes(list); } private void OnApplicationQuit() { Disconnect(); } private async Task<T> ReadNode<T>(string nodeId) { T result = default; try { result = await m_OpcUaClient.ReadNodeAsync<T>(nodeId); } catch (Exception ex) { Debug.LogException(ex); } finally { Debug.Log($\"[{nodeId}]:[{result}]\"); } return result; } private async void ReadNodes(NodeId[] nodeIds) { var result = await m_OpcUaClient.ReadNodesAsync(nodeIds); for (int i = 0; i < result.Count; i++) { var dataValue = result[i]; Debug.Log($\"{dataValue.Value.GetType()}-->{dataValue.WrappedValue.TypeInfo}-->{dataValue.WrappedValue.Value}-->{dataValue.Value}\"); //if (dataValue.Value.GetType() == typeof(double)) //{ // Debug.Log(\"double\"); //} //else if (dataValue.Value.GetType() == typeof(bool)) //{ // Debug.Log(\"bool\"); //} } } private void Disconnect() { if (m_OpcUaClient.Connected) { m_OpcUaClient.Disconnect(); } } private void OnGUI() { GUILayout.Label($\"Connected:{m_OpcUaClient.Connected}\"); if (GUILayout.Button(\"Disconnect\")) { Disconnect(); } if (GUILayout.Button(\"Browse\")) { Browse(20870, 1); } if (GUILayout.Button(\"ReadNode\")) { ReadNode<double>(nodeId); } if (GUILayout.Button(\"ReadMultyNode\")) { ReadNodes(nodeIds); } if (GUILayout.Button(\"SingleSubscription\")) { SingleSubscription(\"AA\", 20857, 1); } if (GUILayout.Button(\"MultySubscription\")) { MultySubscription(\"AA\", nodeIds); } if (GUILayout.Button(\"RemoveSubscription\")) { if (m_OpcUaClient.Connected) { m_OpcUaClient.RemoveSubscription(\"AA\"); } } if (GUILayout.Button(\"History\")) { History(); } }}