using DMS.Infrastructure.Interfaces.Services;
using DMS.Infrastructure.Helper;
using Opc.Ua;
using Opc.Ua.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace DMS.Infrastructure.Services
{
public class OpcUaService : IOpcUaService
{
private Session? _session;
private string? _serverUrl;
///
/// 创建 OPC UA 会话
///
/// OPC UA 服务器地址
/// 取消令牌
///
public async Task CreateSession(string opcUaServerUrl, CancellationToken stoppingToken = default)
{
if (string.IsNullOrEmpty(opcUaServerUrl))
{
throw new ArgumentException("OPC UA server URL cannot be null or empty.", nameof(opcUaServerUrl));
}
try
{
_session = await OpcUaHelper.CreateOpcUaSessionAsync(opcUaServerUrl, stoppingToken);
_serverUrl = opcUaServerUrl;
}
catch (Exception ex)
{
throw new InvalidOperationException($"Failed to create OPC UA session: {ex.Message}", ex);
}
}
///
/// 连接到 OPC UA 服务器
///
public async Task ConnectAsync(CancellationToken stoppingToken = default)
{
if (string.IsNullOrEmpty(_serverUrl))
{
throw new InvalidOperationException("Server URL is not set. Please call CreateSession first.");
}
// 如果已经连接,直接返回
if (_session?.Connected == true)
{
return;
}
// 重新创建会话
await CreateSession(_serverUrl, stoppingToken);
}
///
/// 连接到 OPC UA 服务器(同步版本,用于向后兼容)
///
public void Connect()
{
if (string.IsNullOrEmpty(_serverUrl))
{
throw new InvalidOperationException("Server URL is not set. Please call CreateSession first.");
}
// 如果已经连接,直接返回
if (_session?.Connected == true)
{
return;
}
// 检查会话是否存在但未连接
if (_session != null)
{
// 尝试重新激活会话
try
{
_session.Reconnect();
return;
}
catch
{
// 如果重新连接失败,继续到重新创建会话的步骤
}
}
// 如果没有会话或重新连接失败,抛出异常提示需要调用 CreateSession
throw new InvalidOperationException("Session is not created or connection lost. Please call CreateSession first.");
}
///
/// 断开 OPC UA 服务器连接
///
public void Disconnect()
{
if (_session != null)
{
try
{
_session.Close();
}
catch (Exception ex)
{
// 记录日志但不抛出异常,确保清理工作完成
System.Diagnostics.Debug.WriteLine($"Error closing OPC UA session: {ex.Message}");
}
finally
{
_session = null;
}
}
}
///
/// 添加订阅
///
/// 订阅名称
/// 创建的订阅
public Subscription AddSubscription(string subscriptionName)
{
if (_session == null)
{
throw new InvalidOperationException("Session is not created. Please call CreateSession first.");
}
if (!_session.Connected)
{
throw new InvalidOperationException("Session is not connected. Please call Connect first.");
}
if (string.IsNullOrEmpty(subscriptionName))
{
throw new ArgumentException("Subscription name cannot be null or empty.", nameof(subscriptionName));
}
try
{
var subscription = new Subscription(_session.DefaultSubscription)
{
DisplayName = subscriptionName,
PublishingInterval = 1000,
LifetimeCount = 0,
MaxNotificationsPerPublish = 0,
PublishingEnabled = true,
Priority = 0
};
_session.AddSubscription(subscription);
subscription.Create();
return subscription;
}
catch (Exception ex)
{
throw new InvalidOperationException($"Failed to add subscription '{subscriptionName}': {ex.Message}", ex);
}
}
///
/// 浏览节点
///
/// 起始节点ID
/// 节点引用列表
public IList BrowseNodes(NodeId nodeId)
{
if (_session == null)
{
throw new InvalidOperationException("Session is not created. Please call CreateSession first.");
}
if (!_session.Connected)
{
throw new InvalidOperationException("Session is not connected. Please call Connect first.");
}
if (nodeId == null)
{
throw new ArgumentNullException(nameof(nodeId));
}
try
{
// 使用会话的浏览方法
var response = _session.Browse(
null,
null,
nodeId,
0u,
BrowseDirection.Forward,
ReferenceTypeIds.HierarchicalReferences,
true,
(uint)NodeClass.Unspecified,
out var continuationPoint,
out var references);
return references;
}
catch (Exception ex)
{
throw new InvalidOperationException($"Failed to browse nodes: {ex.Message}", ex);
}
}
///
/// 读取节点值
///
/// 节点ID
/// 节点值
public DataValue ReadValue(NodeId nodeId)
{
if (_session == null)
{
throw new InvalidOperationException("Session is not created. Please call CreateSession first.");
}
if (!_session.Connected)
{
throw new InvalidOperationException("Session is not connected. Please call Connect first.");
}
if (nodeId == null)
{
throw new ArgumentNullException(nameof(nodeId));
}
try
{
// 创建读取值集合
var nodesToRead = new ReadValueIdCollection
{
new ReadValueId
{
NodeId = nodeId,
AttributeId = Attributes.Value
}
};
// 执行读取操作
_session.Read(
null,
0,
TimestampsToReturn.Neither,
nodesToRead,
out var results,
out var diagnosticInfos);
return results[0];
}
catch (Exception ex)
{
throw new InvalidOperationException($"Failed to read value from node '{nodeId}': {ex.Message}", ex);
}
}
///
/// 写入节点值
///
/// 节点ID
/// 要写入的值
/// 写入结果
public StatusCode WriteValue(NodeId nodeId, object value)
{
if (_session == null)
{
throw new InvalidOperationException("Session is not created. Please call CreateSession first.");
}
if (!_session.Connected)
{
throw new InvalidOperationException("Session is not connected. Please call Connect first.");
}
if (nodeId == null)
{
throw new ArgumentNullException(nameof(nodeId));
}
try
{
// 创建写入值集合
var nodesToWrite = new WriteValueCollection
{
new WriteValue
{
NodeId = nodeId,
AttributeId = Attributes.Value,
Value = new DataValue(new Variant(value))
}
};
// 执行写入操作
_session.Write(
null,
nodesToWrite,
out var results,
out var diagnosticInfos);
return results[0];
}
catch (Exception ex)
{
throw new InvalidOperationException($"Failed to write value to node '{nodeId}': {ex.Message}", ex);
}
}
///
/// 检查是否已连接
///
///
public bool IsConnected()
{
return _session?.Connected == true;
}
}
}