unity接入OPC海康机器人
·
在做数字孪生时需要接入海康机器人的数据,数据源为OPC类型,下面就进入干货时间。
using Opc.Ua;
using Opc.Ua.Client;
using OpcUaHelper;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
/// <summary>
/// OPC UA 管理器:负责连接、读取、订阅
/// </summary>
public class OpcUaManager : MonoBehaviour
{
public static OpcUaManager Instance { get; private set; }
private OpcUaClient client;
public bool IsConnected { get; private set; } = false;
private void Awake()
{
if (Instance == null) Instance = this;
else Destroy(gameObject);
}
public async Task ConnectAsync(string serverUrl)
{
try
{
client = new OpcUaClient();
client.UserIdentity = new UserIdentity(new AnonymousIdentityToken());
await client.ConnectServer(serverUrl);
IsConnected = true;
Debug.Log("OPC UA 连接成功:" + serverUrl);
}
catch (Exception ex)
{
Debug.LogError("OPC UA 连接失败:" + ex.Message);
IsConnected = false;
}
}
public void Disconnect()
{
if (client != null)
{
client.Disconnect();
IsConnected = false;
Debug.Log("OPC UA 已断开连接");
}
}
#region 节点读取
public DataValue ReadNode(string nodeId)
{
if (!IsConnected) return null;
try { return client.ReadNode(nodeId); }
catch (Exception ex)
{
Debug.LogError($"读取节点失败 {nodeId}: {ex.Message}");
return null;
}
}
public string ReadNodeAsString(string nodeId)
{
var val = ReadNode(nodeId);
if (val?.WrappedValue?.Value != null) return val.WrappedValue.Value.ToString();
return "暂无数据";
}
public Dictionary<string, string> ReadNodesBatch(List<string> nodeIds)
{
var dict = new Dictionary<string, string>();
if (!IsConnected || nodeIds == null || nodeIds.Count == 0) return dict;
try
{
var nodes = nodeIds.Select(id => new NodeId(id)).ToArray();
var values = client.ReadNodes(nodes);
for (int i = 0; i < nodeIds.Count; i++)
{
dict[nodeIds[i]] = values[i]?.WrappedValue?.Value?.ToString() ?? "暂无数据";
}
}
catch (Exception ex)
{
Debug.LogError("批量读取节点失败:" + ex.Message);
}
return dict;
}
#endregion
#region 节点订阅
public void SubscribeNode(string key, string nodeId, Action<float[]> callback)
{
if (!IsConnected) return;
client.AddSubscription(key, nodeId, (k, item, args) =>
{
if (args.NotificationValue is MonitoredItemNotification notification &&
notification.Value.WrappedValue.TypeInfo.ValueRank == 1 &&
notification.Value.WrappedValue.Value is float[] arr)
{
callback?.Invoke(arr);
}
});
}
public void RemoveSubscription(string key)
{
if (!IsConnected) return;
client.RemoveSubscription(key);
}
public void RemoveAllSubscriptions()
{
if (!IsConnected) return;
client.RemoveAllSubscription();
}
#endregion
}
/// <summary>
/// 机器人数据管理
/// </summary>
public class RobotOpcData : MonoBehaviour
{
public string opcUrl = "opc.tcp://192.168.148.3:48201";
public RobotState Robot1 = new RobotState();
public RobotState Robot2 = new RobotState();
public DataDrivenJoints Join_1;
public DataDrivenJoints Join_2;
private bool robot1Connected = false;
private bool robot2Connected = false;
private void Start()
{
ConnectOpc();
}
private async void ConnectOpc()
{
await OpcUaManager.Instance.ConnectAsync(opcUrl);
if (!OpcUaManager.Instance.IsConnected) return;
// 添加关节订阅
AddJointSubscription("Robot1_Joint", "ns=2;s=Robots.转子车间.ZZ231.Curpose.WorldPos.Joint", data => UpdateJointData(Join_1, data, ref robot1Connected));
AddJointSubscription("Robot2_Joint", "ns=2;s=Robots.转子车间.ZZ232.Curpose.WorldPos.Joint", data => UpdateJointData(Join_2, data, ref robot2Connected));
// 启动定时刷新机器人状态
StartCoroutine(UpdateRobotStatusRoutine());
}
private void AddJointSubscription(string key, string nodeId, Action<float[]> callback)
{
OpcUaManager.Instance.SubscribeNode(key, nodeId, callback);
}
private void UpdateJointData(DataDrivenJoints join, float[] data, ref bool isConnectedFlag)
{
if (!isConnectedFlag) return;
var jointDict = new Dictionary<string, float>();
for (int i = 0; i < data.Length; i++)
jointDict[$"轴_{i}"] = data[i];
join?.AddJointsData(jointDict);
}
private System.Collections.IEnumerator UpdateRobotStatusRoutine()
{
while (true)
{
UpdateRobotState(Robot1, "ZZ231", ref robot1Connected);
UpdateRobotState(Robot2, "ZZ232", ref robot2Connected);
yield return new WaitForSeconds(0.2f); // 0.2秒刷新一次
}
}
private void UpdateRobotState(RobotState robot, string robotPrefix, ref bool isConnectedFlag)
{
string connNode = $"ns=2;s=Robots.转子车间.{robotPrefix}.Connected";
string connValue = OpcUaManager.Instance.ReadNodeAsString(connNode);
isConnectedFlag = connValue.Equals("True", StringComparison.OrdinalIgnoreCase);
if (!isConnectedFlag) return;
// 批量读取任务状态
var taskNodes = new List<string>
{
$"ns=2;s=Robots.转子车间.{robotPrefix}.Task.PRG[1].ParentProgName",
$"ns=2;s=Robots.转子车间.{robotPrefix}.Task.PRG[1].ProgName",
$"ns=2;s=Robots.转子车间.{robotPrefix}.Task.PRG[1].State"
};
var taskValues = OpcUaManager.Instance.ReadNodesBatch(taskNodes);
robot.ParentProgName = SafeString(taskValues[taskNodes[0]]);
robot.ProgName = SafeString(taskValues[taskNodes[1]]);
robot.State = SafeString(taskValues[taskNodes[2]]);
// 批量读取报警
robot.Alarms.Clear();
for (int i = 1; i <= 5; i++)
{
var alarm = new RobotAlarm
{
AlarmTime = SafeString(OpcUaManager.Instance.ReadNodeAsString($"ns=2;s=Robots.转子车间.{robotPrefix}.CurrentAlarm.ALM[{i}].AlarmTime")),
AlarmSeverity = SafeString(OpcUaManager.Instance.ReadNodeAsString($"ns=2;s=Robots.转子车间.{robotPrefix}.CurrentAlarm.ALM[{i}].AlarmSeverity")),
AlarmMessage = SafeString(OpcUaManager.Instance.ReadNodeAsString($"ns=2;s=Robots.转子车间.{robotPrefix}.CurrentAlarm.ALM[{i}].AlarmMessage"))
};
robot.Alarms.Add(alarm);
}
// 读取运行状态
robot.Run = SafeString(OpcUaManager.Instance.ReadNodeAsString($"ns=2;s=Robots.转子车间.{robotPrefix}.UO[1-8].UO[3].Status"));
robot.Ready = SafeString(OpcUaManager.Instance.ReadNodeAsString($"ns=2;s=Robots.转子车间.{robotPrefix}.UO[1-8].UO[2].Status"));
robot.Fault = SafeString(OpcUaManager.Instance.ReadNodeAsString($"ns=2;s=Robots.转子车间.{robotPrefix}.UO[1-8].UO[6].Status"));
}
private string SafeString(string s) => string.IsNullOrEmpty(s) ? "暂无数据" : s;
private void OnDestroy()
{
OpcUaManager.Instance.Disconnect();
}
}
/// <summary>
/// 机器人状态
/// </summary>
public class RobotState
{
public string ParentProgName;
public string ProgName;
public string State;
public string Run;
public string Ready;
public string Fault;
public List<RobotAlarm> Alarms = new List<RobotAlarm>();
}
/// <summary>
/// 机器人报警
/// </summary>
public class RobotAlarm
{
public string AlarmTime;
public string AlarmSeverity;
public string AlarmMessage;
}
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)