Skip to content

Commit a8ed5a2

Browse files
committed
Merge branch 'main' into feat-net10
2 parents 8a5e3a8 + d451a73 commit a8ed5a2

File tree

142 files changed

+1160
-394
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

142 files changed

+1160
-394
lines changed

.github/workflows/pack.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ on:
1010
jobs:
1111
pack:
1212
runs-on: ubuntu-latest
13+
permissions:
14+
id-token: write
15+
contents: read
1316

1417
steps:
1518
- uses: actions/checkout@v4
@@ -19,9 +22,15 @@ jobs:
1922
with:
2023
dotnet-version: 10.0.x
2124

25+
- name: NuGet login
26+
uses: NuGet/login@v1
27+
id: login
28+
with:
29+
user: ${{ secrets.NUGET_USER }}
30+
2231
- name: Publish to Nuget
2332
env:
24-
NUGET_API_KEY: ${{secrets.NUGET_API_KEY}}
33+
NUGET_API_KEY: ${{steps.login.outputs.NUGET_API_KEY}}
2534
Bundle: True
2635

2736
run: |

BootstrapBlazor.slnx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
<Solution>
2+
<Folder Name="/actions/">
3+
<File Path=".github/workflows/auto-pull-request-checks.yml" />
4+
<File Path=".github/workflows/build.yml" />
5+
<File Path=".github/workflows/docker.yml" />
6+
<File Path=".github/workflows/pack.yml" />
7+
<File Path=".github/workflows/publish.yml" />
8+
</Folder>
29
<Folder Name="/configuration/">
310
<File Path=".editorconfig" />
411
<File Path=".gitignore" />
512
<File Path="exclusion.dic" />
613
<File Path="Framework.props" />
14+
<File Path="Version.props" />
715
</Folder>
816
<Folder Name="/docs/">
917
<File Path="README.md" />
@@ -15,6 +23,7 @@
1523
<File Path="localization/pt.json" />
1624
<File Path="localization/ru-RU.json" />
1725
<File Path="localization/th-TH.json" />
26+
<File Path="localization/uk-UA.json" />
1827
<File Path="localization/zh-TW.json" />
1928
</Folder>
2029
<Folder Name="/scripts/" />

delete-bin.cmd

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
@ECHO off
2+
cls
3+
4+
ECHO Deleting all BIN and OBJ folders...
5+
ECHO.
6+
7+
FOR /d /r . %%d in (bin,obj) DO (
8+
IF EXIST "%%d" (
9+
ECHO %%d | FIND /I "\node_modules\" > Nul && (
10+
ECHO.Skipping: %%d
11+
) || (
12+
ECHO.Deleting: %%d
13+
rd /s/q "%%d"
14+
)
15+
)
16+
)
17+
18+
ECHO.
19+
ECHO.BIN and OBJ folders have been successfully deleted. Press any key to exit.
20+
pause > nul

exclusion.dic

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,6 @@ univer
118118
rdkit
119119
webkitdirectory
120120
dotx
121+
Modbus
122+
Protocol
123+
vditor

