Unity中OPC UA客户端OnNotification仅调试模式触发问题求助
问题分析与解决思路
你的核心问题出在Session生命周期管理和线程调度上,以下是具体问题点和修复方案:
1. Session被提前释放(最关键原因)
你用using块包裹Session创建,Start方法执行完毕后,using会自动调用session.Dispose()销毁连接。只有调试时断点延迟了using块的结束,Session还存活,才能收到通知。
修复:
把Session、Subscription改成类成员变量,移除using块,让连接长期存活:
private Session _opcSession; private Subscription _opcSubscription; void Start() { Debug.Log("Create a session"); // 补全缺失的config初始化 var config = new ApplicationConfiguration { ApplicationName = "UnityOPCClient", ApplicationType = ApplicationType.Client, SecurityConfiguration = new SecurityConfiguration { AutoAcceptUntrustedCertificates = true // 测试环境用,生产环境需配置证书验证 }, TransportConfigurations = new TransportConfigurationCollection(), TransportQuotas = new TransportQuotas { OperationTimeout = 60000 }, ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 } }; config.Validate(ApplicationType.Client).GetAwaiter().GetResult(); // 创建Session,不再用using自动释放 _opcSession = Session.Create(config, new ConfiguredEndpoint(null, new EndpointDescription(endpointurl)), true, "", 60000, null, null).GetAwaiter().GetResult(); // 后续Browse、订阅逻辑改用类成员变量 Debug.Log("Browse the server namespace."); ReferenceDescriptionCollection refs; byte[] cp; _opcSession.Browse(null, null, ObjectIds.ObjectsFolder, 0u, BrowseDirection.Forward, ReferenceTypeIds.HierarchicalReferences, true, (uint)NodeClass.Variable | (uint)NodeClass.Object | (uint)NodeClass.Method, out cp, out refs); Debug.Log("Create a subscription"); _opcSubscription = new Subscription(_opcSession.DefaultSubscription) { PublishingInterval = 1000, PublishingEnabled = true, KeepAliveCount = 5 }; _opcSession.AddSubscription(_opcSubscription); _opcSubscription.Create(); Debug.Log("Add monitored items to the subscription."); var alphaItem = new MonitoredItem(_opcSubscription.DefaultItem) { DisplayName = "alpha", StartNodeId = "ns=2;s=Alpha", SamplingInterval = 1000 }; alphaItem.Notification += OnNotification; var betaItem = new MonitoredItem(_opcSubscription.DefaultItem) { DisplayName = "beta", StartNodeId = "ns=2;s=Beta", SamplingInterval = 1000 }; betaItem.Notification += OnNotification; Global.list.Add(alphaItem); Global.list.Add(betaItem); _opcSubscription.AddItems(Global.list); _opcSubscription.ApplyChanges(); // 初始读值 Debug.Log("Read Values of Monitored items."); foreach (var item in Global.list) { var v = _opcSession.ReadValue(item.StartNodeId); int val = Convert.ToInt16(v.Value); Global.values.Add(val); } }
2. Update中重复订阅事件
你在Update里每次都给MonitoredItem绑定OnNotification,会导致同一个事件被绑定多次,触发时重复执行还会造成内存泄漏。直接删除Update里的这段代码:
Global.list.ForEach(i => i.Notification += OnNotification);
3. 后台线程回调的主线程适配
OPC UA的OnNotification事件在后台线程触发,Unity的Debug.Log虽能跨线程输出,但修改全局变量或操作游戏对象时会有线程安全问题,建议把逻辑切到主线程执行:
实现主线程调度:
private Queue<Action> _mainThreadActions = new Queue<Action>(); void Update() { lock (_mainThreadActions) { while (_mainThreadActions.Count > 0) { _mainThreadActions.Dequeue().Invoke(); } } } private void OnNotification(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e) { try { MonitoredItemNotification notification = e.NotificationValue as MonitoredItemNotification; if (notification == null) return; // 将逻辑封装到Action,加入主线程队列 lock (_mainThreadActions) { _mainThreadActions.Enqueue(() => { Debug.Log("Notification: " + notification.Message.SequenceNumber + " \" " + monitoredItem.ResolvedNodeId + " \" and Value = " + notification.Value); Debug.Log(monitoredItem.DisplayName + " = " + notification.Value); string value = Convert.ToString(notification.Value); var y = Global.list.FindIndex(x => x.DisplayName == monitoredItem.DisplayName); if (y >= 0) { int val = Convert.ToInt16(value); Global.values[y] = val; } }); } } catch (Exception ex) { Debug.Log(string.Format("OnMonitoredItemNotification error: {0}", ex.Message)); } }
4. 资源清理
在脚本销毁时关闭Session和Subscription,避免资源泄漏:
void OnDestroy() { if (_opcSubscription != null) { _opcSubscription.Delete(); _opcSubscription = null; } if (_opcSession != null) { _opcSession.Close(); _opcSession.Dispose(); _opcSession = null; } }
额外注意点
- 之前代码中
config变量未初始化,必须补全ApplicationConfiguration配置,否则Session创建会失败。 - 生产环境不要开启
AutoAcceptUntrustedCertificates,需配置正规的证书验证逻辑,避免安全风险。
内容的提问来源于stack exchange,提问作者man4
相关产品推荐
相关产品推荐

