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
3 changes: 3 additions & 0 deletions exclusion.dic
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,6 @@ univer
rdkit
webkitdirectory
dotx
Modbus
Protocol
vditor
1 change: 1 addition & 0 deletions src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
<PackageReference Include="BootstrapBlazor.VideoPlayer" Version="9.0.3" />
<PackageReference Include="BootstrapBlazor.WinBox" Version="9.0.7" />
<PackageReference Include="Longbow.Logging" Version="9.0.1" />
<PackageReference Include="Longbow.Modbus" Version="9.0.0-beta01" />
Copy link

Copilot AI Sep 5, 2025

Choose a reason for hiding this comment

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

Using a beta version (9.0.0-beta01) in production code may introduce instability. Consider using a stable release version if available.

Suggested change
<PackageReference Include="Longbow.Modbus" Version="9.0.0-beta01" />
<PackageReference Include="Longbow.Modbus" Version="9.0.0" />

Copilot uses AI. Check for mistakes.
<PackageReference Include="Longbow.Socket" Version="9.0.1" />
<PackageReference Include="Longbow.Tasks" Version="9.0.2" />
<PackageReference Include="Longbow.TcpSocket" Version="9.0.1" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
@page "/modbus-factory"
@inject IStringLocalizer<ModbusFactories> Localizer

<h3>Modbus 串行通讯服务 <code>IModbusFactory</code></h3>
<h4>组件库内置了 Modbus 串行通讯服务</h4>

<PackageTips Name="Longbow.Modbus" />

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

<Pre>services.AddModbusFactory();</Pre>

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

<Pre>[Inject]
[NotNull]
private IModbusFactory? ModbusFactory { get; set; }</Pre>

<Pre>var client = ModbusFactory.GetOrCreateTcpMaster("bb", options =>
{
options.LocalEndPoint = new IPEndPoint(IPAddress.Loopback, 0);
});</Pre>

<p class="code-label">3. 使用方法</p>

<ul class="ul-demo">
<li>通过 <code>ITcpSocketClient</code> 实例方法 <code>ConnectAsync</code> 连接远端节点</li>
<li>通过 <code>ITcpSocketClient</code> 实例方法 <code>SendAsync</code> 发送协议数据</li>
<li>通过 <code>ITcpSocketClient</code> 实例方法 <code>Close</code> 关闭连接</li>
<li>通过 <code>ITcpSocketClient</code> 实例方法 <code>SetDataHandler</code> 方法设置数据处理器</li>
<li>通过 <code>ITcpSocketClient</code> 实例属性 <code>ReceivedCallBack</code> 方法设置接收数据处理器(注意:此回调未做任何数据处理为原始数据)</li>
Comment on lines +28 to +32
Copy link

Copilot AI Sep 5, 2025

Choose a reason for hiding this comment

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

The documentation incorrectly references ITcpSocketClient methods when describing IModbusClient usage. These should be updated to reference the appropriate Modbus client interface methods instead of TCP socket methods.

Suggested change
<li>通过 <code>ITcpSocketClient</code> 实例方法 <code>ConnectAsync</code> 连接远端节点</li>
<li>通过 <code>ITcpSocketClient</code> 实例方法 <code>SendAsync</code> 发送协议数据</li>
<li>通过 <code>ITcpSocketClient</code> 实例方法 <code>Close</code> 关闭连接</li>
<li>通过 <code>ITcpSocketClient</code> 实例方法 <code>SetDataHandler</code> 方法设置数据处理器</li>
<li>通过 <code>ITcpSocketClient</code> 实例属性 <code>ReceivedCallBack</code> 方法设置接收数据处理器(注意:此回调未做任何数据处理为原始数据)</li>
<li>通过 <code>IModbusClient</code> 实例方法 <code>ConnectAsync</code> 连接远端节点(如有需要)</li>
<li>通过 <code>IModbusClient</code> 实例方法 <code>ReadCoilsAsync</code> 或 <code>ReadRegistersAsync</code> 读取数据</li>
<li>通过 <code>IModbusClient</code> 实例方法 <code>WriteSingleCoilAsync</code> 或 <code>WriteSingleRegisterAsync</code> 写入数据</li>
<li>通过 <code>IModbusClient</code> 实例方法 <code>Close</code> 关闭连接</li>
<li>如需处理接收数据,可通过相关事件或回调方法进行设置(具体请参考 <code>IModbusClient</code> 的文档)</li>

