20 KiB
DoorMission 使用手册
目录
概述
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 门开启条件
门会在以下情况自动开启:
- 小车即将进入区域:当小车到达站点且站点的
EnterDoor字段匹配时,门会在锁定前开启 - 小车在区域内:当小车已进入并锁定站点时,门保持开启状态
2.2 门关闭条件
门会在以下情况自动关闭:
- 小车离开区域后,门自动关闭
2.3 门标识符格式
站点配置中的门标识符格式为:控制器索引.门索引
示例:
"EnterDoor": "1.2" // 表示控制器索引1,门索引2
"LeaveDoor": "2.3" // 表示控制器索引2,门索引3
3. 配置文件动态监控
系统每10秒自动检查 DoorConfig.json 文件,并根据配置变化:
- 添加:新增的门控制器会自动创建并连接
- 删除:已移除的门控制器会自动断开并移除
- 修改:已修改的门控制器会自动更新(IP、端口、类型或门配置变化)
启动与停止
1. 启动任务
var doorMission = new DoorMission();
doorMission.Execute(); // 执行"启动进程"
启动流程:
- 设置数据文件路径(
DoorConfig.json) - 订阅交通控制事件(
BeforeLock、AfterLeave、OnLockAcquired) - 立即加载一次配置(避免监控界面在首次轮询前无数据)
- 启动配置监控任务(每10秒检查一次)
- 启动门控逻辑监控任务(每500毫秒检查一次)
2. 停止任务
doorMission.Stop(); // 执行"停止进程"
停止流程:
- 取消事件订阅
- 取消所有后台任务
- 断开所有门控制器连接
- 等待任务完成(最多等待5秒)
3. 公共方法
3.1 读取门状态
bool isOpen = doorMission.GetDoorState(controllerIndex, doorIndex);
- 参数:
controllerIndex:门控制器索引doorIndex:门索引
- 返回值:
true=打开,false=关闭
3.2 设置门控制目标(自动模式)
doorMission.SetDoorControlTarget(controllerIndex, doorIndex, open);
- 参数:
controllerIndex:门控制器索引doorIndex:门索引open:true=打开,false=关闭
注意:此方法仅设置目标控制状态,实际通信由门控制器内部线程完成,确保线程安全。此方法会立即生效,但可能被手动控制覆盖。
3.3 设置手动控制目标
bool success = doorMission.SetManualDoorControl(controllerIndex, doorIndex, open, holdSeconds);
- 参数:
controllerIndex:门控制器索引doorIndex:门索引open:true=打开,false=关闭holdSeconds:手动保持秒数(可选,默认10秒)
- 返回值:
true=成功,false=失败(当有车辆占用且尝试关闭时返回false)
功能说明:
- 手动控制优先级高于自动控制
- 手动控制会在指定时间后自动过期,恢复自动模式
- 安全保护:当门区域内有车辆占用时,禁止手动关闭门
- 手动控制过期后,系统自动恢复自动控制逻辑
3.4 清除手动控制
doorMission.ClearManualDoorControl(controllerIndex, doorIndex);
- 参数:
controllerIndex:门控制器索引doorIndex:门索引
功能说明:立即清除手动控制请求,恢复自动控制模式。
3.5 清空车辆占用
doorMission.ClearCarsInArea(controllerIndex, doorIndex);
- 参数:
controllerIndex:门控制器索引doorIndex:门索引
功能说明:清空指定门的车辆占用记录。清空后,如果门处于打开状态且没有其他小车需要进入,门会自动关闭。
3.6 获取门控制状态
var status = doorMission.GetDoorControlStatus(controllerIndex, doorIndex);
- 参数:
controllerIndex:门控制器索引doorIndex:门索引
- 返回值:
DoorControlStatus对象,包含:Target:当前目标状态(true=打开,false=关闭)Source:控制来源(ControlSource.Auto或ControlSource.Manual)ManualRemainingSeconds:手动控制剩余秒数(仅当Source=Manual时有效)CarsInArea:车辆占用列表
3.7 获取车辆占用情况
var cars = doorMission.GetCarsInArea(controllerIndex, doorIndex);
- 参数:
controllerIndex:门控制器索引doorIndex:门索引
- 返回值:车辆ID列表(
IReadOnlyList<int>)
配置管理
1. 配置文件格式
配置文件 DoorConfig.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
打开配置界面:
DoorMission.OpenViewer();
门控逻辑
1. 事件响应流程
1.1 BeforeLock 事件
当小车即将锁定站点时触发:
- 检查站点的
EnterDoor字段 - 检查小车当前站点的
PreEnterDoor字段是否匹配 - 如果匹配,设置
_needOpen[(controllerIndex, doorIndex)] = true - 返回门的当前状态(如果门已打开则允许锁定)
1.2 OnLockAcquired 事件
当小车成功锁定站点时触发:
- 检查站点的
EnterDoor字段 - 检查小车当前站点的
PreEnterDoor字段是否匹配 - 如果匹配,将小车ID添加到
carsInAreas[(controllerIndex, doorIndex)] - 设置
_needOpen[(controllerIndex, doorIndex)] = false
1.3 AfterLeave 事件
当小车离开站点时触发:
- 检查站点的
LeaveDoor字段 - 检查小车当前站点的
RearLeaveDoor字段是否匹配 - 如果匹配,从
carsInAreas[(controllerIndex, doorIndex)]中移除小车ID
2. 门控状态监控与仲裁
MonitorDoorLogicAsync 任务每500毫秒执行一次,检查每个门的控制逻辑并进行仲裁:
// 自动目标:有车或需要打开
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. 配置示例
站点配置:
{
"id": 100,
"name": "站点A",
"fields": {
"EnterDoor": "1.2",
"LeaveDoor": "2.3"
}
}
小车站点配置:
{
"id": 50,
"name": "小车当前位置",
"fields": {
"PreEnterDoor": "1.2",
"RearLeaveDoor": "2.3"
}
}
工作流程:
- 小车从站点50驶向站点100
- 到达站点100时,触发
BeforeLock事件 - 系统检查站点100的
EnterDoor("1.2")是否与站点50的PreEnterDoor("1.2")匹配 - 如果匹配,设置控制器1的门2为打开状态
- 门打开后,小车锁定站点100
- 触发
OnLockAcquired事件,门保持打开状态 - 小车离开站点100时,触发
AfterLeave事件 - 系统检查站点100的
LeaveDoor("2.3")是否与站点50的RearLeaveDoor("2.3")匹配 - 如果匹配,从区域内小车列表中移除该小车
- 如果没有其他小车在区域内,门自动关闭
UI界面
1. 配置管理界面
打开方式:
DoorMission.OpenViewer();
功能:
- 门控制器列表管理(添加、删除、修改)
- 门列表管理(为每个控制器添加、删除、修改门)
- 类型选择(自动识别所有带
DoorTypeAttribute的控制器类型) - 配置保存到
DoorConfig.json
2. 监控界面
打开方式:
DoorMission.OpenMonitor();
功能:
- 实时显示所有门的状态
- 显示控制器索引、门索引、当前状态、控制目标、车辆占用
- 显示控制来源和手动控制剩余时间
- 显示控制地址和开到位信号地址
- 手动控制门开关(打开/关闭,带安全保护)
- 清空车辆占用记录
- 自动刷新(默认1秒刷新间隔)
显示信息:
| 列名 | 说明 |
|---|---|
| 控制器编码 | 门控制器索引 |
| 门编码 | 门索引 |
| 当前状态 | 门的当前状态(开/关,带颜色标识:绿色=打开,红色=关闭) |
| 控制目标 | 门的目标控制状态(开/关,带颜色标识:绿色=开,红色=关) |
| 车辆占用 | 门区域内的小车ID列表(多个ID用逗号分隔,无车辆显示"无") |
| 控制来源 | 当前控制来源(自动/手动) |
| 手动剩余(s) | 手动控制剩余秒数(仅当控制来源=手动时显示,自动时显示"-") |
| 控制地址 | Modbus 线圈地址 |
| 开到位地址 | Modbus 离散输入地址 |
手动控制功能:
- 打开按钮:设置手动打开控制,默认保持10秒
- 关闭按钮:设置手动关闭控制,默认保持10秒
- 安全保护:当门区域内有车辆占用时,关闭按钮自动禁用,无法执行关闭操作
- 必须先清空车辆占用,才能手动关闭门
- 清空占用按钮:清空选中门的车辆占用记录
- 清空后,如果门处于打开状态且没有其他小车需要进入,门会自动关闭
- 清空占用后,可以执行手动关闭操作
控制优先级说明:
- 手动控制优先级高于自动控制
- 手动控制会在指定时间(默认10秒)后自动过期,恢复自动模式
- 可以通过"清空占用"按钮清空车辆占用,然后手动关闭门
参数说明
1. 监控间隔
| 参数 | 默认值 | 说明 |
|---|---|---|
| 配置监控间隔 | 10秒 | 检查配置文件的间隔 |
| 门控逻辑监控间隔 | 500毫秒 | 检查门控逻辑的间隔(已优化) |
| 监控界面刷新间隔 | 1秒 | 监控界面自动刷新间隔 |
| 手动控制默认保持时间 | 10秒 | 手动控制请求的默认过期时间 |
2. 线程安全说明
- 门状态读取:通过
controller.DoorStates字典访问,所有读写操作受锁保护 - 门控制写入:通过
controller.SetDoorControlTarget()设置目标状态,实际通信由门控制器内部线程完成 - 配置同步:配置变更时使用锁保护,确保线程安全
- 控制仲裁:手动控制请求和自动控制逻辑的仲裁在同一锁内完成,确保线程安全
- 车辆占用管理:车辆占用的增删改查操作均受锁保护
3. 控制仲裁机制
系统采用控制仲裁机制来协调手动控制和自动控制:
- 优先级:手动控制 > 自动控制
- 手动控制过期:手动控制请求会在指定时间(默认10秒)后自动过期,过期后恢复自动控制
- 安全保护:当门区域内有车辆占用时,禁止手动关闭门,确保安全
- 仲裁流程:
- 计算自动目标(基于车辆占用和需要打开标志)
- 检查是否存在有效的手动控制请求
- 如果手动控制未过期,使用手动目标;否则使用自动目标
- 将最终目标写入门控制器的
DoorControlTargets字段
故障排查
问题1:门控制器无法连接
排查步骤:
- 检查配置文件中的 IP 和端口是否正确
- 检查网络连接是否正常
- 查看诊断日志中的错误信息
- 确认门控制器硬件是否在线
诊断命令:
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:门不自动开启
排查步骤:
- 确认
DoorMission任务已启动 - 检查站点的
EnterDoor字段是否配置正确 - 检查小车站点的
PreEnterDoor字段是否与目标站点的EnterDoor匹配 - 查看门控制器的连接状态是否为
Online - 检查门的状态是否正确读取
诊断命令:
// 检查门状态
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:配置文件更新后不生效
排查步骤:
- 确认配置文件格式正确(JSON格式)
- 检查配置文件是否保存成功
- 等待最多10秒,系统会自动检测更新
- 查看诊断日志中的配置同步信息
问题4:监控界面无数据
排查步骤:
- 确认
DoorMission任务已启动 - 检查是否有配置的门控制器
- 查看门控制器是否成功连接
- 检查监控界面的刷新间隔设置
问题5:手动关闭按钮无法点击
原因:
- 门区域内有车辆占用,系统安全保护机制禁止手动关闭
解决方法:
- 先点击"清空占用"按钮,清空车辆占用记录
- 清空后,关闭按钮会自动启用
- 然后可以执行手动关闭操作
问题6:手动控制不生效
排查步骤:
- 检查手动控制是否已过期(默认10秒)
- 查看"控制来源"列,确认是否为"手动"
- 查看"手动剩余(s)"列,确认剩余时间
- 如果已过期,手动控制会自动恢复为自动模式
- 可以通过监控界面重新设置手动控制
附录
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. 控制来源枚举
public enum ControlSource
{
Auto = 0, // 自动控制
Manual = 1 // 手动控制
}
E. 门控制状态结构
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