#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 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> ReadNodeAsync(List 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 result = new List(); 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> BrowseNodesAsync(string startingNodeId = null) { if (_server == null || !_server.IsConnected) throw new Exception("未连接到 OPC DA 服务器"); var nodes = new List(); 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; } } }