Files
StandardSence/StandardScene.Core/Docs/DoorMission使用手册.md
T

635 lines
20 KiB
Markdown
Raw Normal View History

2026-06-14 11:19:15 +08:00
# DoorMission 使用手册
## 目录
- [概述](#概述)
- [基本功能](#基本功能)
- [启动与停止](#启动与停止)
- [配置管理](#配置管理)
- [门控逻辑](#门控逻辑)
- [站点配置](#站点配置)
- [UI界面](#ui界面)
- [参数说明](#参数说明)
- [故障排查](#故障排查)
- [附录](#附录)
---
## 概述
`DoorMission` 是一个门控进程类,用于管理多个门控制器及其关联的门。它提供了以下核心功能:
- 多门控制器管理(支持不同类型)
- 自动门控逻辑(根据小车位置自动开关门)
- 手动控制功能(支持临时手动控制,优先级高于自动控制)
- 车辆占用管理(跟踪和清空门区域的车辆占用)
- 配置文件动态监控(支持热更新)
- 线程安全的门状态读写
- 可视化的配置和监控界面
### 架构特点
- **抽象化设计**:门控制器通过抽象基类 `BasicDoorController` 实现,支持扩展不同类型的控制器
- **线程安全**:门状态读取和控制写入分离,所有通信操作在门控制器内部线程完成
- **事件驱动**:与交通控制系统集成,响应小车进入/离开站点事件
- **热更新支持**:配置文件每10秒自动检查更新,无需重启任务
- **控制仲裁机制**:手动控制优先级高于自动控制,手动控制过期后自动恢复自动模式
---
## 基本功能
### 1. 门控制器管理
#### 1.1 门控制器类型
门控制器通过 `DoorTypeAttribute` 标记类型,系统会自动识别并创建实例。当前支持:
- **ModbusDoorController**:基于 Modbus TCP 的门控制器
#### 1.2 门控制器配置
每个门控制器包含以下配置:
- **Index**:控制器索引(唯一标识)
- **Ip**IP地址
- **Port**:端口号(默认502
- **Type**:控制器类型(通过 `DoorTypeAttribute.Name` 指定)
- **Doors**:门列表
#### 1.3 门配置
每个门包含以下配置:
- **Index**:门索引(在控制器内唯一)
- **ControlAddress**:开关控制信号地址(Modbus 线圈地址)
- **OpenStatusAddress**:开到位信号地址(Modbus 离散输入地址)
### 2. 自动门控逻辑
#### 2.1 门开启条件
门会在以下情况自动开启:
1. **小车即将进入区域**:当小车到达站点且站点的 `EnterDoor` 字段匹配时,门会在锁定前开启
2. **小车在区域内**:当小车已进入并锁定站点时,门保持开启状态
#### 2.2 门关闭条件
门会在以下情况自动关闭:
- 小车离开区域后,门自动关闭
#### 2.3 门标识符格式
站点配置中的门标识符格式为:**控制器索引.门索引**
**示例**
```
"EnterDoor": "1.2" // 表示控制器索引1,门索引2
"LeaveDoor": "2.3" // 表示控制器索引2,门索引3
```
### 3. 配置文件动态监控
系统每10秒自动检查 `DoorConfig.json` 文件,并根据配置变化:
- **添加**:新增的门控制器会自动创建并连接
- **删除**:已移除的门控制器会自动断开并移除
- **修改**:已修改的门控制器会自动更新(IP、端口、类型或门配置变化)
---
## 启动与停止
### 1. 启动任务
```csharp
var doorMission = new DoorMission();
doorMission.Execute(); // 执行"启动进程"
```
**启动流程**
1. 设置数据文件路径(`DoorConfig.json`
2. 订阅交通控制事件(`BeforeLock``AfterLeave``OnLockAcquired`
3. 立即加载一次配置(避免监控界面在首次轮询前无数据)
4. 启动配置监控任务(每10秒检查一次)
5. 启动门控逻辑监控任务(每500毫秒检查一次)
### 2. 停止任务
```csharp
doorMission.Stop(); // 执行"停止进程"
```
**停止流程**
1. 取消事件订阅
2. 取消所有后台任务
3. 断开所有门控制器连接
4. 等待任务完成(最多等待5秒)
### 3. 公共方法
#### 3.1 读取门状态
```csharp
bool isOpen = doorMission.GetDoorState(controllerIndex, doorIndex);
```
- **参数**
- `controllerIndex`:门控制器索引
- `doorIndex`:门索引
- **返回值**`true`=打开,`false`=关闭
#### 3.2 设置门控制目标(自动模式)
```csharp
doorMission.SetDoorControlTarget(controllerIndex, doorIndex, open);
```
- **参数**
- `controllerIndex`:门控制器索引
- `doorIndex`:门索引
- `open``true`=打开,`false`=关闭
**注意**:此方法仅设置目标控制状态,实际通信由门控制器内部线程完成,确保线程安全。此方法会立即生效,但可能被手动控制覆盖。
#### 3.3 设置手动控制目标
```csharp
bool success = doorMission.SetManualDoorControl(controllerIndex, doorIndex, open, holdSeconds);
```
- **参数**
- `controllerIndex`:门控制器索引
- `doorIndex`:门索引
- `open``true`=打开,`false`=关闭
- `holdSeconds`:手动保持秒数(可选,默认10秒)
- **返回值**`true`=成功,`false`=失败(当有车辆占用且尝试关闭时返回false)
**功能说明**
- 手动控制优先级高于自动控制
- 手动控制会在指定时间后自动过期,恢复自动模式
- **安全保护**:当门区域内有车辆占用时,禁止手动关闭门
- 手动控制过期后,系统自动恢复自动控制逻辑
#### 3.4 清除手动控制
```csharp
doorMission.ClearManualDoorControl(controllerIndex, doorIndex);
```
- **参数**
- `controllerIndex`:门控制器索引
- `doorIndex`:门索引
**功能说明**:立即清除手动控制请求,恢复自动控制模式。
#### 3.5 清空车辆占用
```csharp
doorMission.ClearCarsInArea(controllerIndex, doorIndex);
```
- **参数**
- `controllerIndex`:门控制器索引
- `doorIndex`:门索引
**功能说明**:清空指定门的车辆占用记录。清空后,如果门处于打开状态且没有其他小车需要进入,门会自动关闭。
#### 3.6 获取门控制状态
```csharp
var status = doorMission.GetDoorControlStatus(controllerIndex, doorIndex);
```
- **参数**
- `controllerIndex`:门控制器索引
- `doorIndex`:门索引
- **返回值**`DoorControlStatus` 对象,包含:
- `Target`:当前目标状态(true=打开,false=关闭)
- `Source`:控制来源(`ControlSource.Auto``ControlSource.Manual`
- `ManualRemainingSeconds`:手动控制剩余秒数(仅当Source=Manual时有效)
- `CarsInArea`:车辆占用列表
#### 3.7 获取车辆占用情况
```csharp
var cars = doorMission.GetCarsInArea(controllerIndex, doorIndex);
```
- **参数**
- `controllerIndex`:门控制器索引
- `doorIndex`:门索引
- **返回值**:车辆ID列表(`IReadOnlyList<int>`
---
## 配置管理
### 1. 配置文件格式
配置文件 `DoorConfig.json` 位于程序根目录,格式如下:
```json
[
{
"Index": 1,
"Ip": "192.168.1.100",
"Port": 502,
"Type": "ModbusDoorController",
"Doors": [
{
"Index": 1,
"ControlAddress": 0,
"OpenStatusAddress": 0
},
{
"Index": 2,
"ControlAddress": 1,
"OpenStatusAddress": 1
}
]
},
{
"Index": 2,
"Ip": "192.168.1.101",
"Port": 502,
"Type": "ModbusDoorController",
"Doors": [
{
"Index": 1,
"ControlAddress": 0,
"OpenStatusAddress": 0
}
]
}
]
```
### 2. 配置界面
通过调用 `DoorMission.OpenViewer()` 打开门控制器管理界面,可以:
- 添加、删除、修改门控制器
- 为每个门控制器添加、删除、修改门配置
- 保存配置到 `DoorConfig.json`
**打开配置界面**
```csharp
DoorMission.OpenViewer();
```
---
## 门控逻辑
### 1. 事件响应流程
#### 1.1 BeforeLock 事件
当小车即将锁定站点时触发:
1. 检查站点的 `EnterDoor` 字段
2. 检查小车当前站点的 `PreEnterDoor` 字段是否匹配
3. 如果匹配,设置 `_needOpen[(controllerIndex, doorIndex)] = true`
4. 返回门的当前状态(如果门已打开则允许锁定)
#### 1.2 OnLockAcquired 事件
当小车成功锁定站点时触发:
1. 检查站点的 `EnterDoor` 字段
2. 检查小车当前站点的 `PreEnterDoor` 字段是否匹配
3. 如果匹配,将小车ID添加到 `carsInAreas[(controllerIndex, doorIndex)]`
4. 设置 `_needOpen[(controllerIndex, doorIndex)] = false`
#### 1.3 AfterLeave 事件
当小车离开站点时触发:
1. 检查站点的 `LeaveDoor` 字段
2. 检查小车当前站点的 `RearLeaveDoor` 字段是否匹配
3. 如果匹配,从 `carsInAreas[(controllerIndex, doorIndex)]` 中移除小车ID
### 2. 门控状态监控与仲裁
`MonitorDoorLogicAsync` 任务每500毫秒执行一次,检查每个门的控制逻辑并进行仲裁:
```csharp
// 自动目标:有车或需要打开
var needOpen = _needOpen.TryGetValue(key, out var open) && open;
var hasCarsInArea = carsInAreas.TryGetValue(key, out var cars) && cars.Count > 0;
var autoTarget = needOpen || hasCarsInArea;
// 手动请求仲裁:优先级 Manual > Auto,手动过期后自动恢复
bool finalTarget = autoTarget;
if (_manualRequests.TryGetValue(key, out var manual))
{
if (manual.ExpireAt <= DateTime.Now)
{
_manualRequests.Remove(key); // 手动控制过期,移除
}
else
{
finalTarget = manual.Target; // 手动控制有效,使用手动目标
}
}
// 设置门的目标控制状态
controller.SetDoorControlTarget(doorIndex, finalTarget);
```
**逻辑说明**
- **自动目标计算**
- 如果 `_needOpen[key] = true`,门需要打开(小车即将进入)
- 如果 `carsInAreas[key]` 中有小车,门需要保持打开(小车在区域内)
- 其他情况,自动目标为关闭
- **控制仲裁**
- 手动控制优先级高于自动控制
- 如果存在有效的手动控制请求(未过期),使用手动目标
- 手动控制过期后,自动移除并恢复自动控制
- 最终目标写入门控制器的 `DoorControlTargets` 字段
---
## 站点配置
### 1. 站点字段说明
站点需要配置以下字段以实现门控功能:
| 字段名 | 类型 | 说明 | 示例 |
|--------|------|------|------|
| `EnterDoor` | string | 进站门标识符(格式:控制器索引.门索引) | `"1.2"` |
| `LeaveDoor` | string | 离站门标识符(格式:控制器索引.门索引) | `"2.3"` |
### 2. 小车站点字段说明
小车当前站点需要配置以下字段:
| 字段名 | 类型 | 说明 | 示例 |
|--------|------|------|------|
| `PreEnterDoor` | string | 前方进站门标识符(与目标站点的 `EnterDoor` 匹配) | `"1.2"` |
| `RearLeaveDoor` | string | 后方离站门标识符(与目标站点的 `LeaveDoor` 匹配) | `"2.3"` |
### 3. 配置示例
**站点配置**
```json
{
"id": 100,
"name": "站点A",
"fields": {
"EnterDoor": "1.2",
"LeaveDoor": "2.3"
}
}
```
**小车站点配置**
```json
{
"id": 50,
"name": "小车当前位置",
"fields": {
"PreEnterDoor": "1.2",
"RearLeaveDoor": "2.3"
}
}
```
**工作流程**
1. 小车从站点50驶向站点100
2. 到达站点100时,触发 `BeforeLock` 事件
3. 系统检查站点100的 `EnterDoor``"1.2"`)是否与站点50的 `PreEnterDoor``"1.2"`)匹配
4. 如果匹配,设置控制器1的门2为打开状态
5. 门打开后,小车锁定站点100
6. 触发 `OnLockAcquired` 事件,门保持打开状态
7. 小车离开站点100时,触发 `AfterLeave` 事件
8. 系统检查站点100的 `LeaveDoor``"2.3"`)是否与站点50的 `RearLeaveDoor``"2.3"`)匹配
9. 如果匹配,从区域内小车列表中移除该小车
10. 如果没有其他小车在区域内,门自动关闭
---
## UI界面
### 1. 配置管理界面
**打开方式**
```csharp
DoorMission.OpenViewer();
```
**功能**
- 门控制器列表管理(添加、删除、修改)
- 门列表管理(为每个控制器添加、删除、修改门)
- 类型选择(自动识别所有带 `DoorTypeAttribute` 的控制器类型)
- 配置保存到 `DoorConfig.json`
### 2. 监控界面
**打开方式**
```csharp
DoorMission.OpenMonitor();
```
**功能**
- 实时显示所有门的状态
- 显示控制器索引、门索引、当前状态、控制目标、车辆占用
- 显示控制来源和手动控制剩余时间
- 显示控制地址和开到位信号地址
- 手动控制门开关(打开/关闭,带安全保护)
- 清空车辆占用记录
- 自动刷新(默认1秒刷新间隔)
**显示信息**
| 列名 | 说明 |
|------|------|
| 控制器编码 | 门控制器索引 |
| 门编码 | 门索引 |
| 当前状态 | 门的当前状态(开/关,带颜色标识:绿色=打开,红色=关闭) |
| 控制目标 | 门的目标控制状态(开/关,带颜色标识:绿色=开,红色=关) |
| 车辆占用 | 门区域内的小车ID列表(多个ID用逗号分隔,无车辆显示"无" |
| 控制来源 | 当前控制来源(自动/手动) |
| 手动剩余(s) | 手动控制剩余秒数(仅当控制来源=手动时显示,自动时显示"-" |
| 控制地址 | Modbus 线圈地址 |
| 开到位地址 | Modbus 离散输入地址 |
**手动控制功能**
- **打开按钮**:设置手动打开控制,默认保持10秒
- **关闭按钮**:设置手动关闭控制,默认保持10秒
- **安全保护**:当门区域内有车辆占用时,关闭按钮自动禁用,无法执行关闭操作
- 必须先清空车辆占用,才能手动关闭门
- **清空占用按钮**:清空选中门的车辆占用记录
- 清空后,如果门处于打开状态且没有其他小车需要进入,门会自动关闭
- 清空占用后,可以执行手动关闭操作
**控制优先级说明**
- 手动控制优先级高于自动控制
- 手动控制会在指定时间(默认10秒)后自动过期,恢复自动模式
- 可以通过"清空占用"按钮清空车辆占用,然后手动关闭门
---
## 参数说明
### 1. 监控间隔
| 参数 | 默认值 | 说明 |
|------|--------|------|
| 配置监控间隔 | 10秒 | 检查配置文件的间隔 |
| 门控逻辑监控间隔 | 500毫秒 | 检查门控逻辑的间隔(已优化) |
| 监控界面刷新间隔 | 1秒 | 监控界面自动刷新间隔 |
| 手动控制默认保持时间 | 10秒 | 手动控制请求的默认过期时间 |
### 2. 线程安全说明
- **门状态读取**:通过 `controller.DoorStates` 字典访问,所有读写操作受锁保护
- **门控制写入**:通过 `controller.SetDoorControlTarget()` 设置目标状态,实际通信由门控制器内部线程完成
- **配置同步**:配置变更时使用锁保护,确保线程安全
- **控制仲裁**:手动控制请求和自动控制逻辑的仲裁在同一锁内完成,确保线程安全
- **车辆占用管理**:车辆占用的增删改查操作均受锁保护
### 3. 控制仲裁机制
系统采用**控制仲裁机制**来协调手动控制和自动控制:
- **优先级**:手动控制 > 自动控制
- **手动控制过期**:手动控制请求会在指定时间(默认10秒)后自动过期,过期后恢复自动控制
- **安全保护**:当门区域内有车辆占用时,禁止手动关闭门,确保安全
- **仲裁流程**
1. 计算自动目标(基于车辆占用和需要打开标志)
2. 检查是否存在有效的手动控制请求
3. 如果手动控制未过期,使用手动目标;否则使用自动目标
4. 将最终目标写入门控制器的 `DoorControlTargets` 字段
---
## 故障排查
### 问题1:门控制器无法连接
**排查步骤**
1. 检查配置文件中的 IP 和端口是否正确
2. 检查网络连接是否正常
3. 查看诊断日志中的错误信息
4. 确认门控制器硬件是否在线
**诊断命令**
```csharp
var controllers = doorMission.GetDoorControllers();
foreach (var controller in controllers)
{
Console.WriteLine($"控制器{controller.Index}: IP={controller.Ip}, Port={controller.Port}, State={controller.State}, Error={controller.ErrorMessage}");
}
```
### 问题2:门不自动开启
**排查步骤**
1. 确认 `DoorMission` 任务已启动
2. 检查站点的 `EnterDoor` 字段是否配置正确
3. 检查小车站点的 `PreEnterDoor` 字段是否与目标站点的 `EnterDoor` 匹配
4. 查看门控制器的连接状态是否为 `Online`
5. 检查门的状态是否正确读取
**诊断命令**
```csharp
// 检查门状态
bool isOpen = doorMission.GetDoorState(1, 2);
Console.WriteLine($"控制器1门2的状态: {(isOpen ? "打开" : "关闭")}");
// 检查门控制器状态
var controllers = doorMission.GetDoorControllers();
var controller = controllers.FirstOrDefault(c => c.Index == 1);
if (controller != null)
{
Console.WriteLine($"控制器状态: {controller.State}");
Console.WriteLine($"是否在线: {controller.IsOnline}");
Console.WriteLine($"错误信息: {controller.ErrorMessage}");
}
```
### 问题3:配置文件更新后不生效
**排查步骤**
1. 确认配置文件格式正确(JSON格式)
2. 检查配置文件是否保存成功
3. 等待最多10秒,系统会自动检测更新
4. 查看诊断日志中的配置同步信息
### 问题4:监控界面无数据
**排查步骤**
1. 确认 `DoorMission` 任务已启动
2. 检查是否有配置的门控制器
3. 查看门控制器是否成功连接
4. 检查监控界面的刷新间隔设置
### 问题5:手动关闭按钮无法点击
**原因**
- 门区域内有车辆占用,系统安全保护机制禁止手动关闭
**解决方法**
1. 先点击"清空占用"按钮,清空车辆占用记录
2. 清空后,关闭按钮会自动启用
3. 然后可以执行手动关闭操作
### 问题6:手动控制不生效
**排查步骤**
1. 检查手动控制是否已过期(默认10秒)
2. 查看"控制来源"列,确认是否为"手动"
3. 查看"手动剩余(s)"列,确认剩余时间
4. 如果已过期,手动控制会自动恢复为自动模式
5. 可以通过监控界面重新设置手动控制
---
## 附录
### A. 门标识符解析
门标识符格式:`控制器索引.门索引`
**解析规则**
- 必须包含一个点号(`.`
- 点号前后必须为整数
- 解析失败时返回 `null`
**示例**
- `"1.2"``(controllerIndex: 1, doorIndex: 2)`
- `"10.5"``(controllerIndex: 10, doorIndex: 5)`
- `"1"``null` ❌(缺少点号)
- `"1.2.3"``null` ❌(多个点号)
- `"a.2"``null` ❌(非数字)
### B. 诊断日志说明
系统会在以下情况记录诊断日志:
- 门控制器连接成功/失败
- 门控制器配置变更
- 门状态读取失败
- 门控制写入失败
- 配置加载失败
**日志位置**:系统诊断日志
### C. 类结构关系图
```
DoorMission (门控进程)
├── BasicDoorController (抽象基类)
│ │
│ └── ModbusDoorController (Modbus 实现)
├── DoorManager (配置界面)
├── DoorMonitor (监控界面)
└── DoorModel (配置模型)
```
### D. 控制来源枚举
```csharp
public enum ControlSource
{
Auto = 0, // 自动控制
Manual = 1 // 手动控制
}
```
### E. 门控制状态结构
```csharp
public class DoorControlStatus
{
public bool Target { get; set; } // 当前目标状态(true=打开,false=关闭)
public ControlSource Source { get; set; } // 控制来源(Auto/Manual
public double? ManualRemainingSeconds { get; set; } // 手动控制剩余秒数(仅当Source=Manual时有效)
public IReadOnlyList<int> CarsInArea { get; set; } // 车辆占用列表
}
```
### F. 版本历史
| 版本 | 日期 | 主要更新 |
|------|------|----------|
| 1.0 | 2025-01 | 初始版本,支持 Modbus 门控制器 |
| 1.1 | 2025-01 | 新增门控仲裁机制,支持手动控制与自动控制协调 |
| 1.2 | 2025-01 | 新增车辆占用管理功能,监控界面显示车辆占用情况 |
| 1.3 | 2025-01 | 优化门控逻辑监控间隔至500ms,新增安全保护机制(占用时禁止手动关闭) |
---
**文档更新时间**2025-01-21