src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,18 @@
2929
<PackageReference Include="BootstrapBlazor.BarCode" Version="9.0.1" />
3030
<PackageReference Include="BootstrapBlazor.BarcodeGenerator" Version="9.0.0" />
3131
<PackageReference Include="BootstrapBlazor.BootstrapIcon" Version="9.0.2" />
32-
<PackageReference Include="BootstrapBlazor.Chart" Version="9.0.0" />
32+
<PackageReference Include="BootstrapBlazor.Chart" Version="9.0.1" />
3333
<PackageReference Include="BootstrapBlazor.ChatBot" Version="9.0.0" />
3434
<PackageReference Include="BootstrapBlazor.CherryMarkdown" Version="9.0.3" />
3535
<PackageReference Include="BootstrapBlazor.CodeEditor" Version="9.0.3" />
3636
<PackageReference Include="BootstrapBlazor.Dock" Version="9.0.0" />
37-
<PackageReference Include="BootstrapBlazor.DockView" Version="9.1.18" />
37+
<PackageReference Include="BootstrapBlazor.DockView" Version="9.1.19" />
3838
<PackageReference Include="BootstrapBlazor.Dom2Image" Version="9.0.0" />
3939
<PackageReference Include="BootstrapBlazor.DriverJs" Version="9.0.3" />
4040
<PackageReference Include="BootstrapBlazor.ElementIcon" Version="9.0.3" />
4141
<PackageReference Include="BootstrapBlazor.FileViewer" Version="9.0.0" />
4242
<PackageReference Include="BootstrapBlazor.FluentSystemIcon" Version="9.0.1" />
43-
<PackageReference Include="BootstrapBlazor.FontAwesome" Version="9.1.0" />
43+
<PackageReference Include="BootstrapBlazor.FontAwesome" Version="9.1.1" />
4444
<PackageReference Include="BootstrapBlazor.Gantt" Version="9.0.2" />
4545
<PackageReference Include="BootstrapBlazor.Holiday" Version="9.0.1" />
4646
<PackageReference Include="BootstrapBlazor.Html2Image" Version="9.0.2" />
@@ -68,22 +68,23 @@
6868
<PackageReference Include="BootstrapBlazor.RDKit" Version="9.0.2" />
6969
<PackageReference Include="BootstrapBlazor.SignaturePad" Version="9.0.1" />
7070
<PackageReference Include="BootstrapBlazor.SmilesDrawer" Version="9.0.2" />
71-
<PackageReference Include="BootstrapBlazor.Socket" Version="9.0.4" />
7271
<PackageReference Include="BootstrapBlazor.Sortable" Version="9.0.3" />
7372
<PackageReference Include="BootstrapBlazor.Splitting" Version="9.0.3" />
7473
<PackageReference Include="BootstrapBlazor.SvgEditor" Version="9.0.4" />
7574
<PackageReference Include="BootstrapBlazor.SummerNote" Version="9.0.7" />
7675
<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.6" />
7776
<PackageReference Include="BootstrapBlazor.Tasks.Dashboard" Version="9.0.0" />
78-
<PackageReference Include="BootstrapBlazor.TcpSocket" Version="9.0.4" />
7977
<PackageReference Include="BootstrapBlazor.Topology" Version="9.0.1" />
8078
<PackageReference Include="BootstrapBlazor.UniverIcon" Version="9.0.1" />
8179
<PackageReference Include="BootstrapBlazor.UniverSheet" Version="9.0.5" />
8280
<PackageReference Include="BootstrapBlazor.Vditor" Version="9.0.0" />
8381
<PackageReference Include="BootstrapBlazor.VideoPlayer" Version="9.0.3" />
8482
<PackageReference Include="BootstrapBlazor.WinBox" Version="9.0.7" />
8583
<PackageReference Include="Longbow.Logging" Version="9.0.1" />
84+
<PackageReference Include="Longbow.Modbus" Version="9.1.1" />
85+
<PackageReference Include="Longbow.Sockets" Version="9.0.4" />
8686
<PackageReference Include="Longbow.Tasks" Version="9.0.2" />
87+
<PackageReference Include="Longbow.TcpSocket" Version="9.0.13" />
8788
</ItemGroup>
8889

8990
<ItemGroup>
@@ -92,15 +93,15 @@
9293

9394
<ItemGroup>
9495
<Using Include="BootstrapBlazor.Components" />
95-
<Using Include="BootstrapBlazor.DataAdapters" />
96-
<Using Include="BootstrapBlazor.DataHandlers" />
97-
<Using Include="BootstrapBlazor.DataConverters" />
98-
<Using Include="BootstrapBlazor.TcpSocket" />
9996
<Using Include="BootstrapBlazor.Server.Components.Components" />
10097
<Using Include="BootstrapBlazor.Server.Components.Layout" />
10198
<Using Include="BootstrapBlazor.Server.Data" />
10299
<Using Include="BootstrapBlazor.Server.Extensions" />
103100
<Using Include="BootstrapBlazor.Server.Services" />
101+
<Using Include="Longbow.Sockets.DataAdapters" />
102+
<Using Include="Longbow.Sockets.DataConverters" />
103+
<Using Include="Longbow.Sockets.DataHandlers" />
104+
<Using Include="Longbow.TcpSocket" />
104105
<Using Include="Microsoft.AspNetCore.Components" />
105106
<Using Include="Microsoft.Extensions.Configuration" />
106107
<Using Include="Microsoft.Extensions.DependencyInjection" />

