Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,19 @@
<PackageReference Include="BootstrapBlazor.OctIcon" Version="9.0.4" />
<PackageReference Include="BootstrapBlazor.OfficeViewer" Version="9.0.0" />
<PackageReference Include="BootstrapBlazor.OnScreenKeyboard" Version="9.0.1" />
<PackageReference Include="BootstrapBlazor.OpcDa" Version="9.0.0" />
<PackageReference Include="BootstrapBlazor.PdfReader" Version="9.0.1" />
<PackageReference Include="BootstrapBlazor.PdfViewer" Version="9.0.6" />
<PackageReference Include="BootstrapBlazor.Player" Version="9.0.1" />
<PackageReference Include="BootstrapBlazor.RDKit" Version="9.0.2" />
<PackageReference Include="BootstrapBlazor.SignaturePad" Version="9.0.1" />
<PackageReference Include="BootstrapBlazor.SmilesDrawer" Version="9.0.2" />
<PackageReference Include="BootstrapBlazor.Sortable" Version="9.0.2" />
<PackageReference Include="BootstrapBlazor.Sortable" Version="9.0.3" />
<PackageReference Include="BootstrapBlazor.Splitting" Version="9.0.3" />
<PackageReference Include="BootstrapBlazor.SvgEditor" Version="9.0.3" />
<PackageReference Include="BootstrapBlazor.SummerNote" Version="9.0.4" />
<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.6" />
<PackageReference Include="BootstrapBlazor.TcpSocket" Version="9.0.0-beta01" />
<PackageReference Include="BootstrapBlazor.TcpSocket" Version="9.0.0" />
<PackageReference Include="BootstrapBlazor.Topology" Version="9.0.1" />
<PackageReference Include="BootstrapBlazor.UniverIcon" Version="9.0.1" />
<PackageReference Include="BootstrapBlazor.UniverSheet" Version="9.0.5" />
Expand Down
77 changes: 77 additions & 0 deletions src/BootstrapBlazor.Server/Components/Samples/OpcDa.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
@page "/opc-da"
@inject IStringLocalizer<OpcDa> Localizer

<h3>@Localizer["OpcDaTitle"]</h3>

<h4>@Localizer["OpcDaDescription"]</h4>

<PackageTips Name="BootstrapBlazor.OpcDa"/>

<DemoBlock Title="@Localizer["OpcDaNormalTitle"]" Introduction="@Localizer["OpcDaNormalIntro"]" Name="Normal">
<p class="code-label">1. 点击 <b>连接</b> 按钮与 <code>OpcDa</code> 服务器建立通讯连接</p>
<p>
<BootstrapInputGroup>
<BootstrapInputGroupLabel DisplayText="OpcDa Server"></BootstrapInputGroupLabel>
<Display @bind-Value="@_serverName"></Display>
<Button Text="连接" OnClick="OnConnect" IsDisabled="OpcDaServer.IsConnected"></Button>
<Button Text="断开" OnClick="OnDisConnect" IsDisabled="!OpcDaServer.IsConnected"
Color="Color.Danger"></Button>
</BootstrapInputGroup>
</p>

<p class="code-label">2. 点击 <b>读取</b> 按钮读取 <code>OpcDa</code> 服务器上的位号值</p>
<div class="row g-3 mb-3">
<div class="col-12 col-sm-6">
<BootstrapInputGroup>
<BootstrapInputGroupLabel DisplayText="转速"></BootstrapInputGroupLabel>
<BootstrapInputGroupLabel DisplayText="@Tag1"></BootstrapInputGroupLabel>
<Display @bind-Value="@_tagValue1"></Display>
</BootstrapInputGroup>
</div>
<div class="col-12 col-sm-6">
<BootstrapInputGroup>
<BootstrapInputGroupLabel DisplayText="流速"></BootstrapInputGroupLabel>
<BootstrapInputGroupLabel DisplayText="@Tag2"></BootstrapInputGroupLabel>
<Display @bind-Value="@_tagValue2"></Display>
</BootstrapInputGroup>
</div>
<div class="col-12 col-sm-6">
<Button OnClick="OnRead" Text="读取" IsDisabled="!OpcDaServer.IsConnected"></Button>
</div>
</div>

