diff --git a/src/extensions/BootstrapBlazor.OpcDa/BootstrapBlazor.OpcDa.csproj b/src/extensions/BootstrapBlazor.OpcDa/BootstrapBlazor.OpcDa.csproj index a87ad04d..c930a51f 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/BootstrapBlazor.OpcDa.csproj +++ b/src/extensions/BootstrapBlazor.OpcDa/BootstrapBlazor.OpcDa.csproj @@ -1,7 +1,7 @@  - 10.0.1 + 10.0.2 BootstrapBlazor.OpcDa diff --git a/src/extensions/BootstrapBlazor.OpcDa/Extensions/Extensions.cs b/src/extensions/BootstrapBlazor.OpcDa/Extensions/Extensions.cs index dc3a91da..c7d5e4c9 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/Extensions/Extensions.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/Extensions/Extensions.cs @@ -7,7 +7,8 @@ namespace BootstrapBlazor.OpcDa; /// -/// 扩展方法类 +/// 扩展方法类 +/// Extension methods /// internal static class Extensions { diff --git a/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs b/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs index 6c04f1ee..951f6d6f 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs @@ -3,24 +3,44 @@ // Website: https://www.blazor.zone or https://argozhang.github.io/ using BootstrapBlazor.OpcDa; +using Microsoft.Extensions.DependencyInjection.Extensions; using System.Runtime.Versioning; namespace Microsoft.Extensions.DependencyInjection; /// -/// OpcDaServer 服务扩展类 +/// OpcDaClient 服务扩展类 +/// OpcDaClient service extension class /// public static class ServiceCollectionExtensions { /// - /// 增加 OpcDaServer 操作服务 + /// 增加 OpcDaClient 操作服务 + /// Adds the OpcDa client service /// /// /// [SupportedOSPlatform("windows")] + public static IServiceCollection AddOpcDaClient(this IServiceCollection services) + { + services.TryAddSingleton(); + services.TryAddSingleton(provider => provider.GetRequiredService()); + return services; + } + + /// + /// 增加 OpcDaClient 操作服务 + /// Adds the OpcDa client service + /// + /// + /// + [Obsolete("Use AddOpcDaClient instead.")] + [SupportedOSPlatform("windows")] public static IServiceCollection AddOpcDaServer(this IServiceCollection services) { - services.AddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(provider => provider.GetRequiredService()); + services.TryAddSingleton(provider => provider.GetRequiredService()); return services; } } diff --git a/src/extensions/BootstrapBlazor.OpcDa/IOpcDaClient.cs b/src/extensions/BootstrapBlazor.OpcDa/IOpcDaClient.cs new file mode 100644 index 00000000..00bb2505 --- /dev/null +++ b/src/extensions/BootstrapBlazor.OpcDa/IOpcDaClient.cs @@ -0,0 +1,89 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +namespace BootstrapBlazor.OpcDa; + +/// +/// OpcDa 客户端接口 +/// OpcDa client interface +/// +public interface IOpcDaClient : IDisposable +{ + /// + /// 获得 OPC Server 是否已连接 + /// Gets whether the client is connected to the OPC Server + /// + bool IsConnected { get; } + + /// + /// 获得 OPC Server 名称 + /// Gets the OPC Server name + /// + string? ServerName { get; } + + /// + /// 连接到 OPC Server + /// Connects to an OPC Server + /// + /// + /// + bool Connect(string serverName); + + /// + /// 断开当前连接 + /// Disconnects the current connection + /// + void Disconnect(); + + /// + /// 取消订阅 + /// Cancels a subscription + /// + /// + void CancelSubscription(IOpcSubscription subscription); + + /// + /// 创建订阅 + /// Creates a subscription + /// + /// 订阅名称 + /// 更新频率 默认 1000 毫秒 + /// 是否激活 默认 true + /// + IOpcSubscription CreateSubscription(string name, int updateRate = 1000, bool active = true); + + /// + /// 读取 Item 值 + /// Reads item values + /// + /// + /// + HashSet Read(params HashSet items); + + /// + /// 写入 Item 值 + /// Writes item values + /// + /// + /// + HashSet Write(params HashSet items); + + /// + /// 浏览 OPC Server 中的位号(即数据项或者标签) + /// Browses tags in the OPC Server + /// + /// + /// + /// + /// + OpcBrowseElement[] Browse(string name, OpcBrowseFilters filters, out OpcBrowsePosition? position); + + /// + /// 继续浏览 OPC Server 中的位号(即数据项或者标签) + /// Continues browsing tags in the OPC Server + /// + /// + /// + OpcBrowseElement[] BrowseNext(OpcBrowsePosition position); +} diff --git a/src/extensions/BootstrapBlazor.OpcDa/IOpcDaServer.cs b/src/extensions/BootstrapBlazor.OpcDa/IOpcDaServer.cs index 9f63920e..4224f7da 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/IOpcDaServer.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/IOpcDaServer.cs @@ -5,74 +5,10 @@ namespace BootstrapBlazor.OpcDa; /// -/// OpcDaServer 接口定义 +/// OpcDaServer 兼容接口 +/// Compatibility interface for OpcDaServer /// -public interface IOpcDaServer : IDisposable +[Obsolete("Use IOpcDaClient instead.")] +public interface IOpcDaServer : IOpcDaClient { - /// - /// 获得 OPC Server 是否已连接 - /// - bool IsConnected { get; } - - /// - /// 获得 OPC Server 名称 - /// - string? ServerName { get; } - - /// - /// 连接到 OPC Server 方法 - /// - /// - /// - bool Connect(string serverName); - - /// - /// 断开连接方法 - /// - void Disconnect(); - - /// - /// 取消订阅方法 - /// - /// - void CancelSubscription(IOpcSubscription subscription); - - /// - /// 创建订阅方法 - /// - /// 订阅名称 - /// 更新频率 默认 1000 毫秒 - /// 是否激活 默认 true - /// - IOpcSubscription CreateSubscription(string name, int updateRate = 1000, bool active = true); - - /// - /// 读取 Item 值方法 - /// - /// - /// - HashSet Read(params HashSet items); - - /// - /// 读取 Item 值方法 - /// - /// - /// - HashSet Write(params HashSet items); - - /// - /// 浏览 OPC Server 中的位号 (即数据项或者标签) - /// - /// - /// - /// - /// - OpcBrowseElement[] Browse(string name, OpcBrowseFilters filters, out OpcBrowsePosition? position); - - /// - /// 浏览 OPC Server 中的位号 (即数据项或者标签) - /// - /// - /// - OpcBrowseElement[] BrowseNext(OpcBrowsePosition position); } diff --git a/src/extensions/BootstrapBlazor.OpcDa/IOpcItem.cs b/src/extensions/BootstrapBlazor.OpcDa/IOpcItem.cs index d39a8df7..c5a1ada2 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/IOpcItem.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/IOpcItem.cs @@ -5,12 +5,14 @@ namespace BootstrapBlazor.OpcDa; /// -/// +/// OpcDa 数据项接口 +/// OpcDa item interface /// public interface IOpcItem { /// - /// + /// 获得 数据项名称 + /// Gets the item name /// string Name { get; } } diff --git a/src/extensions/BootstrapBlazor.OpcDa/IOpcSubscription.cs b/src/extensions/BootstrapBlazor.OpcDa/IOpcSubscription.cs index 991bbb2e..02f0f34b 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/IOpcSubscription.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/IOpcSubscription.cs @@ -5,27 +5,32 @@ namespace BootstrapBlazor.OpcDa; /// -/// 订阅接口定义 +/// OpcDa 订阅接口 +/// OpcDa subscription interface /// public interface IOpcSubscription { /// - /// 获得 订阅名称 + /// 获得 订阅名称 + /// Gets the subscription name /// public string Name { get; } /// - /// 获得/设置 是否保留最后一个值 + /// 获得/设置 是否保留最后一个值 + /// Gets or sets whether to retain the previous value /// public bool KeepLastValue { get; set; } /// - /// 获得/设置 数据变更回调 + /// 获得/设置 数据变更回调 + /// Gets or sets the data change callback /// Action>? DataChanged { get; set; } /// - /// 增加数据项 + /// 增加数据项 + /// Adds monitored items /// /// void AddItems(IEnumerable items); diff --git a/src/extensions/BootstrapBlazor.OpcDa/OpcBrowseElement.cs b/src/extensions/BootstrapBlazor.OpcDa/OpcBrowseElement.cs index a0ba41e9..ffa6a43c 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/OpcBrowseElement.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/OpcBrowseElement.cs @@ -7,32 +7,38 @@ namespace BootstrapBlazor.OpcDa; /// -/// 对 OpcDataServer BrowseElement 的封装类 +/// OpcDataServer BrowseElement 封装类 +/// Wrapper for OpcDataServer BrowseElement /// public class OpcBrowseElement { /// - /// 获得/设置 节点名称 + /// 获得/设置 节点名称 + /// Gets or sets the node name /// public string Name { get; set; } /// - /// 获得/设置 Item 名称 + /// 获得/设置 Item 名称 + /// Gets or sets the item name /// public string ItemName { get; set; } /// - /// 获得/设置 是否是数据项 + /// 获得/设置 是否是数据项 + /// Gets or sets whether the node is an item /// public bool IsItem { get; set; } /// - /// 获得/设置 是否有子节点 + /// 获得/设置 是否有子节点 + /// Gets or sets whether the node has children /// public bool HasChildren { get; set; } /// - /// 构造函数 + /// 创建 OpcBrowseElement 实例 + /// Creates an OpcBrowseElement instance /// public OpcBrowseElement() { diff --git a/src/extensions/BootstrapBlazor.OpcDa/OpcBrowseFilterType.cs b/src/extensions/BootstrapBlazor.OpcDa/OpcBrowseFilterType.cs index 537c6246..5aad745c 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/OpcBrowseFilterType.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/OpcBrowseFilterType.cs @@ -5,22 +5,26 @@ namespace BootstrapBlazor.OpcDa; /// -/// OpcDa 浏览过滤器类型枚举 +/// OpcDa 浏览过滤器类型枚举 +/// OpcDa browse filter type /// public enum OpcBrowseFilterType { /// - /// 全部 + /// 全部 + /// All nodes /// All, /// - /// 分支 + /// 分支 + /// Branches /// Branch, /// - /// 数据项 + /// 数据项 + /// Items /// Item } diff --git a/src/extensions/BootstrapBlazor.OpcDa/OpcBrowseFilters.cs b/src/extensions/BootstrapBlazor.OpcDa/OpcBrowseFilters.cs index 0b7f208f..9d43c149 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/OpcBrowseFilters.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/OpcBrowseFilters.cs @@ -5,32 +5,38 @@ namespace BootstrapBlazor.OpcDa; /// -/// 对 OpcDataServer BrowseFilters 的封装类 +/// OpcDataServer BrowseFilters 封装类 +/// Wrapper for OpcDataServer BrowseFilters /// public class OpcBrowseFilters { /// - /// 获得/设置 最大返回节点数量 + /// 获得/设置 最大返回节点数量 + /// Gets or sets the maximum number of returned nodes /// public int MaxElementsReturned { get; set; } /// - /// 获得/设置 元素名称过滤器 + /// 获得/设置 元素名称过滤器 + /// Gets or sets the element name filter /// public string? ElementNameFilter { get; set; } /// - /// 获得/设置 是否返回所有属性 + /// 获得/设置 是否返回所有属性 + /// Gets or sets whether all properties are returned /// public bool ReturnAllProperties { get; set; } /// - /// 获得/设置 是否返回属性值 + /// 获得/设置 是否返回属性值 + /// Gets or sets whether property values are returned /// public bool ReturnPropertyValues { get; set; } /// - /// 获得/设置 浏览过滤器类型 + /// 获得/设置 浏览过滤器类型 + /// Gets or sets the browse filter type /// public OpcBrowseFilterType BrowseFilter { get; set; } } diff --git a/src/extensions/BootstrapBlazor.OpcDa/OpcBrowsePosition.cs b/src/extensions/BootstrapBlazor.OpcDa/OpcBrowsePosition.cs index 371df64f..b5b68a1e 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/OpcBrowsePosition.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/OpcBrowsePosition.cs @@ -5,7 +5,8 @@ namespace BootstrapBlazor.OpcDa; /// -/// 对 OpcDataServer BrowsePosition 的封装类 +/// OpcDataServer BrowsePosition 封装类 +/// Wrapper for OpcDataServer BrowsePosition /// public class OpcBrowsePosition { diff --git a/src/extensions/BootstrapBlazor.OpcDa/OpcDaClient.cs b/src/extensions/BootstrapBlazor.OpcDa/OpcDaClient.cs new file mode 100644 index 00000000..ef6f049d --- /dev/null +++ b/src/extensions/BootstrapBlazor.OpcDa/OpcDaClient.cs @@ -0,0 +1,148 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +using Opc; +using Opc.Da; +using System.Runtime.Versioning; + +namespace BootstrapBlazor.OpcDa; + +/// +/// OpcDa 客户端实现 +/// OpcDa client implementation +/// +[SupportedOSPlatform("windows")] +class OpcDaClient : IOpcDaClient +{ + private Opc.Da.Server? _server = null; + + /// + public string? ServerName { get; private set; } + + /// + public bool IsConnected => _server?.IsConnected ?? false; + + private readonly Dictionary _subscriptions = []; + + /// + /// opcda://localhost/Kepware.KEPServerEX.V6 + public bool Connect(string serverName) + { + ServerName = serverName; + + // 如果已经连接则先断开 + Disconnect(); + + _server = new Opc.Da.Server(new OpcCom.Factory(), new URL(serverName)); + _server.Connect(); + return IsConnected; + } + + /// + public void Disconnect() + { + ServerName = string.Empty; + + if (_server is { IsConnected: true }) + { + foreach (Subscription sub in _server.Subscriptions) + { + _server.CancelSubscription(sub); + } + + _server.Disconnect(); + _server = null; + } + } + + /// + public IOpcSubscription CreateSubscription(string name, int updateRate = 1000, bool active = true) + { + var server = GetOpcServer(); + if (_subscriptions.TryGetValue(name, out var subscription)) + { + // 已经存在该订阅 + server.CancelSubscription(subscription); + } + + subscription = server.CreateSubscription(name, updateRate, active); + _subscriptions.Add(name, subscription); + return subscription.ToOpcSubscription(); + } + + /// + public void CancelSubscription(IOpcSubscription subscription) + { + var server = GetOpcServer(); + var name = subscription.Name; + if (_subscriptions.Remove(name, out var sub)) + { + server.CancelSubscription(sub); + } + } + + /// + public HashSet Read(params HashSet items) + { + var server = GetOpcServer(); + var results = server.Read([.. items.Select(i => new Item() { ItemName = i })]); + return results.Select(i => new OpcReadItem(i.ItemName, i.Quality.ToQuality(), i.Timestamp, i.Value)).ToHashSet(OpcItemEqualityComparer.Default); + } + + /// + public HashSet Write(params HashSet items) + { + var server = GetOpcServer(); + var results = server.Write([.. items.Select(i => new ItemValue() { ItemName = i.Name, Value = i.Value })]); + + return items.Select(i => + { + var item = results.FirstOrDefault(v => v.ItemName == i.Name); + return i with { Result = item != null && item.ResultID == ResultID.S_OK }; + }).ToHashSet(OpcItemEqualityComparer.Default); + } + + private Opc.Da.Server GetOpcServer() + { + if (_server is not { IsConnected: true }) + { + throw new InvalidOperationException("OPC Server is not connected."); + } + + return _server; + } + + /// + public OpcBrowseElement[] Browse(string name, OpcBrowseFilters filters, out OpcBrowsePosition? position) + { + var server = GetOpcServer(); + var results = server.Browse(new ItemIdentifier(name), filters.ToFilters(), out var pos) ?? []; + position = pos == null ? null : new OpcBrowsePosition(pos); + return [.. results.Select(element => new OpcBrowseElement(element))]; + } + + /// + public OpcBrowseElement[] BrowseNext(OpcBrowsePosition position) + { + var server = GetOpcServer(); + var pos = position.Position; + var results = server.BrowseNext(ref pos) ?? []; + return [.. results.Select(element => new OpcBrowseElement(element))]; + } + + private void Dispose(bool disposing) + { + if (disposing) + { + Disconnect(); + } + } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } +} diff --git a/src/extensions/BootstrapBlazor.OpcDa/OpcDaServer.cs b/src/extensions/BootstrapBlazor.OpcDa/OpcDaServer.cs index febcd965..3adab67f 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/OpcDaServer.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/OpcDaServer.cs @@ -2,191 +2,9 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Website: https://www.blazor.zone or https://argozhang.github.io/ -using Opc; -using Opc.Da; -using System.Runtime.Versioning; - namespace BootstrapBlazor.OpcDa; -/// -/// OPC Server 操作类 -/// -[SupportedOSPlatform("windows")] -sealed class OpcDaServer : IOpcDaServer +[Obsolete("Use OpcDaClient instead.")] +sealed class OpcDaServer : OpcDaClient, IOpcDaServer { - private Opc.Da.Server? _server = null; - - /// - /// 获得 OPC Server 名称 - /// - public string? ServerName { get; private set; } - - /// - /// 获得 OPC Server 状态 - /// - public bool IsConnected => _server?.IsConnected ?? false; - - private readonly Dictionary _subscriptions = []; - - /// - /// 连接到 OPCServer 方法 - /// - /// 服务器名称 - /// opcda://localhost/Kepware.KEPServerEX.V6 - /// 成功时返回真 - public bool Connect(string serverName) - { - ServerName = serverName; - - // 如果已经连接则先断开 - Disconnect(); - - _server = new Opc.Da.Server(new OpcCom.Factory(), new URL(serverName)); - _server.Connect(); - return IsConnected; - } - - /// - /// 断开连接方法 - /// - /// - public void Disconnect() - { - ServerName = string.Empty; - - if (_server is { IsConnected: true }) - { - foreach (Subscription sub in _server.Subscriptions) - { - _server.CancelSubscription(sub); - } - - _server.Disconnect(); - _server = null; - } - } - - /// - /// 创建订阅方法 - /// - /// 订阅名称 - /// 更新频率 默认 1000 毫秒 - /// 是否激活 默认 true - /// - public IOpcSubscription CreateSubscription(string name, int updateRate = 1000, bool active = true) - { - var server = GetOpcServer(); - if (_subscriptions.TryGetValue(name, out var subscription)) - { - // 已经存在该订阅 - server.CancelSubscription(subscription); - } - - subscription = server.CreateSubscription(name, updateRate, active); - _subscriptions.Add(name, subscription); - return subscription.ToOpcSubscription(); - } - - /// - /// 取消订阅方法 - /// - /// 订阅接口 实例 - /// - public void CancelSubscription(IOpcSubscription subscription) - { - var server = GetOpcServer(); - var name = subscription.Name; - if (_subscriptions.Remove(name, out var sub)) - { - server.CancelSubscription(sub); - } - } - - /// - /// 读取指定 Item 值方法 - /// - /// - /// - public HashSet Read(params HashSet items) - { - var server = GetOpcServer(); - var results = server.Read([.. items.Select(i => new Item() { ItemName = i })]); - return results.Select(i => new OpcReadItem(i.ItemName, i.Quality.ToQuality(), i.Timestamp, i.Value)).ToHashSet(OpcItemEqualityComparer.Default); - } - - /// - /// 读取指定 Item 值方法 - /// - /// - /// - public HashSet Write(params HashSet items) - { - var server = GetOpcServer(); - var results = server.Write([.. items.Select(i => new ItemValue() { ItemName = i.Name, Value = i.Value })]); - - return items.Select(i => - { - var item = results.FirstOrDefault(v => v.ItemName == i.Name); - return i with { Result = item != null && item.ResultID == ResultID.S_OK }; - }).ToHashSet(OpcItemEqualityComparer.Default); - } - - private Opc.Da.Server GetOpcServer() - { - if (_server is not { IsConnected: true }) - { - throw new InvalidOperationException("OPC Server is not connected."); - } - - return _server; - } - - /// - /// - /// - /// - /// - /// - /// - public OpcBrowseElement[] Browse(string name, OpcBrowseFilters filters, out OpcBrowsePosition? position) - { - var server = GetOpcServer(); - var results = server.Browse(new ItemIdentifier(name), filters.ToFilters(), out var pos) ?? []; - position = pos == null ? null : new OpcBrowsePosition(pos); - return [.. results.Select(element => new OpcBrowseElement(element))]; - } - - /// - /// - /// - /// - /// - public OpcBrowseElement[] BrowseNext(OpcBrowsePosition position) - { - var server = GetOpcServer(); - var pos = position.Position; - var results = server.BrowseNext(ref pos) ?? []; - return [.. results.Select(element => new OpcBrowseElement(element))]; - } - - /// - /// Dispose 方法 - /// - /// - private void Dispose(bool disposing) - { - if (disposing) - { - Disconnect(); - } - } - - /// - /// - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } } diff --git a/src/extensions/BootstrapBlazor.OpcDa/OpcItemEqualityComparer.cs b/src/extensions/BootstrapBlazor.OpcDa/OpcItemEqualityComparer.cs index a2ff6fe0..2f105177 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/OpcItemEqualityComparer.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/OpcItemEqualityComparer.cs @@ -5,26 +5,24 @@ namespace BootstrapBlazor.OpcDa; /// -/// 比较器 +/// 比较器 +/// Equality comparer for /// public class OpcItemEqualityComparer : IEqualityComparer where TItem : IOpcItem { /// - /// 获得 实例 + /// 获得 实例 + /// Gets the instance /// public static OpcItemEqualityComparer Default { get; } = new(); - /// /// - /// /// /// /// public bool Equals(TItem? x, TItem? y) => x?.Name == y?.Name; - /// /// - /// /// /// public int GetHashCode([DisallowNull] TItem item) => item.Name.GetHashCode(); diff --git a/src/extensions/BootstrapBlazor.OpcDa/OpcReadItem.cs b/src/extensions/BootstrapBlazor.OpcDa/OpcReadItem.cs index a62604a4..d70e6c2a 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/OpcReadItem.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/OpcReadItem.cs @@ -5,12 +5,14 @@ namespace BootstrapBlazor.OpcDa; /// -/// OPC Item 读取实体类 +/// OPC Item 读取实体类 +/// OPC item read model /// public record struct OpcReadItem(string Name, Quality Quality, DateTime Timestamp, object? Value) : IOpcItem { /// - /// 获得 Opc Item 上次值 + /// 获得 Opc Item 上次值 + /// Gets the previous item value /// public object? LastValue { get; set; } } diff --git a/src/extensions/BootstrapBlazor.OpcDa/OpcWriteItem.cs b/src/extensions/BootstrapBlazor.OpcDa/OpcWriteItem.cs index 0f170879..7a5421e1 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/OpcWriteItem.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/OpcWriteItem.cs @@ -5,12 +5,14 @@ namespace BootstrapBlazor.OpcDa; /// -/// OPC Item 写入实体类 +/// OPC Item 写入实体类 +/// OPC item write model /// public record struct OpcWriteItem(string Name, object? Value) : IOpcItem { /// - /// 获得/设置 写入结果 + /// 获得/设置 写入结果 + /// Gets or sets the write result /// public bool Result { get; set; } } diff --git a/src/extensions/BootstrapBlazor.OpcDa/Quality.cs b/src/extensions/BootstrapBlazor.OpcDa/Quality.cs index 90e7fe45..5aec0ec7 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/Quality.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/Quality.cs @@ -5,17 +5,20 @@ namespace BootstrapBlazor.OpcDa; /// -/// Opc Item Quality 枚举 +/// Opc Item Quality 枚举 +/// Opc item quality /// public enum Quality { /// - /// 不可信 + /// 不可信 + /// Bad quality /// Bad, /// - /// 可信 + /// 可信 + /// Good quality /// Good } diff --git a/src/extensions/BootstrapBlazor.OpcUa/Extensions/ServiceCollectionExtensions.cs b/src/extensions/BootstrapBlazor.OpcUa/Extensions/ServiceCollectionExtensions.cs index 39286d3f..545b5b65 100644 --- a/src/extensions/BootstrapBlazor.OpcUa/Extensions/ServiceCollectionExtensions.cs +++ b/src/extensions/BootstrapBlazor.OpcUa/Extensions/ServiceCollectionExtensions.cs @@ -18,9 +18,9 @@ public static class ServiceCollectionExtensions /// /// /// - public static IServiceCollection AddOpcUaServer(this IServiceCollection services) + public static IServiceCollection AddOpcUaClient(this IServiceCollection services) { - services.AddScoped(); + services.AddScoped(); return services; } } diff --git a/src/extensions/BootstrapBlazor.OpcUa/IOpcUaServer.cs b/src/extensions/BootstrapBlazor.OpcUa/IOpcUaClient.cs similarity index 98% rename from src/extensions/BootstrapBlazor.OpcUa/IOpcUaServer.cs rename to src/extensions/BootstrapBlazor.OpcUa/IOpcUaClient.cs index 471451e2..846e27a6 100644 --- a/src/extensions/BootstrapBlazor.OpcUa/IOpcUaServer.cs +++ b/src/extensions/BootstrapBlazor.OpcUa/IOpcUaClient.cs @@ -8,7 +8,7 @@ namespace BootstrapBlazor.OpcUa; /// OPC UA 客户端接口 /// OPC UA client interface /// -public interface IOpcUaServer : IAsyncDisposable +public interface IOpcUaClient : IAsyncDisposable { /// /// 获得当前是否已连接 diff --git a/src/extensions/BootstrapBlazor.OpcUa/OpcUaServer.cs b/src/extensions/BootstrapBlazor.OpcUa/OpcUaClient.cs similarity index 99% rename from src/extensions/BootstrapBlazor.OpcUa/OpcUaServer.cs rename to src/extensions/BootstrapBlazor.OpcUa/OpcUaClient.cs index c3fd9b8c..c61f18e2 100644 --- a/src/extensions/BootstrapBlazor.OpcUa/OpcUaServer.cs +++ b/src/extensions/BootstrapBlazor.OpcUa/OpcUaClient.cs @@ -7,7 +7,7 @@ namespace BootstrapBlazor.OpcUa; -sealed class OpcUaServer : IOpcUaServer +sealed class OpcUaClient : IOpcUaClient { private readonly SemaphoreSlim _gate = new(1, 1); private readonly Dictionary _subscriptions = []; diff --git a/test/UnitTestOpcDa/UnitTest1.cs b/test/UnitTestOpcDa/UnitTest1.cs index 45b98aa0..4907f416 100644 --- a/test/UnitTestOpcDa/UnitTest1.cs +++ b/test/UnitTestOpcDa/UnitTest1.cs @@ -15,10 +15,10 @@ public class UnitTest1 public void Write_Ok() { var sc = new ServiceCollection(); - sc.AddOpcDaServer(); + sc.AddOpcDaClient(); var sp = sc.BuildServiceProvider(); - var server = sp.GetRequiredService(); + var server = sp.GetRequiredService(); var ret = server.Connect("opcda://localhost/Kepware.KEPServerEX.V6"); Assert.True(ret); Assert.True(server.IsConnected); @@ -46,10 +46,10 @@ public void Write_Ok() public async Task Subscription_Ok() { var sc = new ServiceCollection(); - sc.AddOpcDaServer(); + sc.AddOpcDaClient(); var sp = sc.BuildServiceProvider(); - var server = sp.GetRequiredService(); + var server = sp.GetRequiredService(); server.Connect("opcda://localhost/Kepware.KEPServerEX.V6"); var subscription = server.CreateSubscription("Test", 100); @@ -85,10 +85,10 @@ public async Task Subscription_Ok() public void Browser_Ok() { var sc = new ServiceCollection(); - sc.AddOpcDaServer(); + sc.AddOpcDaClient(); var sp = sc.BuildServiceProvider(); - var server = sp.GetRequiredService(); + var server = sp.GetRequiredService(); server.Connect("opcda://localhost/Kepware.KEPServerEX.V6"); var elements = server.Browse("Channel1", new OpcBrowseFilters(), out var position); diff --git a/test/UnitTestOpcUa/UnitTest1.cs b/test/UnitTestOpcUa/UnitTest1.cs index 8e67a3a6..89cf3732 100644 --- a/test/UnitTestOpcUa/UnitTest1.cs +++ b/test/UnitTestOpcUa/UnitTest1.cs @@ -10,18 +10,18 @@ namespace UnitTestOpcUa; public class UnitTest1 { [Fact] - public async Task AddOpcUaServer_Ok() + public async Task AddOpcUaClient_Ok() { var services = new ServiceCollection(); - services.AddOpcUaServer(); + services.AddOpcUaClient(); await using var provider = services.BuildServiceProvider(); await using var scope1 = provider.CreateAsyncScope(); await using var scope2 = provider.CreateAsyncScope(); - var server = scope1.ServiceProvider.GetRequiredService(); + var server = scope1.ServiceProvider.GetRequiredService(); - Assert.Same(server, scope1.ServiceProvider.GetRequiredService()); - Assert.NotSame(server, scope2.ServiceProvider.GetRequiredService()); + Assert.Same(server, scope1.ServiceProvider.GetRequiredService()); + Assert.NotSame(server, scope2.ServiceProvider.GetRequiredService()); Assert.False(server.IsConnected); Assert.Null(server.EndpointUrl); } @@ -30,10 +30,10 @@ public async Task AddOpcUaServer_Ok() public async Task Operation_NotConnected() { var services = new ServiceCollection(); - services.AddOpcUaServer(); + services.AddOpcUaClient(); await using var provider = services.BuildServiceProvider(); - var server = provider.GetRequiredService(); + var server = provider.GetRequiredService(); await Assert.ThrowsAsync(() => server.ReadAsync(["ns=2;s=Tag"])); await Assert.ThrowsAsync(() => server.WriteAsync([new OpcUaWriteItem("ns=2;s=Tag", 1)])); @@ -63,10 +63,10 @@ public void Model_Ok() public async Task Dispose_Ok() { var services = new ServiceCollection(); - services.AddOpcUaServer(); + services.AddOpcUaClient(); await using var provider = services.BuildServiceProvider(); - var server = provider.GetRequiredService(); + var server = provider.GetRequiredService(); await server.DisposeAsync(); await Assert.ThrowsAsync(() => server.DisconnectAsync()); @@ -76,10 +76,10 @@ public async Task Dispose_Ok() public async Task Connect_Options() { var services = new ServiceCollection(); - services.AddOpcUaServer(); + services.AddOpcUaClient(); await using var provider = services.BuildServiceProvider(); - var server = provider.GetRequiredService(); + var server = provider.GetRequiredService(); await Assert.ThrowsAsync(() => server.ConnectAsync("", new OpcUaConnectionOptions())); await Assert.ThrowsAsync(() => server.ConnectAsync("opc.tcp://localhost:4840", new OpcUaConnectionOptions