Copilot uses AI. Check for mistakes.
</ul>

<p class="code-label">4. 数据处理器</p>

<p>在我们实际应用中建立套接字连接后就会进行数据通信,数据通信不会是杂乱无章的随机数据,在应用中都是有双方遵守的规约简称通讯协议,在通讯协议的约束下,发送方与接收方均根据通讯协议进行编码或解码工作,将数据有条不紊的传输</p>

<p>数据处理器设计初衷就是为了契合通讯协议大大简化我们开发逻辑,我们已通讯协议每次通讯电文均为 <b>4</b> 位定长举例说明,在实际的通讯过程中,我们接收到的通讯数据存在粘包或者分包的现象</p>

<ul class="ul-demo">
<li><b>粘包</b>比如我们期望收到 <b>1234</b> 四个字符,实际上我们接收到的是 <b>123412</b> 多出来的 <b>12</b> 其实是下一个数据包的内容,我们需要截取前 4 位数据作为一个数据包才能正确处理数据,这种相邻两个通讯数据包的粘连称为<b>粘包</b></li>
<li><b>分包</b>比如我们期望收到 <b>1234</b> 四个字符,实际上我们可能分两次接收到,分别是 <b>12</b><b>34</b>,我们需要将两个数据包拼接成一个才能正确的处理数据。这种情况称为<b>分包</b></li>
</ul>

<p>我们内置了一些常用的数据处理类 <code>IDataPackageHandler</code> 接口为数据包处理接口,虚类 <code>DataPackageHandlerBase</code> 作为数据处理器基类已经内置了 <b>粘包</b> <b>分包</b> 的逻辑,继承此类后专注自己处理的业务即可</p>

<p>使用方法如下:</p>

<Pre>[Inject]
[NotNull]
private ITcpSocketFactory? TcpSocketFactory { get; set; }