src/BootstrapBlazor.Server/Components/Samples/AutoCompletes.razor

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
<h4>@Localizer["Description"]</h4>
77

88
<DemoBlock Title="@Localizer["Block1Title"]" Introduction="@Localizer["Block1Intro"]" Name="Normal">
9-
<section ignore>@Localizer["NormalDescription"]</section>
9+
<section ignore>@((MarkupString)Localizer["NormalDescription"].Value)</section>
1010
<div style="width: 200px;">
11-
<AutoComplete Value="@_value" Items="@StaticItems" IsSelectAllTextOnFocus="true"></AutoComplete>
11+
<AutoComplete Value="@_value" Items="@StaticItems" IsSelectAllTextOnFocus="true" IsClearable></AutoComplete>
1212
</div>
1313
</DemoBlock>
1414

src/BootstrapBlazor.Server/Components/Samples/Charts/Bar.razor.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,15 @@ private Task<ChartDataSource> OnInit(bool stacked, bool setTitle = true)
5959
ds.Options.Title = "Bar Histogram";
6060
}
6161

62+
ds.Options.ShowDataLabel = true;
6263
ds.Options.X.Title = "days";
6364
ds.Options.Y.Title = "Numerical value";
6465
ds.Options.X.Stacked = stacked;
6566
ds.Options.Y.Stacked = stacked;
6667
ds.Labels = Enumerable.Range(1, _barDataCount).Select(i => i.ToString());
6768
for (var index = 0; index < _barDatasetCount; index++)
6869
{
69-
ds.Data.Add(new ChartDataset() { Label = $"Set {index}", Data = Enumerable.Range(1, _barDataCount).Select(i => Random.Shared.Next(20, 37)).Cast<object>() });
70+
ds.Data.Add(new ChartDataset() { Label = $"Set {index}", Data = Enumerable.Range(1, _barDataCount).Select(i => Random.Shared.Next(20, 37) / 10.0f).Cast<object>() });
7071
}
7172

7273
return Task.FromResult(ds);