<p class="code-label">3. 订阅功能</p>
<p>通过订阅可以监控一组 <b>位号</b> 数据改变情况,当数据改变时通过 <code>DataChanged</code> 回调方法通知订阅者</p>
<div class="row g-3">
<div class="col-12 col-sm-6">
<BootstrapInputGroup>
<BootstrapInputGroupLabel DisplayText="订阅名称"></BootstrapInputGroupLabel>
<BootstrapInputGroupLabel DisplayText="Subscription1"></BootstrapInputGroupLabel>
</BootstrapInputGroup>
</div>
<div class="col-12 col-sm-6">
<BootstrapInputGroup>
<BootstrapInputGroupLabel DisplayText="更新频率"></BootstrapInputGroupLabel>
<BootstrapInputGroupLabel DisplayText="1000"></BootstrapInputGroupLabel>
</BootstrapInputGroup>
</div>
<div class="col-12 col-sm-6">
<BootstrapInputGroup>
<BootstrapInputGroupLabel DisplayText="转速"></BootstrapInputGroupLabel>
<BootstrapInputGroupLabel DisplayText="@Tag3"></BootstrapInputGroupLabel>
<Display @bind-Value="@_tagValue3"></Display>
</BootstrapInputGroup>
</div>
<div class="col-12 col-sm-6">
<BootstrapInputGroup>
<BootstrapInputGroupLabel DisplayText="流速"></BootstrapInputGroupLabel>
<BootstrapInputGroupLabel DisplayText="@Tag4"></BootstrapInputGroupLabel>
<Display @bind-Value="@_tagValue4"></Display>
</BootstrapInputGroup>
</div>
<div class="col-12 col-sm-6">
<Button OnClick="OnCreateSubscription" Text="订阅" IsDisabled="@(!OpcDaServer.IsConnected || _subscribed)"></Button>
<Button OnClick="OnCancelSubscription" Text="取消" IsDisabled="!_subscribed"></Button>
</div>
</div>
</DemoBlock>
86 changes: 86 additions & 0 deletions src/BootstrapBlazor.Server/Components/Samples/OpcDa.razor.cs
Original file line number Diff line number Diff line change
@@ -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([email protected]) Website: https://www.blazor.zone

using BootstrapBlazor.OpcDa;
using System.Globalization;

namespace BootstrapBlazor.Server.Components.Samples;

/// <summary>
/// OpcDa 示例
/// </summary>
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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Potential InvalidCastException if Value is not int.

Casting values.ElementAt(1).Value to int without checking its type can cause a runtime exception if the value is not an int. Use a type check or safe conversion to prevent this.

_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<OpcReadItem> items)
{
_tagValue3 = items[0].Value?.ToString();
var v = (int)items[1].Value! / 100d;
_tagValue4 = v.ToString(CultureInfo.InvariantCulture);

InvokeAsync(StateHasChanged);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone

using BootstrapBlazor.OpcDa;
using Longbow.Tasks.Services;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Options;
Expand Down Expand Up @@ -45,6 +46,16 @@ public static IServiceCollection AddBootstrapBlazorServerService(this IServiceCo
services.AddHostedService<MockCustomProtocolSocketServerService>();
services.AddHostedService<MockDisconnectServerService>();

if (OperatingSystem.IsWindows())
{
services.AddOpcDaServer();
}
else
{
// 增加 OpcDa 模拟服务(给 Linux 平台使用)
services.AddMockOpcDaServer();
}

// 增加通用服务
services.AddBootstrapBlazorServices();

Expand Down
9 changes: 8 additions & 1 deletion src/BootstrapBlazor.Server/Locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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 <code>IOpcDaServer</code> and call the <code>Read</code> method to get the PLC Tag value."
}
}
9 changes: 8 additions & 1 deletion src/BootstrapBlazor.Server/Locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -4846,7 +4846,8 @@
"SocketAutoConnect": "自动重连",
"SocketDataEntity": "通讯数据转实体类",
"NetworkMonitor": "网络状态 NetworkMonitor",
"Toolbar": "工具栏 Toolbar"
"Toolbar": "工具栏 Toolbar",
"OpcDaService": "OpcDaServer 服务"
},
"BootstrapBlazor.Server.Components.Samples.Table.TablesHeader": {
"TablesHeaderTitle": "表头分组功能",
Expand Down Expand Up @@ -7158,5 +7159,11 @@
"ToolbarSubTitle": "是按钮或其他应用程序特定工具的容器",
"NormalTitle": "基本用法",
"NormalIntro": ""
},
"BootstrapBlazor.Server.Components.Samples.OpcDa": {
"OpcDaTitle": "OpcDa Server 服务",
"OpcDaDescription": "连接 OpcDa Server 获得 PLC 实时数据",
"OpcDaNormalTitle": "基本用法",
"OpcDaNormalIntro": "通过注入服务 <code>IOpcDaServer</code> 获得实例,调用 <code>Read</code> 方法获得 PLC 位号值"
}
}
3 changes: 2 additions & 1 deletion src/BootstrapBlazor.Server/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading