A:增加订阅类型选择

This commit is contained in:
jimmy committed 2025-12-10 14:10:13 +08:00
1 parent 6ed0d3f1c2
commit de1a29c522
4 files changed
+144 -20

No files matched your search

+1
View File
@@ -73,6 +73,7 @@
<GroupBox Header="实时监控 (Add Watch)" Grid.Row="2" Margin="0,0,0,15"> <GroupBox Header="实时监控 (Add Watch)" Grid.Row="2" Margin="0,0,0,15">
<StackPanel Orientation="Horizontal" Margin="10"> <StackPanel Orientation="Horizontal" Margin="10">
<TextBox hc:InfoElement.Placeholder="地址 (如 DB1.DBW0)" Text="{Binding MonitorAddress}" Width="200" Margin="0,0,10,0"/> <TextBox hc:InfoElement.Placeholder="地址 (如 DB1.DBW0)" Text="{Binding MonitorAddress}" Width="200" Margin="0,0,10,0"/>
<ComboBox ItemsSource="{Binding SupportedMonitorTypes}" SelectedItem="{Binding SelectedMonitorType}" Width="120" Margin="0,0,10,0" DisplayMemberPath="Name"/>
<Button Content="添加监控" Command="{Binding AddMonitorCommand}"/> <Button Content="添加监控" Command="{Binding AddMonitorCommand}"/>
</StackPanel> </StackPanel>
</GroupBox> </GroupBox>
@@ -0,0 +1,30 @@
using Plc.Models;
using Sharp.Wpf.Mvvm;
using System;
namespace Client.ViewModels;
public class PlcTagViewModel : ViewModelBase
{
private readonly PlcTag _tag;
public PlcTagViewModel(PlcTag tag)
{
_tag = tag ?? throw new ArgumentNullException(nameof(tag));
}
public string Id => _tag.Id;
public string Address => _tag.Address;
public Type DataType => _tag.DataType;
public object? Value => _tag.Value;
public PlcTag Tag => _tag;
public void UpdateValue()
{
OnPropertyChanged(nameof(Value));
}
}
+22 -1
View File
@@ -31,6 +31,7 @@ public class PlcViewModel : ViewModelBase
// 监控 // 监控
private string _monitorAddress = "DB1.DBW0"; private string _monitorAddress = "DB1.DBW0";
private Type _selectedMonitorType = typeof(short);
public PlcViewModel( public PlcViewModel(
IPlcService plcService, IPlcService plcService,
@@ -116,10 +117,30 @@ public class PlcViewModel : ViewModelBase
get => _monitorAddress; get => _monitorAddress;
set => SetProperty(ref _monitorAddress, value); set => SetProperty(ref _monitorAddress, value);
} }
public Type SelectedMonitorType
{
get => _selectedMonitorType;
set => SetProperty(ref _selectedMonitorType, value);
}
// 数据源 // 数据源
public ObservableCollection<PlcBrand> Brands { get; } = new(Enum.GetValues<PlcBrand>()); public ObservableCollection<PlcBrand> Brands { get; } = new(Enum.GetValues<PlcBrand>());
public ObservableCollection<CpuType> CpuTypes { get; } = new(Enum.GetValues<CpuType>()); public ObservableCollection<CpuType> CpuTypes { get; } = new(Enum.GetValues<CpuType>());
public ObservableCollection<Type> SupportedMonitorTypes { get; } = new()
{
typeof(bool),
typeof(byte),
typeof(short),
typeof(ushort),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(float),
typeof(double),
typeof(string)
};
public ObservableCollection<PlcTagViewModel> MonitoredTags { get; } = new(); public ObservableCollection<PlcTagViewModel> MonitoredTags { get; } = new();
#endregion #endregion
@@ -206,7 +227,7 @@ public class PlcViewModel : ViewModelBase
var tag = new PlcTag var tag = new PlcTag
{ {
Address = MonitorAddress, Address = MonitorAddress,
DataType = typeof(short) DataType = SelectedMonitorType
}; };
var vm = new PlcTagViewModel(tag); var vm = new PlcTagViewModel(tag);
+91 -19
View File
@@ -189,6 +189,96 @@ _plcService.Subscribe(tag);
--- ---
## ⚙️ 核心通信优化机制
本项目采用了**分桶(Batching/Bucketing)**优化策略,这是高性能 PLC 通信的关键。
### 1. 为什么需要优化?
PLC 通信通常基于 TCP/IP 协议,每次读写请求都包含网络握手、协议头封装和 PLC 内部处理时间。如果用户订阅了 100 个离散点位,如果发送 100 次网络请求,延迟将极高(例如 100 * 20ms = 2000ms),无法满足实时监控需求。
### 2. 优化原理
系统通过 `IPlcOptimizer`(如 `SiemensOptimizer`)在内存中动态重组读写请求,将多个小的、地址相邻的请求合并为一个大的网络请求。
#### 处理流程(伪代码)
```mermaid
graph LR
A[原始订阅Tags] --> B[1. 分组 Grouping]
B --> C[2. 排序 Sorting]
C --> D[3. 合并 Merging]
D --> E[执行网络请求]
```
1. **分组 (Grouping)**: 根据内存区域(如 DB1, DB2, M区, I区)将 Tag 分组。不同区域无法合并。
2. **排序 (Sorting)**: 在同一组内,按字节偏移量(Byte Offset)从小到大排序。
3. **合并 (Merging)**:
* 遍历排序后的 Tags。
* 计算两个 Tag 之间的**间隙 (Gap)**。
* 如果 `Gap <= MaxGapBytes` (默认20字节) 且 `TotalLength <= MaxPduSize` (最大包长),则合并。
* 否则,创建一个新的**批次 (Batch)**。
### 3. 实际案例分析
假设用户订阅了以下点位(西门子 S7):
- Tag A: `DB1.DBW0` (2 bytes)
- Tag B: `DB1.DBW4` (2 bytes)
- Tag C: `DB1.DBW1000` (2 bytes)
**处理逻辑:**
1. **Tag A 和 Tag B**:
* Tag A 结束于 byte 2,Tag B 开始于 byte 4。
* 间隙 Gap = 4 - 2 = 2 bytes。
* 2 bytes < 20 bytes (阈值),且总长未超 PDU。
* **结果**: 合并为一个请求:`Start: 0, Length: 6`。虽然读回了无用的 byte 2-3,但节省了一次网络请求。
2. **Tag B 和 Tag C**:
* Tag B 结束于 byte 6,Tag C 开始于 byte 1000。
* 间隙 Gap = 994 bytes。
* 994 bytes > 20 bytes。
* **结果**: 断开,Tag C 放入新的批次。
**最终网络请求**: 2 次(而不是 3 次)。
### 4. 极限情况处理
* **只订阅 1 个点位**: 优化器生成 1 个批次,包含 1 个 Tag。无额外开销。
* **连续订阅 1000 个点位 (例如 DB1.DBW0 - DB1.DBW2000)**:
* 总长度 2000 字节。
* 假设 PLC PDU 限制为 960 字节。
* 优化器会自动切分为 3 个批次:
* Batch 1: 0 - 960
* Batch 2: 960 - 1920
* Batch 3: 1920 - 2000
* 系统自动处理分包,上层业务无感。
### 5. 未来扩展性 (适配欧姆龙/三菱)
本架构设计之初就考虑了多品牌支持。适配新品牌只需实现以下接口,**无需修改 Client/ViewModel 代码**:
1. **实现 `IPlcDriver`**: 处理底层的 Socket/串口 通信(如 FINS, MC 协议)。
2. **实现 `IPlcAddress`**: 定义新品牌的地址格式(如欧姆龙的 `D100`, `W0.00`)。
3. **实现 `IPlcOptimizer`**: 针对新协议调整优化参数。
#### 扩展示例(伪代码)
```csharp
// 欧姆龙 FINS 协议优化器示例
public class OmronOptimizer : IPlcOptimizer
{
// 欧姆龙按 Word (2byte) 寻址,且单次读取限制不同
private const int MaxWordCount = 2000;
public List<PlcReadBatch> Optimize(IEnumerable<PlcTag> tags, int maxPduSize)
{
// 1. 按内存区 (DM, CIO, WR) 分组
// 2. 按 Word 地址排序
// 3. 合并逻辑:如果两个地址相差 < 10 个 Words,则合并读取
// 4. 返回适用于 FINS 协议的 ReadRequest 列表
}
}
```
---
## 🎯 高级功能 ## 🎯 高级功能
### 1. 标签订阅与监控 ### 1. 标签订阅与监控
@@ -211,24 +301,6 @@ foreach (var tag in tags)
await _plcService.StartAsync(); await _plcService.StartAsync();
``` ```
### 2. 批量优化原理
优化器会自动:
1. **按区域分组** - DB1、M、I、Q 等分开处理
2. **按地址排序** - 确保连续性
3. **间隙合并** - 小于 `MaxGapBytes` 的间隙合并为一次读取
4. **PDU限制** - 单次读取不超过 `MaxPduSize`
**示例:**
```
标签1: DB1.DBW0 (2 bytes)
标签2: DB1.DBW4 (2 bytes) ← 间隙 2 bytes
标签3: DB1.DBW8 (2 bytes) ← 间隙 2 bytes
优化后: 读取 DB1.DBW0, 10 bytes
包含所有三个标签和中间的间隙
```
### 3. 使用特性绑定 ### 3. 使用特性绑定
```csharp ```csharp
@@ -555,4 +627,4 @@ MIT License - 详见 [LICENSE](LICENSE) 文件
- 项目主页: [GitHub](https://github.com/yourcompany/plc) - 项目主页: [GitHub](https://github.com/yourcompany/plc)
- 问题反馈: [Issues](https://github.com/yourcompany/plc/issues) - 问题反馈: [Issues](https://github.com/yourcompany/plc/issues)
- 文档: [Wiki](https://github.com/yourcompany/plc/wiki) - 文档: [Wiki](https://github.com/yourcompany/plc/wiki)