mirror of
https://github.com/RRQM/TouchSocket.git
synced 2025-12-19 18:06:45 +08:00
docs(plcbridge): 更新 PLC 桥接服务文档
在 `plcbridgedescription.mdx` 中添加 PLC 桥接服务的详细说明,包括必要性、痛点分析、解决方案及独特价值等内容。更新 `plcbridgeservice.mdx`,提供 TouchSocketPro.PlcBridges 的使用说明和示例。修改 `sidebars.ts`,新增 PLC Bridge 组件文档链接,提升用户访问便利性
This commit is contained in:
266
handbook/docs/plcbridgedescription.mdx
Normal file
266
handbook/docs/plcbridgedescription.mdx
Normal file
@@ -0,0 +1,266 @@
|
||||
---
|
||||
id: plcbridgedescription
|
||||
title: Plc 桥接服务说明
|
||||
---
|
||||
|
||||
## 一、为什么你需要 PLC Bridge?解决工业自动化中的核心痛点
|
||||
|
||||
在工业自动化开发中,直接读写 PLC 看起来是最直接的方式,但随着系统复杂度增加,这种方式会带来一系列严重问题。TouchSocketPro.PlcBridges 正是为解决这些核心痛点而生。
|
||||
|
||||
## 二、直接读写 PLC 的五大痛点
|
||||
|
||||
### 2.1 多设备协同的复杂性噩梦
|
||||
|
||||
当系统需要连接多个 PLC 设备时:
|
||||
```csharp
|
||||
// 伪代码示例:直接连接多个 PLC
|
||||
var plc1 = new SiemensPLC("192.168.1.10");
|
||||
var plc2 = new OmronPLC("192.168.1.11");
|
||||
var plc3 = new ModbusPLC("192.168.1.12");
|
||||
|
||||
// 写入数据需要分别处理每个设备
|
||||
await plc1.WriteRegister(0, value1);
|
||||
await plc2.WriteRegister(5, value2);
|
||||
await plc3.WriteCoil(10, true);
|
||||
```
|
||||
|
||||
**痛点分析**:
|
||||
- 需要了解每个 PLC 的特定协议和地址映射
|
||||
- 错误处理逻辑重复且复杂
|
||||
- 设备间的数据依赖难以管理
|
||||
|
||||
### 2.2 性能瓶颈:高频读写效率低下
|
||||
|
||||
当需要频繁读写时:
|
||||
```csharp
|
||||
// 伪代码示例:直接读写大量数据点
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
var value = await plc.ReadRegister(i);
|
||||
ProcessData(value);
|
||||
}
|
||||
```
|
||||
|
||||
**痛点分析**:
|
||||
- 每次读写都需要建立/断开连接
|
||||
- 小数据包导致网络带宽浪费
|
||||
- 无法合并相邻地址的读写请求
|
||||
|
||||
### 2.3 数据类型转换的繁琐工作
|
||||
|
||||
处理不同数据类型时:
|
||||
|
||||
```csharp
|
||||
// 伪代码示例:手动处理数据类型转换
|
||||
var bytes = await plc.ReadBytes(0, 4);
|
||||
float temperature = BitConverter.ToSingle(bytes, 0);
|
||||
|
||||
var intBytes = await plc.ReadBytes(4, 2);
|
||||
int pressure = BitConverter.ToInt16(intBytes, 0);
|
||||
```
|
||||
|
||||
**痛点分析**:
|
||||
- 需要手动处理字节序(大端/小端)
|
||||
- 复杂类型(浮点数、结构体)转换代码冗长
|
||||
- 容易因类型处理错误导致数据错误
|
||||
|
||||
### 2.4 系统扩展的困难
|
||||
|
||||
当需要添加新设备时:
|
||||
|
||||
```csharp
|
||||
// 伪代码示例:添加新设备需要重构代码
|
||||
// 原有代码
|
||||
if (deviceType == "Siemens")
|
||||
{
|
||||
// Siemens 特定逻辑
|
||||
}
|
||||
else if (deviceType == "Omron")
|
||||
{
|
||||
// Omron 特定逻辑
|
||||
}
|
||||
|
||||
// 添加 Modbus 支持
|
||||
else if (deviceType == "Modbus")
|
||||
{
|
||||
// 新增 Modbus 逻辑
|
||||
}
|
||||
```
|
||||
|
||||
**痛点分析**:
|
||||
- 每次添加新设备类型都需要修改核心逻辑
|
||||
- 系统变得臃肿难以维护
|
||||
- 测试覆盖率难以保证
|
||||
|
||||
### 2.5 实时性难以保障
|
||||
|
||||
```csharp
|
||||
// 伪代码示例:直接读写无法保证实时性
|
||||
var task1 = ReadSensorData();
|
||||
var task2 = WriteControlSignal();
|
||||
|
||||
await Task.WhenAll(task1, task2); // 无法控制执行顺序
|
||||
```
|
||||
|
||||
**痛点分析**:
|
||||
- 无法保证关键指令的执行顺序
|
||||
- 缺乏优先级管理机制
|
||||
- 并发操作可能导致资源冲突
|
||||
|
||||
## 三、PLC Bridge如何解决这些痛点
|
||||
|
||||
### 3.1 统一访问接口(解决多设备协同问题)
|
||||
|
||||
```csharp
|
||||
// 使用 PLC Bridge 统一访问不同设备
|
||||
var plcOperator = plcBridge.CreateOperator<short>();
|
||||
|
||||
// 无论底层是什么PLC设备,使用相同API
|
||||
await plcOperator.WriteAsync(new WritableValue<short>(0, 100));
|
||||
var result = await plcOperator.ReadAsync(new ReadableValue<short>(0, 1));
|
||||
```
|
||||
|
||||
**优势**:
|
||||
- 统一接口简化开发
|
||||
- 设备更换无需修改业务代码
|
||||
- 支持热插拔添加/移除设备
|
||||
|
||||
### 3.2 智能请求合并(解决性能瓶颈)
|
||||
|
||||
```csharp
|
||||
// PLC Bridge 自动合并相邻请求
|
||||
var writableValues = new WritableValueCollection<short>(
|
||||
new WritableValue<short>(0, 100), // 地址0
|
||||
new WritableValue<short>(1, 200), // 地址1(相邻)
|
||||
new WritableValue<short>(5, 300) // 地址5(间隔=4)
|
||||
);
|
||||
|
||||
// 实际只产生1次通信(地址0-5批量写入)
|
||||
await plcOperator.WriteAsync(writableValues);
|
||||
```
|
||||
|
||||
**优势**:
|
||||
- 减少70-90%的通信次数
|
||||
- 最大程度利用网络带宽
|
||||
- 通过 MaxGap 参数精细控制合并策略
|
||||
|
||||
### 3.3. 自动类型转换(解决数据类型问题)
|
||||
|
||||
```csharp
|
||||
// PLC Bridge 自动处理类型转换
|
||||
float[] temperatures = {23.5f, 24.1f, 22.8f};
|
||||
|
||||
// 自动转换为字节流写入
|
||||
await plcOperator.WriteAsync(new WritableValue<float>(0, temperatures));
|
||||
|
||||
// 自动转换回原始类型
|
||||
var result = await plcOperator.ReadAsync(new ReadableValue<float>(0, 3));
|
||||
```
|
||||
|
||||
**优势**:
|
||||
- 支持所有非托管类型
|
||||
- 自动处理字节序转换
|
||||
- 复杂结构体一键序列化
|
||||
|
||||
### 3.4 设备抽象层(解决扩展性问题)
|
||||
|
||||
```csharp
|
||||
// 添加新PLC设备只需实现驱动器接口
|
||||
public class CustomPlcDrive : IPlcDrive<short>
|
||||
{
|
||||
// 实现驱动器接口
|
||||
}
|
||||
|
||||
// 注册到PLC Bridge
|
||||
plcBridge.AddDriveAsync(new CustomPlcDrive(/* 配置 */));
|
||||
```
|
||||
|
||||
**优势**:
|
||||
- 新设备支持不影响业务逻辑
|
||||
- 插件式架构便于扩展
|
||||
- 核心系统保持稳定
|
||||
|
||||
### 3.5 执行控制(解决实时性问题)
|
||||
|
||||
```csharp
|
||||
// 通过分组控制执行顺序
|
||||
var criticalDrive = new MemoryPlcDrive<short>(
|
||||
new PlcDriveOption { Group = "Critical" });
|
||||
|
||||
var normalDrive = new MemoryPlcDrive<short>(
|
||||
new PlcDriveOption { Group = "Normal" });
|
||||
|
||||
// Critical组任务优先执行且串行处理
|
||||
```
|
||||
|
||||
**优势**:
|
||||
- 分组控制关键任务执行顺序
|
||||
- 支持任务优先级管理
|
||||
- 内置超时和重试机制
|
||||
|
||||
## 四、PLC Bridge 的独特价值
|
||||
|
||||
### 4.1 架构优化前后对比
|
||||
|
||||
| **场景** | **直接读写 PLC** | **使用 PLC Bridge** |
|
||||
|----------|------------------|---------------------|
|
||||
| 多设备协同 | 每个设备独立处理 | 统一接口管理所有设备 |
|
||||
| 高频数据采集 | 频繁小包通信,性能低下 | 智能合并请求,减少70%+通信量 |
|
||||
| 系统扩展 | 修改核心代码,风险高 | 添加驱动器,业务零修改 |
|
||||
| 实时控制 | 无执行顺序保障 | 分组控制关键任务 |
|
||||
| 数据类型处理 | 手动转换,易出错 | 自动处理所有类型转换 |
|
||||
| 错误处理 | 分散在各处 | 统一结果对象(Result) |
|
||||
|
||||
### 4.2 PLC Bridge 的核心价值矩阵
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[PLC Bridge 价值] --> B(开发效率提升)
|
||||
A --> C(系统性能优化)
|
||||
A --> D(维护成本降低)
|
||||
A --> E(系统可靠性增强)
|
||||
|
||||
B --> B1(减少70%+代码量)
|
||||
B --> B2(统一访问接口)
|
||||
B --> B3(快速集成新设备)
|
||||
|
||||
C --> C1(请求合并优化)
|
||||
C --> C2(内存池技术)
|
||||
C --> C3(异步批量处理)
|
||||
|
||||
D --> D1(核心业务与设备解耦)
|
||||
D --> D2(配置驱动扩展)
|
||||
D --> D3(集中错误处理)
|
||||
|
||||
E --> E1(执行顺序保障)
|
||||
E --> E2(自动重试机制)
|
||||
E --> E3(数据一致性保护)
|
||||
```
|
||||
|
||||
## 五、何时需要 PLC Bridge?
|
||||
|
||||
### 5.1 适用场景
|
||||
1. **多PLC协同系统**:连接多种品牌/协议的PLC设备
|
||||
2. **高频数据采集**:需要优化通信性能的场景
|
||||
3. **大型SCADA系统**:需要统一设备管理接口
|
||||
4. **关键过程控制**:需要保障执行顺序和实时性
|
||||
5. **快速迭代项目**:需要灵活扩展设备支持
|
||||
|
||||
### 5.2 使用建议
|
||||
- **简单系统**(单PLC,少量读写):直接读写可能更简单
|
||||
- **复杂系统**(多设备,高频读写):PLC Bridge 是必备架构组件
|
||||
- **关键任务系统**:PLC Bridge 提供必需的可靠性和实时性保障
|
||||
|
||||
## 六、结论
|
||||
|
||||
PLC Bridge 不是简单的通信封装,而是工业自动化领域的**架构解决方案**。它解决了直接读写 PLC 方式在复杂系统中暴露的核心痛点:
|
||||
|
||||
1. 通过**统一接口**消除多设备协同复杂度
|
||||
2. 通过**智能合并**优化高频读写性能
|
||||
3. 通过**自动转换**简化数据类型处理
|
||||
4. 通过**抽象层**实现无缝系统扩展
|
||||
5. 通过**执行控制**保障关键任务实时性
|
||||
|
||||
在工业4.0和IIoT时代,随着系统复杂度不断增加,PLC Bridge 已成为构建可靠、高效、可扩展工业自动化系统的**必备基础设施**。
|
||||
|
||||
|
||||
246
handbook/docs/plcbridgeservice.mdx
Normal file
246
handbook/docs/plcbridgeservice.mdx
Normal file
@@ -0,0 +1,246 @@
|
||||
---
|
||||
id: plcbridgeservice
|
||||
title: 历史更新
|
||||
---
|
||||
|
||||
import Pro from "@site/src/components/Pro.js";
|
||||
|
||||
### 定义
|
||||
|
||||
命名空间:TouchSocketPro.PlcBridges <br/>
|
||||
程序集:[TouchSocketPro.PlcBridges.dll](https://www.nuget.org/packages/TouchSocketPro.PlcBridges)
|
||||
|
||||
# TouchSocketPro.PlcBridges 使用说明
|
||||
|
||||
## 一、说明 <Pro/>
|
||||
`TouchSocketPro.PlcBridges` 是一个高效、灵活的PLC数据桥接库,专为.NET开发者设计,用于简化与可编程逻辑控制器(PLC)的数据交互流程。该库提供了强大的数据读写管理能力,支持多种PLC协议和数据类型,使工业自动化开发更加便捷高效。
|
||||
|
||||
|
||||
|
||||
## 二、支持的目标框架
|
||||
|
||||
- .NET Framework 4.5+
|
||||
- .NET Standard 2.0+
|
||||
- .NET Core 3.1+
|
||||
- .NET 5/6/8/9
|
||||
|
||||
|
||||
## 三、快速开始
|
||||
|
||||
### 3.1 安装NuGet包
|
||||
|
||||
```bash
|
||||
Install-Package TouchSocketPro.PlcBridges
|
||||
```
|
||||
|
||||
### 3.2 基本使用示例
|
||||
|
||||
```csharp
|
||||
using TouchSocket.PlcBridges;
|
||||
|
||||
// 1. 初始化PLC桥接服务
|
||||
var plcBridge = new PlcBridgeService();
|
||||
|
||||
// 2. 简单配置
|
||||
await plcBridge.SetupAsync(new TouchSocketConfig());
|
||||
|
||||
// 3. 添加内存PLC驱动器
|
||||
await plcBridge.AddDriveAsync(new MemoryPlcDrive<short>(
|
||||
new PlcDriveOption()
|
||||
{
|
||||
Name = "DeviceA",
|
||||
Start = 0,
|
||||
Count = 10
|
||||
}));
|
||||
|
||||
// 4. 启动PLC桥接服务
|
||||
await plcBridge.StartAsync();
|
||||
|
||||
// 5. 创建操作器
|
||||
var plcOperator = plcBridge.CreateOperator<short>();
|
||||
|
||||
// 6. 写入数据
|
||||
var writeResult = await plcOperator.WriteAsync(
|
||||
new WritableValueCollection<short>(
|
||||
new WritableValue<short>(0, new short[] {1,2,3,4,5})
|
||||
));
|
||||
|
||||
// 7. 读取数据
|
||||
var readableValues = new ReadableValueCollection<short>(
|
||||
new ReadableValue<short>(0, 5));
|
||||
var readResult = await plcOperator.ReadAsync(readableValues);
|
||||
|
||||
// 8. 停止服务
|
||||
await plcBridge.StopAsync();
|
||||
```
|
||||
|
||||
## 四、配置详解
|
||||
|
||||
### 4.1 PlcDriveOption 配置属性
|
||||
|
||||
| 属性 | 类型 | 说明 | 默认值 |
|
||||
|------|------|------|--------|
|
||||
| `Name` | `string` | 驱动器名称标识符,用于唯一标识当前驱动实例 | `null` |
|
||||
| `Start` | `int` | 映射到PLC桥接服务的**起始地址偏移量** | 必须自定义赋值 |
|
||||
| `Count` | `int` | 映射到PLC桥接服务的**数据单元数量** | 必须自定义赋值 |
|
||||
| `EndianType` | `EndianType` | PLC数据的**字节序类型**(Big-Endian) | `EndianType.Big` |
|
||||
| `MaxReadGap` | `int` | 读取地址范围间的最大间隙阈值(单位:地址偏移量):<br/>- 当连续地址块之间的间隙 ≤ 该值时,会被合并为一次性读取操作<br/>- 示例:地址块 `0-1`, `2-3`, `4-7` (间隙≤10) → 合并读取 `0-7` | `10` |
|
||||
| `MaxWriteGap` | `int` | 写入地址范围间的最大间隙阈值(默认值):<br/>- `0` 表示每次写入操作单独处理(不合并)<br/>- 若设置为有效值需配合 `WriteGapValidityWindow` | `0` |
|
||||
| `WriteGapValidityWindow` | `TimeSpan` | 写入间隙有效时间窗口(需与 `MaxWriteGap` 配合使用):<br/>- 当写入操作存在间隙(≤`MaxWriteGap`)时,若该间隙值在窗口时间内被读取过<br/>- 系统会将读取值作为补丁数据,与写入操作合并批量提交<br/>- **作用**:避免间隙地址被意外覆盖(如默认0值填充) | `TimeSpan.Zero` |
|
||||
| `Group` | `string` | 驱动器分组名称:<br/>- 相同分组名称的驱动器使用**同一个Task执行队列**(串行化执行) | `null` |
|
||||
| `DelayTime` | `TimeSpan` | 驱动器轮询延迟时间:<br/>- 值越大,批量处理合并的可能性越高(提升吞吐量)<br/>- 值过大会**降低实时性**,需根据业务场景权衡 | `TimeSpan.Zero` |
|
||||
|
||||
|
||||
### 4.2 示例配置
|
||||
|
||||
```csharp
|
||||
var driveOption = new PlcDriveOption
|
||||
{
|
||||
Name = "DeviceA",
|
||||
Start = 0,
|
||||
Count = 10,
|
||||
EndianType = EndianType.Little,
|
||||
MaxReadGap = 5,
|
||||
MaxWriteGap = 1,
|
||||
WriteGapValidityWindow = TimeSpan.FromMilliseconds(500),
|
||||
Group = "GroupA",
|
||||
DelayTime = TimeSpan.FromMilliseconds(100)
|
||||
};
|
||||
```
|
||||
|
||||
## 五、核心类与方法
|
||||
|
||||
### 5.1 PlcBridgeService 类
|
||||
|
||||
PLC桥接服务的主入口点,负责管理驱动器和操作请求。
|
||||
|
||||
#### 5.1.1 主要方法
|
||||
|
||||
| 方法 | 说明 |
|
||||
|------|------|
|
||||
| `SetupAsync(TouchSocketConfig)` | 配置服务 |
|
||||
| `AddDriveAsync(IPlcDrive)` | 添加PLC驱动器 |
|
||||
| `StartAsync()` | 启动服务 |
|
||||
| `StopAsync()` | 停止服务 |
|
||||
| `CreateOperator<T>()` | 创建数据操作器 |
|
||||
|
||||
### 5.2 IPlcOperator 接口
|
||||
|
||||
提供读写PLC数据的操作接口。
|
||||
|
||||
#### 5.2.1 主要方法
|
||||
|
||||
| 方法 | 说明 |
|
||||
|------|------|
|
||||
| `ReadAsync(ReadableValueCollection<T>)` | 读取数据 |
|
||||
| `WriteAsync(WritableValueCollection<T>)` | 写入数据 |
|
||||
|
||||
### 5.3 数据集合类
|
||||
|
||||
| 类 | 说明 |
|
||||
|------|------|
|
||||
| `ReadableValue<T>` | 定义可读值(起始地址+长度) |
|
||||
| `ReadableValueCollection<T>` | 可读值集合 |
|
||||
| `WritableValue<T>` | 定义可写值(起始地址+数据) |
|
||||
| `WritableValueCollection<T>` | 可写值集合 |
|
||||
|
||||
## 六、高级特性
|
||||
|
||||
### 6.1 请求合并优化
|
||||
|
||||
通过配置`MaxWriteGap`和`WriteGapValidityWindow`实现相邻请求的智能合并:
|
||||
|
||||
```csharp
|
||||
// 配置写入间隙为1,有效窗口500ms
|
||||
var driveOption = new PlcDriveOption
|
||||
{
|
||||
MaxWriteGap = 1,
|
||||
WriteGapValidityWindow = TimeSpan.FromMilliseconds(500)
|
||||
};
|
||||
|
||||
// 写入操作会自动合并相邻请求
|
||||
var writeResult = await plcOperator.WriteAsync(
|
||||
new WritableValueCollection<byte>(
|
||||
new WritableValue<byte>(0, new byte[] {0,1,2,3}),
|
||||
new WritableValue<byte>(5, new byte[] {5,6})
|
||||
));
|
||||
```
|
||||
|
||||
### 6.2 使用PlcObject简化访问
|
||||
|
||||
通过定义PLC对象映射简化数据访问:
|
||||
|
||||
```csharp
|
||||
partial class MyPlcObject : PlcObject
|
||||
{
|
||||
public MyPlcObject(IPlcBridgeService bridgeService)
|
||||
: base(bridgeService) { }
|
||||
|
||||
[PlcField<short>(Start = 0)]
|
||||
private short m_shortValue;
|
||||
|
||||
[PlcField<short>(Start = 1, Quantity = 3)]
|
||||
private ReadOnlyMemory<short> m_shortValues;
|
||||
}
|
||||
|
||||
// 使用
|
||||
var myPlcObject = new MyPlcObject(plcBridge);
|
||||
var resultSet = await myPlcObject.SetShortValueAsync(1);
|
||||
var resultGet = await myPlcObject.GetShortValueAsync();
|
||||
```
|
||||
|
||||
### 6.3 多驱动器协同工作
|
||||
|
||||
|
||||
支持多个驱动器协同工作,自动处理地址映射:
|
||||
|
||||
```csharp
|
||||
// 添加两个驱动器
|
||||
var memoryPlcDrive_1 = new MemoryPlcDrive<short>(
|
||||
new PlcDriveOption() { Start = 0, Count = 5 });
|
||||
var memoryPlcDrive_2 = new MemoryPlcDrive<short>(
|
||||
new PlcDriveOption() { Start = 5, Count = 5 });
|
||||
|
||||
plcBridge.AddDriveAsync(memoryPlcDrive_1);
|
||||
plcBridge.AddDriveAsync(memoryPlcDrive_2);
|
||||
|
||||
// 写入跨越两个驱动器的数据
|
||||
var writeResult = await plcOperator.WriteAsync(
|
||||
new WritableValueCollection<short>(
|
||||
new WritableValue<short>(0, new short[] {0,1,2,3,4,5,6,7,8,9})
|
||||
));
|
||||
```
|
||||
|
||||
## 七、性能优化建议
|
||||
|
||||
1. **合理设置MaxGap参数**:
|
||||
|
||||
```csharp
|
||||
// 增大间隙值可提高合并率
|
||||
driveOption.MaxReadGap = 20;
|
||||
driveOption.MaxWriteGap = 5;
|
||||
```
|
||||
|
||||
2. **使用分组控制执行顺序**:
|
||||
|
||||
```csharp
|
||||
// 相同分组的驱动器串行执行
|
||||
driveOption.Group = "CriticalGroup";
|
||||
```
|
||||
|
||||
3. **调整延迟时间平衡实时性与性能**:
|
||||
|
||||
```csharp
|
||||
// 适当增加延迟时间提高合并率
|
||||
driveOption.DelayTime = TimeSpan.FromMilliseconds(100);
|
||||
```
|
||||
|
||||
4. **利用内存池减少分配开销**:
|
||||
|
||||
```csharp
|
||||
// 使用ByteBlock减少内存分配
|
||||
using (var byteBlock = new ByteBlock(1024))
|
||||
{
|
||||
// 处理数据
|
||||
}
|
||||
```
|
||||
@@ -623,6 +623,22 @@ module.exports =
|
||||
"id": "mqttclient"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "category",
|
||||
"label": "23、PlcBridge组件",
|
||||
"items": [
|
||||
{
|
||||
"type": "doc",
|
||||
"label": "23.1 PlcBridge说明",
|
||||
"id": "plcbridgedescription"
|
||||
},
|
||||
{
|
||||
"type": "doc",
|
||||
"label": "23.2 PlcBridge服务",
|
||||
"id": "plcbridgeservice"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user