Skip to content

Commit 3ecc842

Browse files
authored
doc(Modbus): add IModbusFactory documentation (dotnetcore#6717)
* chore: 更新字典 * doc: 调整菜单 * doc: 更新菜单 * doc: 更新字典 * chore: 更改注入服务位置 * doc: 增加 Modbus 服务文档 * chore: 增加 Modbus 包依赖 * doc: 更新文档
1 parent 9ec0c0b commit 3ecc842

File tree

11 files changed

+197
-15
lines changed

11 files changed

+197
-15
lines changed

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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
<PackageReference Include="BootstrapBlazor.VideoPlayer" Version="9.0.3" />
8282
<PackageReference Include="BootstrapBlazor.WinBox" Version="9.0.7" />
8383
<PackageReference Include="Longbow.Logging" Version="9.0.1" />
84+
<PackageReference Include="Longbow.Modbus" Version="9.0.0-beta01" />
8485
<PackageReference Include="Longbow.Socket" Version="9.0.1" />
8586
<PackageReference Include="Longbow.Tasks" Version="9.0.2" />
8687
<PackageReference Include="Longbow.TcpSocket" Version="9.0.1" />
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
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+
<p class="code-label">1. 服务注入</p>
10+
11+
<Pre>services.AddModbusFactory();</Pre>
12+
13+
<p class="code-label">2. 使用服务</p>
14+
<p>调用 <code>ModbusFactory</code> 实例方法 <code>GetOrCreateTcpMaster</code> 即可得到一个 <code>IModbusClient</code> 实例。内部提供复用机制,调用两次得到的 <code>IModbusClient</code> 为同一对象</p>
15+
16+
<Pre>[Inject]
17+
[NotNull]
18+
private IModbusFactory? ModbusFactory { get; set; }</Pre>
19+
20+
<Pre>var client = ModbusFactory.GetOrCreateTcpMaster("bb", options =>
21+
{
22+
options.LocalEndPoint = new IPEndPoint(IPAddress.Loopback, 0);
23+
});</Pre>
24+
25+
<p class="code-label">3. 使用方法</p>
26+
27+
<ul class="ul-demo">
28+
<li>通过 <code>ITcpSocketClient</code> 实例方法 <code>ConnectAsync</code> 连接远端节点</li>
29+
<li>通过 <code>ITcpSocketClient</code> 实例方法 <code>SendAsync</code> 发送协议数据</li>
30+
<li>通过 <code>ITcpSocketClient</code> 实例方法 <code>Close</code> 关闭连接</li>
31+
<li>通过 <code>ITcpSocketClient</code> 实例方法 <code>SetDataHandler</code> 方法设置数据处理器</li>
32+
<li>通过 <code>ITcpSocketClient</code> 实例属性 <code>ReceivedCallBack</code> 方法设置接收数据处理器(注意:此回调未做任何数据处理为原始数据)</li>
33+
</ul>
34+
35+
<p class="code-label">4. 数据处理器</p>
36+
37+
<p>在我们实际应用中,建立套接字连接后就会进行数据通信,数据通信不会是杂乱无章的随机数据,在应用中都是有双方遵守的规约简称通讯协议,在通讯协议的约束下,发送方与接收方均根据通讯协议进行编码或解码工作,将数据有条不紊的传输</p>
38+
39+
<p>数据处理器设计初衷就是为了契合通讯协议大大简化我们开发逻辑,我们已通讯协议每次通讯电文均为 <b>4</b> 位定长举例说明,在实际的通讯过程中,我们接收到的通讯数据存在粘包或者分包的现象</p>
40+
41+
<ul class="ul-demo">
42+
<li><b>粘包</b>:比如我们期望收到 <b>1234</b> 四个字符,实际上我们接收到的是 <b>123412</b> 多出来的 <b>12</b> 其实是下一个数据包的内容,我们需要截取前 4 位数据作为一个数据包才能正确处理数据,这种相邻两个通讯数据包的粘连称为<b>粘包</b></li>
43+
<li><b>分包</b>:比如我们期望收到 <b>1234</b> 四个字符,实际上我们可能分两次接收到,分别是 <b>12</b> 和 <b>34</b>,我们需要将两个数据包拼接成一个才能正确的处理数据。这种情况称为<b>分包</b></li>
44+
</ul>
45+
46+
<p>我们内置了一些常用的数据处理类 <code>IDataPackageHandler</code> 接口为数据包处理接口,虚类 <code>DataPackageHandlerBase</code> 作为数据处理器基类已经内置了 <b>粘包</b> <b>分包</b> 的逻辑,继承此类后专注自己处理的业务即可</p>
47+
48+
<p>使用方法如下:</p>
49+
50+
<Pre>[Inject]
51+
[NotNull]
52+
private ITcpSocketFactory? TcpSocketFactory { get; set; }
53+
54+
private async Task CreateClient()
55+
{
56+
// 创建 ITcpSocketClient 实例
57+
var client = TcpSocketFactory.GetOrCreate("localhost", 0);
58+
59+
// 设置数据适配器 使用 FixLengthDataPackageHandler 数据处理器处理数据定长 4 的数据
60+
var adapter = new DataPackageAdapter
61+
{
62+
DataPackageHandler = new FixLengthDataPackageHandler(4)
63+
};
64+
65+
// 如果 client 不销毁切记使用 RemoveDataPackageAdapter 移除回调委托防止内存泄露
66+
client.AddDataPackageAdapter(adapter, buffer =>
67+
{
68+
// buffer 即是接收到的数据
69+
return ValueTask.CompletedTask;
70+
});
71+
72+
// 连接远端节点 连接成功后自动开始接收数据
73+
var connected = await client.ConnectAsync("192.168.10.100", 6688);
74+
}
75+
</Pre>
76+
77+
<p>内置数据处理器</p>
78+
79+
<ul class="ul-demo">
80+
<li><code>FixLengthDataPackageHandler</code> <b>固定长度数据处理器</b> 即每个通讯包都是固定长度</li>
81+
<li><code>DelimiterDataPackageHandler</code> <b>分隔符数据处理器</b> 即通讯包以特定一个或一组字节分割</li>
82+
</ul>
83+
84+
<p class="code-label">5. 数据适配器</p>
85+
86+
<p>在我们实际应用中,接收到数据包后(已经过数据处理器)大多情况下是需要将电文转化为应用中的具体数据类型 <code>Class</code> 或 <code>Struct</code>。将原始数据包转化为类或者结构体的过程由我们的数据适配器来实现</p>
87+
88+
<p>数据适配器设计思路如下</p>
89+
90+
<ol class="ul-demo">
91+
<li>使用 <code>DataTypeConverterAttribute</code> 标签约定通讯数据使用那个转换类型进行转换 指定类型需继承 <code>IDataConverter</code>
92+
接口
93+
</li>
94+
<li>使用 <code>DataPropertyConverterAttribute</code> 标签约定如何转换数据类型 (Property) 属性值</li>
95+
</ol>
96+
97+
<Pre>[DataTypeConverter(Type = typeof(DataConverter&lt;MockEntity&gt;))]
98+
class MockEntity
99+
{
100+
[DataPropertyConverter(Type = typeof(byte[]), Offset = 0, Length = 5)]
101+
public byte[]? Header { get; set; }
102+
103+
[DataPropertyConverter(Type = typeof(byte[]), Offset = 5, Length = 2)]
104+
public byte[]? Body { get; set; }
105+
106+
[DataPropertyConverter(Type = typeof(Foo), Offset = 7, Length = 1, ConverterType = typeof(FooConverter), ConverterParameters = ["test"])]
107+
public string? Value1 { get; set; }
108+
}</Pre>
109+
110+
<Pre>class FooConverter(string name) : IDataPropertyConverter
111+
{
112+
public object? Convert(ReadOnlyMemory&lt;byte&gt; data)
113+
{
114+
return new Foo() { Id = data.Span[0], Name = name };
115+
}
116+
}</Pre>
117+
118+
<p class="code-label">针对第三方程序集的数据类型解决方案如下:</p>
119+
<p>使用 <code></code></p>
120+
121+
<Pre>builder.Services.ConfigureDataConverters(options =>
122+
{
123+
options.AddTypeConverter&lt;MockEntity&gt;();
124+
options.AddPropertyConverter&lt;MockEntity&gt;(entity =&gt; entity.Header, new DataPropertyConverterAttribute()
125+
{
126+
Offset = 0,
127+
Length = 5
128+
});
129+
options.AddPropertyConverter&lt;MockEntity&gt;(entity =&gt; entity.Body, new DataPropertyConverterAttribute()
130+
{
131+
Offset = 5,
132+
Length = 2
133+
});
134+
});
135+
</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+
}