src/BootstrapBlazor.Server/Components/Samples/ListGroups.razor.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,14 @@ private AttributeItem[] GetAttributes() =>
8787
DefaultValue = " — "
8888
},
8989
new()
90+
{
91+
Name = "OnDoubleClickItem",
92+
Description = Localizer["AttrOnDoubleClickItem"],
93+
Type = "Func<TItem, Task>",
94+
ValueList = " — ",
95+
DefaultValue = " — "
96+
},
97+
new()
9098
{
9199
Name = "GetItemDisplayText",
92100
Description = Localizer["GetItemDisplayText"],
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
@page "/modbus-factory"
2+
@inject IStringLocalizer<ModbusFactories> Localizer
3+
4+
<h3>Modbus 串行通信服务 <code>IModbusFactory</code></h3>
5+
<h4>组件库内置了 Modbus 串行通信服务</h4>
6+
7+
<PackageTips Name="Longbow.Modbus" />
8+
9+
<Tips><div>特别注意:本服务不支持 <code>wasm</code> 模式</div></Tips>
10+
11+
<p class="code-label">1. 服务注入</p>
12+
13+
<Pre>services.AddModbusFactory();</Pre>
14+
15+
<Pre>[Inject]
16+
[NotNull]
17+
private IModbusFactory? ModbusFactory { get; set; }</Pre>
18+
19+
<p class="code-label">2. 使用服务</p>
20+
<p>调用 <code>ModbusFactory</code> 实例方法 <code>GetOrCreateTcpMaster</code> 即可得到一个 <code>IModbusClient</code> 实例。内部提供复用机制,调用两次得到的 <code>IModbusClient</code> 为同一对象</p>
21+
22+
<p class="code-label">3. 通过工厂获得相对应协议 <code>IModbusClient</code> 实例</p>
23+
24+
<p><code>IModbusClient</code> 有两个派生类</p>
25+
26+
<ul class="ul-demo">
27+
<li><code>Socket</code> 通讯协议 <code>Tcp/Udp</code> 使用 <code>IModbusTcpClient</code></li>
28+
<li><code>SerialPort</code> 通讯协议 <code>RTU</code> 使用 <code>IModbusRtuClient</code></li>
29+
</ul>
30+
31+
<p>Modbus 可以通过不同的物理介质传输,主要有以下几种方式:</p>
32+
33+
<ul class="ul-demo">
34+
<li><code>Modbus RTU (Remote Terminal Unit)</code>: 采用二进制编码,使用紧凑的二进制表示数据,效率高,是最常用的串行通信模式。通常基于 <code>RS-485</code>(支持多设备)或 <code>RS-232</code>(点对点)物理层,CRC 校验确保数据完整性。</li>
35+
<li><code>Modbus TCP/IP</code>: 运行于以太网上,使用 <code>TCP/IP</code> 协议,默认端口 <code>502</code>。它在 Modbus RTU 协议基础上添加了 MBAP 报文头,并由于TCP本身是可靠连接的服务,因此去掉了 CRC 校验码。</li>
36+
</ul>
37+
38+
<p><code>IModbusFactory</code> 实例方法</p>
39+
40+
<ul class="ul-demo">
41+
<li>通过 <code>GetOrCreateTcpMaster</code> 方法得到 <code>IModbusTcpClient</code> 实例</li>
42+
<li>通过 <code>GetOrCreateUdpMaster</code> 方法得到 <code>IModbusTcpClient</code> 实例</li>
43+
<li>通过 <code>GetOrCreateRtuMaster</code> 方法得到 <code>IModbusRtuClient</code> 实例</li>
44+
<li>通过 <code>GetOrCreateRtuOverTcpMaster</code> 方法得到 <code>IModbusTcpClient</code> 实例</li>
45+
<li>通过 <code>GetOrCreateRtuOverUdpMaster</code> 方法得到 <code>IModbusTcpClient</code> 实例</li>
46+
</ul>
47+
48+
<p>调用其对应的 <code>Remove</code> 方法即可从缓存中移除指定名称的 <code>IModbusClient</code> 实例。如</p>
49+
50+
<Pre>ModbusFactory.RemoveTcpMaster("test");</Pre>
51+
52+
<p class="code-label">4. 数据操作</p>
53+
54+
<p><code>Modbus</code> 数据类型共四种</p>
55+
56+
<ul class="ul-demo">
57+
<li>线圈 (Coils) 可读可写 数字量输出,如开关状态</li>
58+
<li>离散输入 (Discrete Inputs) 只读 数字量输出,如开关状态</li>
59+
<li>输入寄存器 (Input Registers) 只读 模拟量输入,如温度、压力传感器数据</li>
60+
<li>保持寄存器 (Holding Registers) 可读可写 模拟量输出,如设定值、控制参数</li>
61+
</ul>
62+
63+
<p>对应 <code>IModbusClient</code> 实例方法如下</p>
64+
65+
<ul class="ul-demo">
66+
<li>线圈 (Coils) <code>ReadCoilsAsync</code> <code>WriteCoilAsync</code> <code>WriteMultipleCoilsAsync</code></li>
67+
<li>离散输入 (Discrete Inputs) <code>ReadInputsAsync</code></li>
68+
<li>输入寄存器 (Input Registers) <code>ReadInputRegistersAsync</code></li>
69+
<li>保持寄存器 (Holding Registers) <code>ReadHoldingRegistersAsync</code> <code>WriteRegisterAsync</code> <code>WriteMultipleRegistersAsync</code></li>
70+
</ul>
71+
72+
<p>Modbus 协议的最大读取/写入数量限制参考自 Modbus Application Protocol Specification V1.1b3:</p>
73+
74+
<ul class="ul-demo">
75+
<li>线圈 (Coils) 最大读取数量: <code>2000</code>, 最大写入数量: <code>1968</code></li>
76+
<li>离散输入 (Discrete Inputs) 最大读取数量: <code>2000</code></li>
77+
<li>输入寄存器 (Input Registers) 最大读取数量: <code>125</code></li>
78+
<li>保持寄存器 (Holding Registers) 最大读取数量: <code>125</code>, 最大写入数量: <code>123</code></li>
79+
</ul>
80+
81+
<p><code>IModbusClient</code> 所有读取返回值均为 <code>IModbusResponse</code> 实例</p> 其定义如下:
82+
83+
<Pre>public interface IModbusResponse
84+
{
85+
// 获得 原始数据
86+
ReadOnlyMemory&lt;byte&gt; Buffer { get; }
87+
88+
// 获得 Longbow.Modbus.IModbusMessageBuilder 实例
89+
IModbusMessageBuilder Builder { get; }
90+
}</Pre>
91+
92+
<p>通过调用其扩展方法或者 <code>Builder</code> 属性 <code>IModbusMessageBuilder</code> 实例方法</p>
93+
94+
<ul class="ul-demo">
95+
<li><code>ReadBoolValues</code> 将 <code>IModbusResponse</code> 实例中 <code>Buffer</code> 转换成布尔数组</li>
96+
<li><code>ReadUShortValues</code> 将 <code>IModbusResponse</code> 实例中 <code>Buffer</code> 转换成无符号短整型数组</li>
97+
</ul>
98+
99+
<p>通过接口 <code>IModbusResponse</code> 获得到其原始数据 <code>Buffer</code> 可以通过自定义扩展非常方便的扩展出符合自己业务的数据类型。如通过连续 2 个寄存器存储的数据,得到遵循 IEEE 754 标准的 32 位 <b>浮点数</b></p>
100+
101+
<p><b>注意:</b>在将 <code>Buffer</code> 转换为自定义类型(如 32 位浮点数)时,需要注意字节序(Endianness)。字节序会影响数据的解释方式,错误的字节序可能导致解析结果不正确。请根据实际设备或协议规范选择合适的字节序进行转换。</p>
102+
103+
<p>项目包含 Benchmark 基准测试工程</p>
104+
105+
<Pre>private const int NumberOfTask = 10;
106+
private const int TaskNumberOfClient = 10;
107+
private const int ClientCount = 10;
108+
109+
private async Task InitLongbowModbus()
110+
{
111+
var sc = new ServiceCollection();
112+
sc.AddModbusFactory();
113+
114+
var provider = sc.BuildServiceProvider();
115+
var factory = provider.GetRequiredService&lt;IModbusFactory&gt;();
116+
117+
for (var index = 0; index &lt; ClientCount; index++)
118+
{
119+
var client = factory.GetOrCreateTcpMaster();
120+
await client.ConnectAsync("127.0.0.1", 502);
121+
await client.ReadHoldingRegistersAsync(0x01, 0x00, 100);
122+
123+
_lgbModbusClients.Add(client);
124+
}
125+
}
126+
127+
[Benchmark]
128+
public async Task LongbowModbus()
129+
{
130+
var tasks = _lgbModbusClients.SelectMany(c =>
131+
{
132+
var tasks = new List&lt;Task&gt;();
133+
for (int i = 0; i &lt; TaskNumberOfClient; i++)
134+
{
135+
tasks.Add(Task.Run(async () =&gt;
136+
{
137+
for (int i = 0; i &lt; NumberOfTask; i++)
138+
{
139+
var d = await c.ReadHoldingRegistersAsync(1, 0, 100);
140+
}
141+
}));
142+
}
143+
return tasks;
144+
}).ToList();
145+
146+
await Task.WhenAll(tasks);
147+
}</Pre>
148+
149+
<p>共 10 个 <code>Socket</code> 连接,每个连接 10 个并发任务,每个任务进行 10 次 <code>ReadHoldingRegistersAsync</code> 方法调用,每个方法读取 100 个地址数据</p>
150+
151+
<p>Benchmark 结果如下:</p>
152+
153+
<Pre>| Method | Mean | Error | StdDev | Allocated |
154+
|------------------ |--------:|---------:|---------:|----------:|
155+
| LongbowModbus | 2.442 s | 0.0471 s | 0.0503 s | 1.14 MB |
156+
| NModbus | 4.752 s | 0.1544 s | 0.4552 s | 3.3 MB |</Pre>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License
3+
// See the LICENSE file in the project root for more information.
4+
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone
5+
6+
namespace BootstrapBlazor.Server.Components.Samples.Modbus;
7+
8+
/// <summary>
9+
/// IModbusFactory 服务说明文档
10+
/// </summary>
11+
public partial class ModbusFactories
12+
{
13+
14+
}

0 commit comments

Comments
 (0)