You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

225 lines
6.5 KiB
C#

#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Imm.Daemon.Opc.Impl
* 3143E6AC-55C1-46DA-BDF8-8A19682A8A26
*
* WenJY
*
* 2025-09-22 17:25:33
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using Sln.Imm.Daemon.Model.dto;
using TitaniumAS.Opc.Client;
using TitaniumAS.Opc.Client.Common;
using TitaniumAS.Opc.Client.Da;
using TitaniumAS.Opc.Client.Da.Browsing;
namespace Sln.Imm.Daemon.Opc.Impl;
public class OpcDaService : IOpcService, IDisposable
{
private OpcDaServer _server;
private bool _disposed = false;
public OpcDaService()
{
// 初始化 TitaniumAS.Opc.Client 库
Bootstrap.Initialize();
}
public async Task<bool> ConnectAsync(string serverUrl)
{
try
{
// 构建服务器URL
Uri url = UrlBuilder.Build(serverUrl);
// 创建服务器实例并连接
_server = new OpcDaServer(url);
_server.Connect();
return _server.IsConnected;
}
catch (Exception ex)
{
Console.WriteLine($"连接到 OPC DA 服务器失败: {ex.Message}");
return false;
}
}
public async Task DisconnectAsync()
{
if (_server != null && _server.IsConnected)
{
_server.Disconnect();
_server.Dispose();
_server = null;
}
}
public async Task<List<OpcNode>> ReadNodeAsync(List<string> nodeId)
{
if (_server == null || !_server.IsConnected)
throw new Exception("未连接到 OPC DA 服务器");
try
{
// 创建临时组和项进行读取
using (var group = _server.AddGroup("TempReadGroup"))
{
group.IsActive = true;
OpcDaItemDefinition[] definitions = new OpcDaItemDefinition[nodeId.Count];
for (int i = 0; i < nodeId.Count; i++)
{
var definition = new OpcDaItemDefinition
{
ItemId = nodeId[i],
IsActive = true
};
definitions[i] = definition;
}
// 添加要读取的项
var results = group.AddItems(definitions);
if (results[0].Error.Failed)
throw new Exception($"添加项失败: {results[0].Error}");
// 读取项值
var values = await group.ReadAsync(group.Items);
if (values[0].Error.Failed)
throw new Exception($"读取失败: {values[0].Error}");
var indexedNodeIds = nodeId.Select((value, index) => new { Index = index, Value = value });
List<OpcNode> result = new List<OpcNode>();
foreach (var item in indexedNodeIds)
{
var dataValue = values[item.Index];
result.Add(new OpcNode()
{
NodeId = item.Value,
DisplayName = item.Value,
Value = dataValue.Value,
SourceTimestamp = dataValue.Timestamp,
DataType = values[0].Value?.GetType().Name ?? "Unknown"
});
}
return result;
}
}
catch (Exception ex)
{
throw new InvalidOperationException($"OPC DA 读取节点时出错:{ex.Message}");
}
}
public async Task WriteNodeAsync(string nodeId, object value)
{
if (_server == null || !_server.IsConnected)
throw new Exception("未连接到 OPC DA 服务器");
try
{
// 创建临时组和项进行写入
using (var group = _server.AddGroup("TempWriteGroup"))
{
group.IsActive = true;
// 添加要写入的项
var definition = new OpcDaItemDefinition
{
ItemId = nodeId,
IsActive = true
};
var results = group.AddItems(new[] { definition });
if (results[0].Error.Failed)
throw new Exception($"添加项失败: {results[0].Error}");
// 写入值
var item = results[0].Item;
var writeResult = await group.WriteAsync(new[] { item }, new[] { value });
if (writeResult[0].Failed)
throw new Exception($"写入失败: {writeResult[0]}");
}
}
catch (Exception ex)
{
Console.WriteLine($"写入节点时出错: {ex.Message}");
throw;
}
}
public async Task<List<OpcNode>> BrowseNodesAsync(string startingNodeId = null)
{
if (_server == null || !_server.IsConnected)
throw new Exception("未连接到 OPC DA 服务器");
var nodes = new List<OpcNode>();
try
{
// 使用浏览功能浏览服务器地址空间
var browser = new OpcDaBrowserAuto(_server);
var elements = browser.GetElements(startingNodeId);
foreach (var element in elements)
{
nodes.Add(new OpcNode
{
NodeId = element.ItemId,
DisplayName = element.Name,
DataType = "Unknown" // OPC DA 浏览不直接提供数据类型
});
// 如果元素有子元素,递归浏览
if (element.HasChildren)
{
var childNodes = await BrowseNodesAsync(element.ItemId);
nodes.AddRange(childNodes);
}
}
return nodes;
}
catch (Exception ex)
{
Console.WriteLine($"浏览节点时出错: {ex.Message}");
throw;
}
}
public void Dispose()
{
if (!_disposed)
{
DisconnectAsync().Wait();
_disposed = true;
}
}
}