diff --git a/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj b/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj index 645a8882de7..db787fd10c8 100644 --- a/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj +++ b/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj @@ -60,18 +60,19 @@ + - + - + diff --git a/src/BootstrapBlazor.Server/Components/Samples/OpcDa.razor b/src/BootstrapBlazor.Server/Components/Samples/OpcDa.razor new file mode 100644 index 00000000000..c5309c2ea0d --- /dev/null +++ b/src/BootstrapBlazor.Server/Components/Samples/OpcDa.razor @@ -0,0 +1,77 @@ +@page "/opc-da" +@inject IStringLocalizer Localizer + +

@Localizer["OpcDaTitle"]

+ +

@Localizer["OpcDaDescription"]

+ + + + +

1. 点击 连接 按钮与 OpcDa 服务器建立通讯连接

+

+ + + + + + +

+ +

2. 点击 读取 按钮读取 OpcDa 服务器上的位号值

+
+
+ + + + + +
+
+ + + + + +
+
+ +
+
+ +

3. 订阅功能

+

通过订阅可以监控一组 位号 数据改变情况,当数据改变时通过 DataChanged 回调方法通知订阅者

+
+
+ + + + +
+
+ + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + +
+
+
diff --git a/src/BootstrapBlazor.Server/Components/Samples/OpcDa.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/OpcDa.razor.cs new file mode 100644 index 00000000000..6e6f02df506 --- /dev/null +++ b/src/BootstrapBlazor.Server/Components/Samples/OpcDa.razor.cs @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License +// See the LICENSE file in the project root for more information. +// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone + +using BootstrapBlazor.OpcDa; +using System.Globalization; + +namespace BootstrapBlazor.Server.Components.Samples; + +/// +/// OpcDa 示例 +/// +public partial class OpcDa : ComponentBase +{ + [Inject] + [NotNull] + private IOpcDaServer? OpcDaServer { get; set; } + + private string? _serverName = "opcda://localhost/Kepware.KEPServerEX.V6/Mock"; + + private const string Tag1 = "Channel1.Device1.Tag1"; + private const string Tag2 = "Channel1.Device1.Tag2"; + private const string Tag3 = "Channel1.Device1.Tag3"; + private const string Tag4 = "Channel1.Device1.Tag4"; + + private string? _tagValue1; + private string? _tagValue2; + private string? _tagValue3; + private string? _tagValue4; + + private IOpcSubscription? _subscription; + + private bool _subscribed; + + private void OnConnect() + { + if (!string.IsNullOrEmpty(_serverName)) + { + OpcDaServer.Connect(_serverName); + } + } + + private void OnDisConnect() + { + OnCancelSubscription(); + OpcDaServer.Disconnect(); + } + + private void OnRead() + { + var values = OpcDaServer.Read(Tag1, Tag2); + _tagValue1 = values.ElementAt(0).Value?.ToString(); + + var v = (int)values.ElementAt(1).Value! / 100d; + _tagValue2 = v.ToString(CultureInfo.InvariantCulture); + } + + private void OnCreateSubscription() + { + _subscribed = true; + _subscription = OpcDaServer.CreateSubscription("Subscription1", 1000, true); + _subscription.DataChanged += UpdateValues; + _subscription.AddItems([Tag3, Tag4]); + } + + private void OnCancelSubscription() + { + _subscribed = false; + if (_subscription != null) + { + _subscription.DataChanged -= UpdateValues; + OpcDaServer.CancelSubscription(_subscription); + } + } + + private void UpdateValues(List items) + { + _tagValue3 = items[0].Value?.ToString(); + var v = (int)items[1].Value! / 100d; + _tagValue4 = v.ToString(CultureInfo.InvariantCulture); + + InvokeAsync(StateHasChanged); + } +} + diff --git a/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs b/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs index eeee6ed773f..4c5e2a72358 100644 --- a/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs +++ b/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs @@ -1612,6 +1612,12 @@ void AddServices(DemoMenuItem item) Url = "mask" }, new() + { + IsNew = true, + Text = Localizer["OpcDaService"], + Url = "opc-da" + }, + new() { Text = Localizer["PrintService"], Url = "print-service" diff --git a/src/BootstrapBlazor.Server/Extensions/ServiceCollectionExtensions.cs b/src/BootstrapBlazor.Server/Extensions/ServiceCollectionExtensions.cs index 9645ead253d..a39e37ffae9 100644 --- a/src/BootstrapBlazor.Server/Extensions/ServiceCollectionExtensions.cs +++ b/src/BootstrapBlazor.Server/Extensions/ServiceCollectionExtensions.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. // Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone +using BootstrapBlazor.OpcDa; using Longbow.Tasks.Services; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Options; @@ -45,6 +46,16 @@ public static IServiceCollection AddBootstrapBlazorServerService(this IServiceCo services.AddHostedService(); services.AddHostedService(); + if (OperatingSystem.IsWindows()) + { + services.AddOpcDaServer(); + } + else + { + // 增加 OpcDa 模拟服务(给 Linux 平台使用) + services.AddMockOpcDaServer(); + } + // 增加通用服务 services.AddBootstrapBlazorServices(); diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index 8be381be25a..d21150eac41 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -4846,7 +4846,8 @@ "SocketAutoConnect": "Reconnect", "SocketDataEntity": "DataEntity", "NetworkMonitor": "Network Monitor", - "Toolbar": "Toolbar" + "Toolbar": "Toolbar", + "OpcDaService": "OpcDaServer" }, "BootstrapBlazor.Server.Components.Samples.Table.TablesHeader": { "TablesHeaderTitle": "Header grouping function", @@ -7158,5 +7159,11 @@ "ToolbarSubTitle": "ToolBar component is a container for buttons or other application-specific tools", "NormalTitle": "Normal", "NormalIntro": "" + }, + "BootstrapBlazor.Server.Components.Samples.OpcDa": { + "OpcDaTitle": "OpcDa Server", + "OpcDaDescription": "Connect to OpcDa Server to obtain PLC real-time data", + "OpcDaNormalTitle": "Basic usage", + "OpcDaNormalIntro": "Get an instance by injecting the service IOpcDaServer and call the Read method to get the PLC Tag value." } } diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index ea2223ed4e9..3cea5bb4290 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -4846,7 +4846,8 @@ "SocketAutoConnect": "自动重连", "SocketDataEntity": "通讯数据转实体类", "NetworkMonitor": "网络状态 NetworkMonitor", - "Toolbar": "工具栏 Toolbar" + "Toolbar": "工具栏 Toolbar", + "OpcDaService": "OpcDaServer 服务" }, "BootstrapBlazor.Server.Components.Samples.Table.TablesHeader": { "TablesHeaderTitle": "表头分组功能", @@ -7158,5 +7159,11 @@ "ToolbarSubTitle": "是按钮或其他应用程序特定工具的容器", "NormalTitle": "基本用法", "NormalIntro": "" + }, + "BootstrapBlazor.Server.Components.Samples.OpcDa": { + "OpcDaTitle": "OpcDa Server 服务", + "OpcDaDescription": "连接 OpcDa Server 获得 PLC 实时数据", + "OpcDaNormalTitle": "基本用法", + "OpcDaNormalIntro": "通过注入服务 IOpcDaServer 获得实例,调用 Read 方法获得 PLC 位号值" } } diff --git a/src/BootstrapBlazor.Server/docs.json b/src/BootstrapBlazor.Server/docs.json index f95c8aa73ef..6b65fd874dc 100644 --- a/src/BootstrapBlazor.Server/docs.json +++ b/src/BootstrapBlazor.Server/docs.json @@ -249,7 +249,8 @@ "socket/auto-connect": "Sockets\\AutoReconnects", "socket/data-entity": "Sockets\\DataEntities", "network-monitor": "NetworkMonitors", - "toolbar": "Toolbars" + "toolbar": "Toolbars", + "opc-da": "OpcDa" }, "video": { "table": "BV1ap4y1x7Qn?p=1",