From 232a752e838b25a1efb7d428d814d8f0b4d445e7 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sun, 30 Aug 2026 09:57:35 +0800 Subject: [PATCH 1/6] refactor(Opc): rename server services to clients Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Extensions/ServiceCollectionExtensions.cs | 20 ++++- .../BootstrapBlazor.OpcDa/IOpcDaClient.cs | 78 +++++++++++++++++++ .../BootstrapBlazor.OpcDa/IOpcDaServer.cs | 70 +---------------- .../{OpcDaServer.cs => OpcDaClient.cs} | 2 +- .../Extensions/ServiceCollectionExtensions.cs | 4 +- .../{IOpcUaServer.cs => IOpcUaClient.cs} | 2 +- .../{OpcUaServer.cs => OpcUaClient.cs} | 2 +- test/UnitTestOpcDa/UnitTest1.cs | 12 +-- test/UnitTestOpcUa/UnitTest1.cs | 22 +++--- 9 files changed, 118 insertions(+), 94 deletions(-) create mode 100644 src/extensions/BootstrapBlazor.OpcDa/IOpcDaClient.cs rename src/extensions/BootstrapBlazor.OpcDa/{OpcDaServer.cs => OpcDaClient.cs} (99%) rename src/extensions/BootstrapBlazor.OpcUa/{IOpcUaServer.cs => IOpcUaClient.cs} (98%) rename src/extensions/BootstrapBlazor.OpcUa/{OpcUaServer.cs => OpcUaClient.cs} (99%) diff --git a/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs b/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs index 6c04f1ee..b6328b9a 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs @@ -3,24 +3,36 @@ // 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 服务扩展类 /// public static class ServiceCollectionExtensions { /// - /// 增加 OpcDaServer 操作服务 + /// 增加 OpcDaClient 操作服务 /// /// /// [SupportedOSPlatform("windows")] - public static IServiceCollection AddOpcDaServer(this IServiceCollection services) + public static IServiceCollection AddOpcDaClient(this IServiceCollection services) { - services.AddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(provider => provider.GetRequiredService()); + services.TryAddSingleton(provider => provider.GetRequiredService()); return services; } + + /// + /// 增加 OpcDaClient 操作服务 + /// + /// + /// + [Obsolete("Use AddOpcDaClient instead.")] + [SupportedOSPlatform("windows")] + public static IServiceCollection AddOpcDaServer(this IServiceCollection services) => services.AddOpcDaClient(); } diff --git a/src/extensions/BootstrapBlazor.OpcDa/IOpcDaClient.cs b/src/extensions/BootstrapBlazor.OpcDa/IOpcDaClient.cs new file mode 100644 index 00000000..7e3d1b06 --- /dev/null +++ b/src/extensions/BootstrapBlazor.OpcDa/IOpcDaClient.cs @@ -0,0 +1,78 @@ +// 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; + +/// +/// OpcDaClient 接口定义 +/// +public interface IOpcDaClient : IDisposable +{ + /// + /// 获得 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/IOpcDaServer.cs b/src/extensions/BootstrapBlazor.OpcDa/IOpcDaServer.cs index 9f63920e..b9c7f433 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/IOpcDaServer.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/IOpcDaServer.cs @@ -5,74 +5,8 @@ namespace BootstrapBlazor.OpcDa; /// -/// OpcDaServer 接口定义 +/// OpcDaServer 兼容接口定义 /// -public interface IOpcDaServer : IDisposable +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/OpcDaServer.cs b/src/extensions/BootstrapBlazor.OpcDa/OpcDaClient.cs similarity index 99% rename from src/extensions/BootstrapBlazor.OpcDa/OpcDaServer.cs rename to src/extensions/BootstrapBlazor.OpcDa/OpcDaClient.cs index febcd965..5549758c 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/OpcDaServer.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/OpcDaClient.cs @@ -12,7 +12,7 @@ namespace BootstrapBlazor.OpcDa; /// OPC Server 操作类 /// [SupportedOSPlatform("windows")] -sealed class OpcDaServer : IOpcDaServer +sealed class OpcDaClient : IOpcDaClient, IOpcDaServer { private Opc.Da.Server? _server = null; 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 From 3c47dd632f04b4714372c9259c973e9563df8051 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sun, 30 Aug 2026 10:07:34 +0800 Subject: [PATCH 2/6] Revert "refactor(Opc): rename server services to clients" This reverts commit 232a752e838b25a1efb7d428d814d8f0b4d445e7. --- .../Extensions/ServiceCollectionExtensions.cs | 20 +---- .../BootstrapBlazor.OpcDa/IOpcDaClient.cs | 78 ------------------- .../BootstrapBlazor.OpcDa/IOpcDaServer.cs | 70 ++++++++++++++++- .../{OpcDaClient.cs => OpcDaServer.cs} | 2 +- test/UnitTestOpcDa/UnitTest1.cs | 12 +-- 5 files changed, 79 insertions(+), 103 deletions(-) delete mode 100644 src/extensions/BootstrapBlazor.OpcDa/IOpcDaClient.cs rename src/extensions/BootstrapBlazor.OpcDa/{OpcDaClient.cs => OpcDaServer.cs} (99%) diff --git a/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs b/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs index b6328b9a..6c04f1ee 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs @@ -3,36 +3,24 @@ // 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; /// -/// OpcDaClient 服务扩展类 +/// OpcDaServer 服务扩展类 /// public static class ServiceCollectionExtensions { /// - /// 增加 OpcDaClient 操作服务 + /// 增加 OpcDaServer 操作服务 /// /// /// [SupportedOSPlatform("windows")] - public static IServiceCollection AddOpcDaClient(this IServiceCollection services) + public static IServiceCollection AddOpcDaServer(this IServiceCollection services) { - services.TryAddSingleton(); - services.TryAddSingleton(provider => provider.GetRequiredService()); - services.TryAddSingleton(provider => provider.GetRequiredService()); + services.AddSingleton(); return services; } - - /// - /// 增加 OpcDaClient 操作服务 - /// - /// - /// - [Obsolete("Use AddOpcDaClient instead.")] - [SupportedOSPlatform("windows")] - public static IServiceCollection AddOpcDaServer(this IServiceCollection services) => services.AddOpcDaClient(); } diff --git a/src/extensions/BootstrapBlazor.OpcDa/IOpcDaClient.cs b/src/extensions/BootstrapBlazor.OpcDa/IOpcDaClient.cs deleted file mode 100644 index 7e3d1b06..00000000 --- a/src/extensions/BootstrapBlazor.OpcDa/IOpcDaClient.cs +++ /dev/null @@ -1,78 +0,0 @@ -// 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; - -/// -/// OpcDaClient 接口定义 -/// -public interface IOpcDaClient : IDisposable -{ - /// - /// 获得 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/IOpcDaServer.cs b/src/extensions/BootstrapBlazor.OpcDa/IOpcDaServer.cs index b9c7f433..9f63920e 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/IOpcDaServer.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/IOpcDaServer.cs @@ -5,8 +5,74 @@ namespace BootstrapBlazor.OpcDa; /// -/// OpcDaServer 兼容接口定义 +/// OpcDaServer 接口定义 /// -public interface IOpcDaServer : IOpcDaClient +public interface IOpcDaServer : IDisposable { + /// + /// 获得 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/OpcDaClient.cs b/src/extensions/BootstrapBlazor.OpcDa/OpcDaServer.cs similarity index 99% rename from src/extensions/BootstrapBlazor.OpcDa/OpcDaClient.cs rename to src/extensions/BootstrapBlazor.OpcDa/OpcDaServer.cs index 5549758c..febcd965 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/OpcDaClient.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/OpcDaServer.cs @@ -12,7 +12,7 @@ namespace BootstrapBlazor.OpcDa; /// OPC Server 操作类 /// [SupportedOSPlatform("windows")] -sealed class OpcDaClient : IOpcDaClient, IOpcDaServer +sealed class OpcDaServer : IOpcDaServer { private Opc.Da.Server? _server = null; diff --git a/test/UnitTestOpcDa/UnitTest1.cs b/test/UnitTestOpcDa/UnitTest1.cs index 4907f416..45b98aa0 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.AddOpcDaClient(); + sc.AddOpcDaServer(); 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.AddOpcDaClient(); + sc.AddOpcDaServer(); 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.AddOpcDaClient(); + sc.AddOpcDaServer(); 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); From d00d9c8640e92f2a89f03886539119cbe7416166 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sun, 30 Aug 2026 10:08:31 +0800 Subject: [PATCH 3/6] chore: bump version 10.0.2 --- .../BootstrapBlazor.OpcDa/BootstrapBlazor.OpcDa.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From dec35c57bbd232a15bebe98da4ac5317c62df416 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sun, 30 Aug 2026 10:08:41 +0800 Subject: [PATCH 4/6] Reapply "refactor(Opc): rename server services to clients" This reverts commit 3c47dd632f04b4714372c9259c973e9563df8051. --- .../Extensions/ServiceCollectionExtensions.cs | 20 ++++- .../BootstrapBlazor.OpcDa/IOpcDaClient.cs | 78 +++++++++++++++++++ .../BootstrapBlazor.OpcDa/IOpcDaServer.cs | 70 +---------------- .../{OpcDaServer.cs => OpcDaClient.cs} | 2 +- test/UnitTestOpcDa/UnitTest1.cs | 12 +-- 5 files changed, 103 insertions(+), 79 deletions(-) create mode 100644 src/extensions/BootstrapBlazor.OpcDa/IOpcDaClient.cs rename src/extensions/BootstrapBlazor.OpcDa/{OpcDaServer.cs => OpcDaClient.cs} (99%) diff --git a/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs b/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs index 6c04f1ee..b6328b9a 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs @@ -3,24 +3,36 @@ // 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 服务扩展类 /// public static class ServiceCollectionExtensions { /// - /// 增加 OpcDaServer 操作服务 + /// 增加 OpcDaClient 操作服务 /// /// /// [SupportedOSPlatform("windows")] - public static IServiceCollection AddOpcDaServer(this IServiceCollection services) + public static IServiceCollection AddOpcDaClient(this IServiceCollection services) { - services.AddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(provider => provider.GetRequiredService()); + services.TryAddSingleton(provider => provider.GetRequiredService()); return services; } + + /// + /// 增加 OpcDaClient 操作服务 + /// + /// + /// + [Obsolete("Use AddOpcDaClient instead.")] + [SupportedOSPlatform("windows")] + public static IServiceCollection AddOpcDaServer(this IServiceCollection services) => services.AddOpcDaClient(); } diff --git a/src/extensions/BootstrapBlazor.OpcDa/IOpcDaClient.cs b/src/extensions/BootstrapBlazor.OpcDa/IOpcDaClient.cs new file mode 100644 index 00000000..7e3d1b06 --- /dev/null +++ b/src/extensions/BootstrapBlazor.OpcDa/IOpcDaClient.cs @@ -0,0 +1,78 @@ +// 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; + +/// +/// OpcDaClient 接口定义 +/// +public interface IOpcDaClient : IDisposable +{ + /// + /// 获得 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/IOpcDaServer.cs b/src/extensions/BootstrapBlazor.OpcDa/IOpcDaServer.cs index 9f63920e..b9c7f433 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/IOpcDaServer.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/IOpcDaServer.cs @@ -5,74 +5,8 @@ namespace BootstrapBlazor.OpcDa; /// -/// OpcDaServer 接口定义 +/// OpcDaServer 兼容接口定义 /// -public interface IOpcDaServer : IDisposable +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/OpcDaServer.cs b/src/extensions/BootstrapBlazor.OpcDa/OpcDaClient.cs similarity index 99% rename from src/extensions/BootstrapBlazor.OpcDa/OpcDaServer.cs rename to src/extensions/BootstrapBlazor.OpcDa/OpcDaClient.cs index febcd965..5549758c 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/OpcDaServer.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/OpcDaClient.cs @@ -12,7 +12,7 @@ namespace BootstrapBlazor.OpcDa; /// OPC Server 操作类 /// [SupportedOSPlatform("windows")] -sealed class OpcDaServer : IOpcDaServer +sealed class OpcDaClient : IOpcDaClient, IOpcDaServer { private Opc.Da.Server? _server = null; 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); From 06e370462ee35d78df39c5228bb0474959e5100b Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sun, 30 Aug 2026 10:14:01 +0800 Subject: [PATCH 5/6] =?UTF-8?q?feat:=20=E6=9B=B4=E6=94=B9=E6=B3=A8?= =?UTF-8?q?=E5=85=A5=E6=9C=8D=E5=8A=A1=E4=B8=BA=20AddOpcDaClient?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extensions/Extensions.cs | 3 +- .../Extensions/ServiceCollectionExtensions.cs | 9 ++- .../BootstrapBlazor.OpcDa/IOpcDaClient.cs | 33 ++++++---- .../BootstrapBlazor.OpcDa/IOpcDaServer.cs | 4 +- .../BootstrapBlazor.OpcDa/IOpcItem.cs | 6 +- .../BootstrapBlazor.OpcDa/IOpcSubscription.cs | 15 +++-- .../BootstrapBlazor.OpcDa/OpcBrowseElement.cs | 18 +++-- .../OpcBrowseFilterType.cs | 12 ++-- .../BootstrapBlazor.OpcDa/OpcBrowseFilters.cs | 18 +++-- .../OpcBrowsePosition.cs | 3 +- .../BootstrapBlazor.OpcDa/OpcDaClient.cs | 66 ++++--------------- .../OpcItemEqualityComparer.cs | 10 ++- .../BootstrapBlazor.OpcDa/OpcReadItem.cs | 6 +- .../BootstrapBlazor.OpcDa/OpcWriteItem.cs | 6 +- .../BootstrapBlazor.OpcDa/Quality.cs | 9 ++- 15 files changed, 110 insertions(+), 108 deletions(-) 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 b6328b9a..92944d95 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs @@ -9,12 +9,14 @@ namespace Microsoft.Extensions.DependencyInjection; /// -/// OpcDaClient 服务扩展类 +/// OpcDaClient 服务扩展类 +/// OpcDaClient service extension class /// public static class ServiceCollectionExtensions { /// - /// 增加 OpcDaClient 操作服务 + /// 增加 OpcDaClient 操作服务 + /// Adds the OpcDa client service /// /// /// @@ -28,7 +30,8 @@ public static IServiceCollection AddOpcDaClient(this IServiceCollection services } /// - /// 增加 OpcDaClient 操作服务 + /// 增加 OpcDaClient 操作服务 + /// Adds the OpcDa client service /// /// /// diff --git a/src/extensions/BootstrapBlazor.OpcDa/IOpcDaClient.cs b/src/extensions/BootstrapBlazor.OpcDa/IOpcDaClient.cs index 7e3d1b06..00bb2505 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/IOpcDaClient.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/IOpcDaClient.cs @@ -5,40 +5,47 @@ namespace BootstrapBlazor.OpcDa; /// -/// OpcDaClient 接口定义 +/// OpcDa 客户端接口 +/// OpcDa client interface /// public interface IOpcDaClient : IDisposable { /// - /// 获得 OPC Server 是否已连接 + /// 获得 OPC Server 是否已连接 + /// Gets whether the client is connected to the OPC Server /// bool IsConnected { get; } /// - /// 获得 OPC Server 名称 + /// 获得 OPC Server 名称 + /// Gets the OPC Server name /// string? ServerName { get; } /// - /// 连接到 OPC Server 方法 + /// 连接到 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 毫秒 @@ -47,21 +54,24 @@ public interface IOpcDaClient : IDisposable IOpcSubscription CreateSubscription(string name, int updateRate = 1000, bool active = true); /// - /// 读取 Item 值方法 + /// 读取 Item 值 + /// Reads item values /// /// /// HashSet Read(params HashSet items); /// - /// 读取 Item 值方法 + /// 写入 Item 值 + /// Writes item values /// /// /// HashSet Write(params HashSet items); /// - /// 浏览 OPC Server 中的位号 (即数据项或者标签) + /// 浏览 OPC Server 中的位号(即数据项或者标签) + /// Browses tags in the OPC Server /// /// /// @@ -70,7 +80,8 @@ public interface IOpcDaClient : IDisposable OpcBrowseElement[] Browse(string name, OpcBrowseFilters filters, out OpcBrowsePosition? position); /// - /// 浏览 OPC Server 中的位号 (即数据项或者标签) + /// 继续浏览 OPC Server 中的位号(即数据项或者标签) + /// Continues browsing tags in the OPC Server /// /// /// diff --git a/src/extensions/BootstrapBlazor.OpcDa/IOpcDaServer.cs b/src/extensions/BootstrapBlazor.OpcDa/IOpcDaServer.cs index b9c7f433..4224f7da 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/IOpcDaServer.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/IOpcDaServer.cs @@ -5,8 +5,10 @@ namespace BootstrapBlazor.OpcDa; /// -/// OpcDaServer 兼容接口定义 +/// OpcDaServer 兼容接口 +/// Compatibility interface for OpcDaServer /// +[Obsolete("Use IOpcDaClient instead.")] public interface IOpcDaServer : IOpcDaClient { } 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 index 5549758c..748a9435 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/OpcDaClient.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/OpcDaClient.cs @@ -9,31 +9,24 @@ namespace BootstrapBlazor.OpcDa; /// -/// OPC Server 操作类 +/// OpcDa 客户端实现 +/// OpcDa client implementation /// [SupportedOSPlatform("windows")] -sealed class OpcDaClient : IOpcDaClient, IOpcDaServer +class OpcDaClient : IOpcDaClient, 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; @@ -46,10 +39,7 @@ public bool Connect(string serverName) return IsConnected; } - /// - /// 断开连接方法 - /// - /// + /// public void Disconnect() { ServerName = string.Empty; @@ -66,13 +56,7 @@ public void Disconnect() } } - /// - /// 创建订阅方法 - /// - /// 订阅名称 - /// 更新频率 默认 1000 毫秒 - /// 是否激活 默认 true - /// + /// public IOpcSubscription CreateSubscription(string name, int updateRate = 1000, bool active = true) { var server = GetOpcServer(); @@ -87,11 +71,7 @@ public IOpcSubscription CreateSubscription(string name, int updateRate = 1000, b return subscription.ToOpcSubscription(); } - /// - /// 取消订阅方法 - /// - /// 订阅接口 实例 - /// + /// public void CancelSubscription(IOpcSubscription subscription) { var server = GetOpcServer(); @@ -102,11 +82,7 @@ public void CancelSubscription(IOpcSubscription subscription) } } - /// - /// 读取指定 Item 值方法 - /// - /// - /// + /// public HashSet Read(params HashSet items) { var server = GetOpcServer(); @@ -114,11 +90,7 @@ public HashSet Read(params HashSet items) 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(); @@ -141,13 +113,7 @@ private Opc.Da.Server GetOpcServer() return _server; } - /// /// - /// - /// - /// - /// - /// public OpcBrowseElement[] Browse(string name, OpcBrowseFilters filters, out OpcBrowsePosition? position) { var server = GetOpcServer(); @@ -156,11 +122,7 @@ public OpcBrowseElement[] Browse(string name, OpcBrowseFilters filters, out OpcB return [.. results.Select(element => new OpcBrowseElement(element))]; } - /// /// - /// - /// - /// public OpcBrowseElement[] BrowseNext(OpcBrowsePosition position) { var server = GetOpcServer(); @@ -169,10 +131,6 @@ public OpcBrowseElement[] BrowseNext(OpcBrowsePosition position) return [.. results.Select(element => new OpcBrowseElement(element))]; } - /// - /// Dispose 方法 - /// - /// private void Dispose(bool disposing) { if (disposing) @@ -181,9 +139,7 @@ private void Dispose(bool disposing) } } - /// /// - /// public void Dispose() { Dispose(true); 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 } From 7bd1f943ce508d1c84531ad025c33af4ceb642bf Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sun, 30 Aug 2026 10:15:38 +0800 Subject: [PATCH 6/6] fix(OpcDa): isolate obsolete server registration Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Extensions/ServiceCollectionExtensions.cs | 9 +++++++-- src/extensions/BootstrapBlazor.OpcDa/OpcDaClient.cs | 2 +- src/extensions/BootstrapBlazor.OpcDa/OpcDaServer.cs | 10 ++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 src/extensions/BootstrapBlazor.OpcDa/OpcDaServer.cs diff --git a/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs b/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs index 92944d95..951f6d6f 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/Extensions/ServiceCollectionExtensions.cs @@ -25,7 +25,6 @@ public static IServiceCollection AddOpcDaClient(this IServiceCollection services { services.TryAddSingleton(); services.TryAddSingleton(provider => provider.GetRequiredService()); - services.TryAddSingleton(provider => provider.GetRequiredService()); return services; } @@ -37,5 +36,11 @@ public static IServiceCollection AddOpcDaClient(this IServiceCollection services /// [Obsolete("Use AddOpcDaClient instead.")] [SupportedOSPlatform("windows")] - public static IServiceCollection AddOpcDaServer(this IServiceCollection services) => services.AddOpcDaClient(); + public static IServiceCollection AddOpcDaServer(this IServiceCollection services) + { + services.TryAddSingleton(); + services.TryAddSingleton(provider => provider.GetRequiredService()); + services.TryAddSingleton(provider => provider.GetRequiredService()); + return services; + } } diff --git a/src/extensions/BootstrapBlazor.OpcDa/OpcDaClient.cs b/src/extensions/BootstrapBlazor.OpcDa/OpcDaClient.cs index 748a9435..ef6f049d 100644 --- a/src/extensions/BootstrapBlazor.OpcDa/OpcDaClient.cs +++ b/src/extensions/BootstrapBlazor.OpcDa/OpcDaClient.cs @@ -13,7 +13,7 @@ namespace BootstrapBlazor.OpcDa; /// OpcDa client implementation /// [SupportedOSPlatform("windows")] -class OpcDaClient : IOpcDaClient, IOpcDaServer +class OpcDaClient : IOpcDaClient { private Opc.Da.Server? _server = null; diff --git a/src/extensions/BootstrapBlazor.OpcDa/OpcDaServer.cs b/src/extensions/BootstrapBlazor.OpcDa/OpcDaServer.cs new file mode 100644 index 00000000..3adab67f --- /dev/null +++ b/src/extensions/BootstrapBlazor.OpcDa/OpcDaServer.cs @@ -0,0 +1,10 @@ +// 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; + +[Obsolete("Use OpcDaClient instead.")] +sealed class OpcDaServer : OpcDaClient, IOpcDaServer +{ +}