private async Task CreateClient()
{
// 创建 ITcpSocketClient 实例
var client = TcpSocketFactory.GetOrCreate("localhost", 0);

// 设置数据适配器 使用 FixLengthDataPackageHandler 数据处理器处理数据定长 4 的数据
var adapter = new DataPackageAdapter
{
DataPackageHandler = new FixLengthDataPackageHandler(4)
};

// 如果 client 不销毁切记使用 RemoveDataPackageAdapter 移除回调委托防止内存泄露
client.AddDataPackageAdapter(adapter, buffer =>
{
// buffer 即是接收到的数据
return ValueTask.CompletedTask;
});

// 连接远端节点 连接成功后自动开始接收数据
Comment on lines +52 to +72
Copy link

Copilot AI Sep 5, 2025

Choose a reason for hiding this comment

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

The code example incorrectly shows using ITcpSocketFactory and ITcpSocketClient in Modbus documentation. This should demonstrate IModbusFactory and IModbusClient usage instead.

Suggested change
private ITcpSocketFactory? TcpSocketFactory { get; set; }
private async Task CreateClient()
{
// 创建 ITcpSocketClient 实例
var client = TcpSocketFactory.GetOrCreate("localhost", 0);
// 设置数据适配器 使用 FixLengthDataPackageHandler 数据处理器处理数据定长 4 的数据
var adapter = new DataPackageAdapter
{
DataPackageHandler = new FixLengthDataPackageHandler(4)
};
// 如果 client 不销毁切记使用 RemoveDataPackageAdapter 移除回调委托防止内存泄露
client.AddDataPackageAdapter(adapter, buffer =>
{
// buffer 即是接收到的数据
return ValueTask.CompletedTask;
});
// 连接远端节点 连接成功后自动开始接收数据
private IModbusFactory? ModbusFactory { get; set; }
private async Task CreateModbusClient()
{
// 创建 IModbusClient 实例
var client = ModbusFactory.GetOrCreateTcpMaster("modbus", options =>
{
options.LocalEndPoint = new IPEndPoint(IPAddress.Loopback, 0);
});
// 设置数据包处理器(如有需要,可自定义处理器)
var handler = new FixLengthDataPackageHandler(4);
client.SetDataHandler(handler, buffer =>
{
// buffer 即是接收到的数据
return ValueTask.CompletedTask;
});
// 连接远端 Modbus 设备

Copilot uses AI. Check for mistakes.
var connected = await client.ConnectAsync("192.168.10.100", 6688);
}
</Pre>

<p>内置数据处理器</p>

<ul class="ul-demo">
<li><code>FixLengthDataPackageHandler</code> <b>固定长度数据处理器</b> 即每个通讯包都是固定长度</li>
<li><code>DelimiterDataPackageHandler</code> <b>分隔符数据处理器</b> 即通讯包以特定一个或一组字节分割</li>
</ul>

<p class="code-label">5. 数据适配器</p>

<p>在我们实际应用中接收到数据包后(已经过数据处理器)大多情况下是需要将电文转化为应用中的具体数据类型 <code>Class</code><code>Struct</code>。将原始数据包转化为类或者结构体的过程由我们的数据适配器来实现</p>

<p>数据适配器设计思路如下</p>

<ol class="ul-demo">
<li>使用 <code>DataTypeConverterAttribute</code> 标签约定通讯数据使用那个转换类型进行转换 指定类型需继承 <code>IDataConverter</code>
接口
</li>
<li>使用 <code>DataPropertyConverterAttribute</code> 标签约定如何转换数据类型 (Property) 属性值</li>
</ol>

<Pre>[DataTypeConverter(Type = typeof(DataConverter&lt;MockEntity&gt;))]
class MockEntity
{
[DataPropertyConverter(Type = typeof(byte[]), Offset = 0, Length = 5)]
public byte[]? Header { get; set; }

[DataPropertyConverter(Type = typeof(byte[]), Offset = 5, Length = 2)]
public byte[]? Body { get; set; }

[DataPropertyConverter(Type = typeof(Foo), Offset = 7, Length = 1, ConverterType = typeof(FooConverter), ConverterParameters = ["test"])]
public string? Value1 { get; set; }
}</Pre>

<Pre>class FooConverter(string name) : IDataPropertyConverter
{
public object? Convert(ReadOnlyMemory&lt;byte&gt; data)
{
return new Foo() { Id = data.Span[0], Name = name };
}
}</Pre>

<p class="code-label">针对第三方程序集的数据类型解决方案如下</p>
<p>使用 <code></code></p>
Copy link

Copilot AI Sep 5, 2025

Choose a reason for hiding this comment

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

Empty code tags with no content. This appears to be incomplete documentation that should either be removed or completed with the intended content.

Suggested change
<p>使用 <code></code></p>
<p>使用</p>

Copilot uses AI. Check for mistakes.

<Pre>builder.Services.ConfigureDataConverters(options =>
{
options.AddTypeConverter&lt;MockEntity&gt;();
options.AddPropertyConverter&lt;MockEntity&gt;(entity =&gt; entity.Header, new DataPropertyConverterAttribute()
{
Offset = 0,
Length = 5
});
options.AddPropertyConverter&lt;MockEntity&gt;(entity =&gt; entity.Body, new DataPropertyConverterAttribute()
{
Offset = 5,
Length = 2
});
});
</Pre>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// 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

namespace BootstrapBlazor.Server.Components.Samples.Modbus;

/// <summary>
/// IModbusFactory 服务说明文档
/// </summary>
public partial class ModbusFactories
{

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

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

<Pre>services.AddBootstrapBlazorTcpSocketFactory();</Pre>
<Pre>services.AddTcpSocketFactory();</Pre>

<p class="code-label">2. 使用服务</p>
<p>调用 <code>TcpSocketFactory</code> 实例方法 <code>GetOrCreate</code> 即可得到一个 <code>ITcpSocketClient</code> 实例。内部提供复用机制,调用两次得到的 <code>ITcpSocketClient</code> 为同一对象</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// See the LICENSE file in the project root for more information.
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone

namespace BootstrapBlazor.Server.Components.Samples;
namespace BootstrapBlazor.Server.Components.Samples.Sockets;

/// <summary>
/// ISocketFactory 服务说明文档
Expand Down
37 changes: 30 additions & 7 deletions src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ public static List<MenuItem> GenerateMenus(this IStringLocalizer<NavMenu> Locali
};
AddSocket(item);

item = new DemoMenuItem()
{
Text = Localizer["ModbusComponents"],
Icon = "fa-fw fa-solid fa-satellite-dish text-danger"
};
AddModbus(item);

item = new DemoMenuItem()
{
Text = Localizer["Services"],
Expand Down Expand Up @@ -207,6 +214,12 @@ void AddSocket(DemoMenuItem item)
{
item.Items = new List<DemoMenuItem>
{
new()
{
IsNew = true,
Text = Localizer["TcpSocketFactory"],
Url = "socket-factory"
},
new()
{
Text = Localizer["SocketManualReceive"],
Expand Down Expand Up @@ -234,7 +247,23 @@ void AddSocket(DemoMenuItem item)
Url = "socket/data-entity"
}
};
AddBadge(item, count: 1);

AddBadge(item, count: 2);
}

void AddModbus(DemoMenuItem item)
{
item.Items = new List<DemoMenuItem>
{
new()
{
IsNew = true,
Text = Localizer["ModbusFactory"],
Url = "modbus-factory"
}
};

AddBadge(item, count: 2);
}

void AddQuickStar(DemoMenuItem item)
Expand Down Expand Up @@ -1641,12 +1670,6 @@ void AddServices(DemoMenuItem item)
Url = "print-service"
},
new()
{
IsNew = true,
Text = Localizer["TcpSocketFactory"],
Url = "socket-factory"
},
new()
{
Text = Localizer["ThemeProvider"],
Url = "theme-provider"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ public static IServiceCollection AddBootstrapBlazorServerService(this IServiceCo
services.AddMockOpcDaServer();
}

// 增加 ITcpSocketFactory 服务
services.AddTcpSocketFactory();

// 增加 IModbusFactory 服务
services.AddModbusFactory();

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,6 @@ public static IServiceCollection AddBootstrapBlazorServices(this IServiceCollect
// 增加 JuHe 定位服务
services.AddBootstrapBlazorJuHeIpLocatorService();

// 增加 ITcpSocketFactory 服务
services.AddTcpSocketFactory();

// 增加 PetaPoco ORM 数据服务操作类
// 需要时打开下面代码
//services.AddPetaPoco(option =>
Expand Down
4 changes: 3 additions & 1 deletion src/BootstrapBlazor.Server/Locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -4855,12 +4855,14 @@
"Vditor": "Vditor Markdown",
"TcpSocketFactory": "ITcpSocketFactory",
"OfficeViewer": "Office Viewer",
"SocketComponents": "ITcpSocketFactory",
"SocketComponents": "Socket Services",
"SocketAutoReceive": "Auto Receive",
"SocketManualReceive": "Manual Receive",
"DataPackageAdapter": "DataPackageAdapter",
"SocketAutoConnect": "Reconnect",
"SocketDataEntity": "DataEntity",
"ModbusComponents": "Modbus Services",
"ModbusFactory": "IModbusFactory",
"NetworkMonitor": "Network Monitor",
"Toolbar": "Toolbar",
"OpcDaService": "OpcDaServer",
Expand Down
5 changes: 3 additions & 2 deletions src/BootstrapBlazor.Server/Locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -2644,7 +2644,6 @@
"SkipValidateDescription": "在某些情况下表单中有些列的值可能是二级分类等等,需要知道一级分类的信息,这个时候一级分类需要额外的组件来呈现,如果 <code>Select</code>,而这个组件是与当前上下文绑定模型 <code>Model</code> 无关的,这种需求中通过设置 <code>SkipValidate</code> 值为 <code>true</code>,关闭此组件的模型验证功能即可",
"IsDisplayTitle": "只读表单",
"IsDisplayIntro": "通过设置 <code>IsDisplay=\"true\"</code> 使整个表单不可编辑"

},
"BootstrapBlazor.Server.Components.Samples.FloatingLabels": {
"FloatingLabelsTitle": "FloatingLabel 输入框",
Expand Down Expand Up @@ -4856,12 +4855,14 @@
"Vditor": "富文本框 Vditor Markdown",
"TcpSocketFactory": "套接字服务 ITcpSocketFactory",
"OfficeViewer": "Office 文档预览组件",
"SocketComponents": "Socket 服务",
"SocketComponents": "套接字服务 Socket",
"SocketAutoReceive": "自动接收数据",
"SocketManualReceive": "手动接收数据",
"DataPackageAdapter": "数据适配器",
"SocketAutoConnect": "自动重连",
"SocketDataEntity": "通讯数据转实体类",
"ModbusComponents": "串行通信协议 Modbus",
"ModbusFactory": "通讯服务 IModbusFactory",
"NetworkMonitor": "网络状态 NetworkMonitor",
"Toolbar": "工具栏 Toolbar",
"OpcDaService": "OpcDaServer 服务",
Expand Down
Loading