|
|
#region << 版 本 注 释 >>
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
|
* 版权所有 (c) 2025 WenJY 保留所有权利。
|
|
|
* CLR版本:4.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;
|
|
|
}
|
|
|
}
|
|
|
} |