src/BootstrapBlazor.Server/Components/Samples/SocketFactories.razor renamed to src/BootstrapBlazor.Server/Components/Samples/Sockets/SocketFactories.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
<p class="code-label">1. 服务注入</p>
1010

11-
<Pre>services.AddBootstrapBlazorTcpSocketFactory();</Pre>
11+
<Pre>services.AddTcpSocketFactory();</Pre>
1212

1313
<p class="code-label">2. 使用服务</p>
1414
<p>调用 <code>TcpSocketFactory</code> 实例方法 <code>GetOrCreate</code> 即可得到一个 <code>ITcpSocketClient</code> 实例。内部提供复用机制,调用两次得到的 <code>ITcpSocketClient</code> 为同一对象</p>

src/BootstrapBlazor.Server/Components/Samples/SocketFactories.razor.cs renamed to src/BootstrapBlazor.Server/Components/Samples/Sockets/SocketFactories.razor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone
55

6-
namespace BootstrapBlazor.Server.Components.Samples;
6+
namespace BootstrapBlazor.Server.Components.Samples.Sockets;
77

88
/// <summary>
99
/// ISocketFactory 服务说明文档

src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ public static List<MenuItem> GenerateMenus(this IStringLocalizer<NavMenu> Locali
9090
};
9191
AddSocket(item);
9292

93+
item = new DemoMenuItem()
94+
{
95+
Text = Localizer["ModbusComponents"],
96+
Icon = "fa-fw fa-solid fa-satellite-dish text-danger"
97+
};
98+
AddModbus(item);
99+
93100
item = new DemoMenuItem()
94101
{
95102
Text = Localizer["Services"],
@@ -207,6 +214,12 @@ void AddSocket(DemoMenuItem item)
207214
{
208215
item.Items = new List<DemoMenuItem>
209216
{
217+
new()
218+
{
219+
IsNew = true,
220+
Text = Localizer["TcpSocketFactory"],
221+
Url = "socket-factory"
222+
},
210223
new()
211224
{
212225
Text = Localizer["SocketManualReceive"],
@@ -234,7 +247,23 @@ void AddSocket(DemoMenuItem item)
234247
Url = "socket/data-entity"
235248
}
236249
};
237-
AddBadge(item, count: 1);
250+
251+
AddBadge(item, count: 2);
252+
}
253+
254+
void AddModbus(DemoMenuItem item)
255+
{
256+
item.Items = new List<DemoMenuItem>
257+
{
258+
new()
259+
{
260+
IsNew = true,
261+
Text = Localizer["ModbusFactory"],
262+
Url = "modbus-factory"
263+
}
264+
};
265+
266+
AddBadge(item, count: 2);
238267
}
239268

240269
void AddQuickStar(DemoMenuItem item)
@@ -1641,12 +1670,6 @@ void AddServices(DemoMenuItem item)
16411670
Url = "print-service"
16421671
},
16431672
new()
1644-
{
1645-
IsNew = true,
1646-
Text = Localizer["TcpSocketFactory"],
1647-
Url = "socket-factory"
1648-
},
1649-
new()
16501673
{
16511674
Text = Localizer["ThemeProvider"],
16521675
Url = "theme-provider"

src/BootstrapBlazor.Server/Extensions/ServiceCollectionExtensions.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ public static IServiceCollection AddBootstrapBlazorServerService(this IServiceCo
5252
services.AddMockOpcDaServer();
5353
}
5454

55+
// 增加 ITcpSocketFactory 服务
56+
services.AddTcpSocketFactory();
57+
58+
// 增加 IModbusFactory 服务
59+
services.AddModbusFactory();
60+
5561
// 增加通用服务
5662
services.AddBootstrapBlazorServices();
5763

src/BootstrapBlazor.Server/Extensions/ServiceCollectionSharedExtensions.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,6 @@ public static IServiceCollection AddBootstrapBlazorServices(this IServiceCollect
9494
// 增加 JuHe 定位服务
9595
services.AddBootstrapBlazorJuHeIpLocatorService();
9696

97-
// 增加 ITcpSocketFactory 服务
98-
services.AddTcpSocketFactory();
99-
10097
// 增加 PetaPoco ORM 数据服务操作类
10198
// 需要时打开下面代码
10299
//services.AddPetaPoco(option =>

src/BootstrapBlazor.Server/Locales/en-US.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4855,12 +4855,14 @@
48554855
"Vditor": "Vditor Markdown",
48564856
"TcpSocketFactory": "ITcpSocketFactory",
48574857
"OfficeViewer": "Office Viewer",
4858-
"SocketComponents": "ITcpSocketFactory",
4858+
"SocketComponents": "Socket Services",
48594859
"SocketAutoReceive": "Auto Receive",
48604860
"SocketManualReceive": "Manual Receive",
48614861
"DataPackageAdapter": "DataPackageAdapter",
48624862
"SocketAutoConnect": "Reconnect",
48634863
"SocketDataEntity": "DataEntity",
4864+
"ModbusComponents": "Modbus Services",
4865+
"ModbusFactory": "IModbusFactory",
48644866
"NetworkMonitor": "Network Monitor",
48654867
"Toolbar": "Toolbar",
48664868
"OpcDaService": "OpcDaServer",

0 commit comments

Comments
 (0)