Files
StandardSence/StandardScene.Core/ExtendDevice/Door/DoorMission.cs
T
zhaowei.huang a0dc1e6cd0 refactor: 插件 UI 从 WinForms 迁移到 CycleGUI,并修复代码质量问题
将 StandardScene 各插件的配置/监控窗体从 WinForms 迁移到 CycleGUI(删除 .Designer.cs/.resx,重写为 PanelBuilder 立即模式 UI,新增 CycleUiHelper 统一对话框)。

同时修复代码审核中的问题:
- 后台文件写入加锁 + try/catch(ButtonBoxManager / DoorManager,对齐 LoopViewer.SaveTasks 模式)
- CoderFieldsMetadata.cs 启用 #nullable enable,消除 CS8632 警告
- DummyCar 移除已废弃的 rightClickAction()/SetPosition()
- CarRemoteHelper.OpenVehicleWebPage 的 Process.Start 加 try/catch
- 重命名名不副实的 Mstsc()(现为打开网页)
- 统一弃元命名为 _
- TrafficInterlockViewer 改用稳定 Id(GUID)做选择/编辑,替代行索引
- csproj 改用 $(CGUILibDir) 解析 CycleGUI,绝对路径收敛到 Directory.Build.props

构建:dotnet build StandardScene.sln → 0 错误,30 警告(均为历史遗留)。
注:static 单例状态重构(审核第 8 项)暂未处理,留待单独任务。
2026-06-26 15:00:53 +08:00

1025 lines
39 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using LessokajiWeaverUtilities.MagicAttributes;
using LessokajiWeaverUtilities.Utilities;
using Newtonsoft.Json;
using SimpleLite.RCS;
using SimpleLite.RCS.CarTypes;
using SimpleCore;
using SimpleCore.Library;
using SimpleCore.PropType;
using SimpleCore.Traffic;
using StandardScene.Utils;
namespace StandardScene.ExtendDevice.Door
{
//todo:2026.1.21 1,门控制器的通信参数配置移动到具体门控制器类中管理,实现对不同通信类型的支持
[MissionType(Name = "门控进程")]
[I18N.DocumentTranslation(Name = "DoorMission", locale = "en")]
public class DoorMission : Mission
{
private const string DataFileName = "DoorConfig.json";
private string _dataFilePath;
/// <summary>
/// 当前所有门控制器实例列表
/// </summary>
private List<BasicDoorController> _doorControllers = new List<BasicDoorController>();
/// <summary>
/// 用于管理异步循环的取消令牌源
/// </summary>
private CancellationTokenSource _cancellationTokenSource;
/// <summary>
/// 保存监控配置与状态的后台任务
/// </summary>
private Task _configTask;
private Task _stateTask;
/// <summary>
/// 同步锁,用于保护门控制器列表的并发访问
/// </summary>
private readonly object _syncLock = new object();
/// <summary>
/// 门区域内的小车记录:(ControllerIndex, DoorIndex) -> List<CarId>
/// </summary>
[JsonIgnore] private readonly Dictionary<(int ControllerIndex, int DoorIndex), List<int>> carsInAreas = new Dictionary<(int ControllerIndex, int DoorIndex), List<int>>();
/// <summary>
/// 需要打开的门:(ControllerIndex, DoorIndex) -> bool
/// </summary>
[JsonIgnore] private readonly Dictionary<(int ControllerIndex, int DoorIndex), bool> _needOpen = new Dictionary<(int ControllerIndex, int DoorIndex), bool>();
/// <summary>
/// 手动控制请求:(ControllerIndex, DoorIndex) -> ManualControlRequest
/// </summary>
[JsonIgnore] private readonly Dictionary<(int ControllerIndex, int DoorIndex), ManualControlRequest> _manualRequests = new Dictionary<(int ControllerIndex, int DoorIndex), ManualControlRequest>();
private const int DefaultManualHoldSeconds = 10;
public enum ControlSource
{
Auto = 0,
Manual = 1
}
private class ManualControlRequest
{
public bool Target { get; set; }
public DateTime ExpireAt { get; set; }
}
public class DoorControlStatus
{
public bool Target { get; set; }
public ControlSource Source { get; set; }
public double? ManualRemainingSeconds { get; set; }
public IReadOnlyList<int> CarsInArea { get; set; } = Array.Empty<int>();
}
/// <summary>
/// 监控界面快照项(线程安全快照,避免UI直接枚举可变字典)
/// </summary>
public class DoorMonitorSnapshotItem
{
public int ControllerIndex { get; set; }
public int DoorIndex { get; set; }
public DoorState State { get; set; }
public bool Target { get; set; }
public ControlSource Source { get; set; }
public double? ManualRemainingSeconds { get; set; }
public IReadOnlyList<int> CarsInArea { get; set; } = Array.Empty<int>();
public ushort ControlAddress { get; set; }
public ushort OpenStatusAddress { get; set; }
}
[MethodMember(Name = "启动进程")]
[I18N.DocumentTranslation(Name = "Start Mission", locale = "en")]
public override void Execute()
{
// 设置数据文件路径
_dataFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DataFileName);
// 如果已经启动,先停止之前的循环
StopInternalAsync().GetAwaiter().GetResult();
TrafficControl.BeforeLock += BeforeLockEvent;
TrafficControl.AfterLeave += AfterLeaveEvent;
TrafficControl.OnLockAcquired += OnLockAcquiredEvent;
// 启动时立即加载一次配置,避免监控界面在第一次轮询前无数据
try
{
var configControllers = LoadDoorControllerConfig();
SyncDoorControllers(configControllers);
}
catch (Exception ex)
{
Diagnosis.Log($"启动时加载门控制器配置失败: {ExceptionFormatter.FormatEx(ex)}", "DoorMission", true);
}
// 创建新的取消令牌源
_cancellationTokenSource = new CancellationTokenSource();
// 启动异步循环
var token = _cancellationTokenSource.Token;
_configTask = Task.Run(async () => await MonitorDoorConfigAsync(token), token);
_stateTask = Task.Run(async () => await MonitorDoorLogicAsync(token), token);
status.status = "Running";
}
/// <summary>
/// 停止监控任务
/// </summary>
[MethodMember(Name = "停止进程")]
[I18N.DocumentTranslation(Name = "Stop Mission", locale = "en")]
public void Stop()
{
StopInternalAsync().GetAwaiter().GetResult();
status.status = "/";
}
/// <summary>
/// 取消并释放当前的取消令牌源
/// </summary>
private async Task StopInternalAsync()
{
// 取消事件订阅
TrafficControl.BeforeLock -= BeforeLockEvent;
TrafficControl.AfterLeave -= AfterLeaveEvent;
TrafficControl.OnLockAcquired -= OnLockAcquiredEvent;
var cts = Interlocked.Exchange(ref _cancellationTokenSource, null);
var configTask = Interlocked.Exchange(ref _configTask, null);
var stateTask = Interlocked.Exchange(ref _stateTask, null);
if (cts == null && configTask == null && stateTask == null)
{
return;
}
try
{
cts?.Cancel();
}
catch (ObjectDisposedException)
{
// 已释放,忽略
}
var runningTasks = new[] { configTask, stateTask }
.Where(t => t != null)
.ToArray();
if (runningTasks.Length > 0)
{
var aggregateTask = Task.WhenAll(runningTasks);
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(5));
var completedTask = await Task.WhenAny(aggregateTask, timeoutTask).ConfigureAwait(false);
if (completedTask == timeoutTask)
{
Diagnosis.Log("停止门控任务超时", "DoorMission", true);
}
else
{
try
{
await aggregateTask.ConfigureAwait(false);
}
catch (Exception ex)
{
Diagnosis.Log($"停止门控任务时发生异常: {ExceptionFormatter.FormatEx(ex)}", "DoorMission", true);
}
}
}
cts?.Dispose();
DisconnectAllDoorControllers();
}
/// <summary>
/// 断开所有门控制器连接
/// </summary>
private void DisconnectAllDoorControllers()
{
List<BasicDoorController> snapshot;
lock (_syncLock)
{
snapshot = _doorControllers.ToList();
}
foreach (var controller in snapshot)
{
try
{
controller.Disconnect();
}
catch (Exception ex)
{
Diagnosis.Log($"停止门控制器失败: Index={controller.Index}, Error={ExceptionFormatter.FormatEx(ex)}", "DoorMission", true);
}
}
}
/// <summary>
/// 异步监控门控制器配置文件
/// </summary>
private async Task MonitorDoorConfigAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
// 读取配置文件
var configControllers = LoadDoorControllerConfig();
// 同步门控制器列表
SyncDoorControllers(configControllers);
// 等待10秒
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken);
}
catch (OperationCanceledException)
{
// 正常取消,退出循环
break;
}
catch (Exception ex)
{
Diagnosis.Log($"门控制器配置监控错误: {ExceptionFormatter.FormatEx(ex)}", "DoorMission", true);
// 发生错误时等待5秒后重试
try
{
await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken);
}
catch (OperationCanceledException)
{
break;
}
}
}
}
/// <summary>
/// 监控门控逻辑
/// </summary>
private async Task MonitorDoorLogicAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
lock (_syncLock)
{
foreach (var controller in _doorControllers)
{
foreach (var doorConfig in controller.DoorConfigs.Values)
{
var doorIndex = doorConfig.Index;
var key = (controller.Index, doorIndex);
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);
}
}
}
await Task.Delay(TimeSpan.FromMilliseconds(500), cancellationToken);
}
catch (OperationCanceledException)
{
// 正常取消,退出循环
break;
}
catch (Exception ex)
{
Diagnosis.Log($"门控逻辑监控错误: {ExceptionFormatter.FormatEx(ex)}", "DoorMission", true);
// 发生错误时等待5秒后重试
try
{
await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken);
}
catch (OperationCanceledException)
{
break;
}
}
}
}
/// <summary>
/// 加载门控制器配置文件
/// </summary>
private List<DoorControllerModel> LoadDoorControllerConfig()
{
try
{
if (File.Exists(_dataFilePath))
{
var jsonContent = File.ReadAllText(_dataFilePath, Encoding.UTF8);
if (!string.IsNullOrWhiteSpace(jsonContent))
{
var controllers = jsonContent.JsonTo<List<DoorControllerModel>>();
return controllers ?? new List<DoorControllerModel>();
}
}
}
catch (Exception ex)
{
Diagnosis.Log($"加载门控制器配置文件失败: {ExceptionFormatter.FormatEx(ex)}", "DoorMission", true);
}
return new List<DoorControllerModel>();
}
/// <summary>
/// 同步门控制器列表,根据配置文件进行增删改
/// </summary>
private void SyncDoorControllers(List<DoorControllerModel> configControllers)
{
var controllersToAdd = new List<DoorControllerModel>();
lock (_syncLock)
{
// 创建配置中的门控制器索引字典
var configDict = configControllers.ToDictionary(c => c.Index);
// 创建当前门控制器索引字典
var currentDict = _doorControllers.ToDictionary(c => c.Index);
// 1. 删除:在配置中不存在的门控制器
var toRemove = _doorControllers.Where(c => !configDict.ContainsKey(c.Index)).ToList();
foreach (var controller in toRemove)
{
try
{
controller.Disconnect();
_doorControllers.Remove(controller);
Diagnosis.Post($"删除门控制器: Index={controller.Index}, IP={controller.Ip}", "DoorMission", true);
}
catch (Exception ex)
{
Diagnosis.Log($"删除门控制器失败: {ExceptionFormatter.FormatEx(ex)}", "DoorMission", true);
}
}
// 2. 添加和修改:遍历配置中的门控制器
foreach (var configController in configControllers)
{
if (currentDict.TryGetValue(configController.Index, out var existingController))
{
// 修改:检查是否需要更新
if (ShouldUpdateDoorController(existingController, configController))
{
try
{
UpdateDoorController(existingController, configController);
Diagnosis.Post($"更新门控制器: Index={configController.Index}, IP={configController.Ip}, Type={configController.Type}", "DoorMission", true);
}
catch (Exception ex)
{
Diagnosis.Log($"更新门控制器失败: {ExceptionFormatter.FormatEx(ex)}", "DoorMission", true);
}
}
}
else
{
controllersToAdd.Add(configController);
}
}
}
foreach (var configController in controllersToAdd)
{
try
{
var newController = CreateDoorControllerInstance(configController);
if (newController != null)
{
lock (_syncLock)
{
_doorControllers.Add(newController);
}
Diagnosis.Post($"添加门控制器: Index={configController.Index}, IP={configController.Ip}, Type={configController.Type}", "DoorMission", true);
}
else
{
Diagnosis.Log($"无法创建门控制器实例: Index={configController.Index}, Type={configController.Type}", "DoorMission", true);
}
}
catch (Exception ex)
{
Diagnosis.Log($"添加门控制器失败: {ExceptionFormatter.FormatEx(ex)}", "DoorMission", true);
}
}
}
/// <summary>
/// 获取门控制器类型的 DoorTypeAttribute.Name
/// </summary>
private string GetDoorTypeName(Type controllerType)
{
if (controllerType == null)
{
return string.Empty;
}
var attr = controllerType.GetCustomAttribute<DoorTypeAttribute>();
return attr?.Name ?? controllerType.Name;
}
/// <summary>
/// 判断是否需要更新门控制器
/// </summary>
private bool ShouldUpdateDoorController(BasicDoorController existingController, DoorControllerModel configController)
{
// 检查基本属性是否变更
var existingTypeName = GetDoorTypeName(existingController.GetType());
if (existingController.Ip != configController.Ip
|| existingController.Port != configController.Port
|| existingTypeName != configController.Type)
{
return true;
}
// 检查门配置信息是否变更
return HasDoorConfigsChanged(existingController, configController);
}
/// <summary>
/// 检查门配置信息是否变更
/// </summary>
private bool HasDoorConfigsChanged(BasicDoorController existingController, DoorControllerModel configController)
{
var configDoors = configController.Doors ?? new List<DoorModel>();
var configDict = configDoors.ToDictionary(d => d.Index);
var existingDict = existingController.DoorConfigs;
// 检查门数量是否变化
if (existingDict.Count != configDict.Count)
{
return true;
}
// 检查每个门的配置是否变化
foreach (var configDoor in configDoors)
{
if (!existingDict.TryGetValue(configDoor.Index, out var existingDoor))
{
// 新增了门
return true;
}
// 检查门配置是否变化
if (existingDoor.ControlAddress != configDoor.ControlAddress
|| existingDoor.OpenStatusAddress != configDoor.OpenStatusAddress)
{
return true;
}
}
// 检查是否有门被删除
foreach (var existingKey in existingDict.Keys)
{
if (!configDict.ContainsKey(existingKey))
{
return true;
}
}
return false;
}
/// <summary>
/// 更新门控制器属性
/// </summary>
private void UpdateDoorController(BasicDoorController controller, DoorControllerModel configController)
{
// 如果类型改变,需要重新创建实例
var existingTypeName = GetDoorTypeName(controller.GetType());
if (existingTypeName != configController.Type)
{
// 断开旧连接
controller.Disconnect();
// 从列表中移除
_doorControllers.Remove(controller);
// 创建新实例
var newController = CreateDoorControllerInstance(configController);
if (newController != null)
{
_doorControllers.Add(newController);
}
}
else
{
// 只更新属性
bool needReconnect = controller.Ip != configController.Ip || controller.Port != configController.Port;
controller.Ip = configController.Ip;
controller.Port = configController.Port;
// 更新门配置信息
controller.UpdateDoorConfigs(configController.Doors);
// 初始化门状态(基于配置中的门索引)
var doorIndices = configController.Doors?.Select(d => d.Index).ToList() ?? new List<int>();
controller.InitializeDoors(doorIndices);
// 如果IP或端口改变,需要重新连接
if (needReconnect)
{
controller.Disconnect();
controller.Connect();
}
}
}
/// <summary>
/// 根据 DoorTypeAttribute.Name 获取类型
/// </summary>
private Type GetControllerTypeByDoorTypeName(string doorTypeName)
{
if (string.IsNullOrWhiteSpace(doorTypeName))
{
return null;
}
try
{
// 在插件/宿主环境下 GetExecutingAssembly 可能不是 StandardScene.dll
// 这里固定从门控制器基类所在程序集查找
// 跨程序集发现:门控制器具体类型可能位于卫星插件 dllStandardScene.Devices.Door),
// 用内核同款全域类型发现替代仅扫基类所在程序集。
var allControllerTypes = SimpleLite.Utils.UiTypeDiscovery.AllTypes()
.Where(t => t.IsClass
&& !t.IsAbstract
&& t.Namespace == typeof(BasicDoorController).Namespace
&& t.IsSubclassOf(typeof(BasicDoorController))
&& t.IsDefined(typeof(DoorTypeAttribute), false));
foreach (var type in allControllerTypes)
{
var attr = type.GetCustomAttribute<DoorTypeAttribute>();
if (attr != null && attr.Name == doorTypeName)
{
return type;
}
}
return null;
}
catch (Exception ex)
{
Diagnosis.Log($"获取门控制器类型失败: DoorTypeName={doorTypeName}, Error={ExceptionFormatter.FormatEx(ex)}", "DoorMission", true);
return null;
}
}
/// <summary>
/// 通过 DoorTypeAttribute.Name 创建门控制器实例
/// </summary>
private BasicDoorController CreateDoorControllerInstance(DoorControllerModel configController)
{
if (string.IsNullOrWhiteSpace(configController.Type))
{
return null;
}
try
{
// 通过 DoorTypeAttribute.Name 查找类型
var controllerType = GetControllerTypeByDoorTypeName(configController.Type);
if (controllerType == null)
{
Diagnosis.Log($"未找到门控制器类型: {configController.Type}", "DoorMission", true);
return null;
}
// 使用反射创建实例
var instance = (BasicDoorController)Activator.CreateInstance(controllerType);
// 设置属性
instance.Index = configController.Index;
instance.Ip = configController.Ip;
instance.Port = configController.Port;
// 初始化门配置信息
instance.InitializeDoorConfigs(configController.Doors);
// 初始化门状态(基于配置中的门索引)
var doorIndices = configController.Doors?.Select(d => d.Index).ToList() ?? new List<int>();
instance.InitializeDoors(doorIndices);
// 自动连接
instance.Connect();
return instance;
}
catch (Exception ex)
{
Diagnosis.Log($"创建门控制器实例失败: Type={configController.Type}, Error={ExceptionFormatter.FormatEx(ex)}", "DoorMission", true);
return null;
}
}
/// <summary>
/// 获取当前所有门控制器实例(只读)
/// </summary>
public IReadOnlyList<BasicDoorController> GetDoorControllers()
{
lock (_syncLock)
{
return _doorControllers.ToList().AsReadOnly();
}
}
/// <summary>
/// 获取用于监控界面的门状态快照
/// </summary>
public IReadOnlyList<DoorMonitorSnapshotItem> GetDoorMonitorSnapshot()
{
lock (_syncLock)
{
var snapshot = new List<DoorMonitorSnapshotItem>();
var now = DateTime.Now;
foreach (var controller in _doorControllers)
{
var doorConfigs = controller.DoorConfigs?.Values?.ToList() ?? new List<DoorModel>();
foreach (var doorConfig in doorConfigs)
{
var key = (controller.Index, doorConfig.Index);
var cars = carsInAreas.TryGetValue(key, out var list) && list != null
? list.ToList().AsReadOnly()
: new List<int>().AsReadOnly();
var source = ControlSource.Auto;
double? manualRemaining = null;
bool target = false;
if (_manualRequests.TryGetValue(key, out var manual))
{
if (manual.ExpireAt > now)
{
source = ControlSource.Manual;
target = manual.Target;
manualRemaining = (manual.ExpireAt - now).TotalSeconds;
}
else
{
_manualRequests.Remove(key);
}
}
if (source == ControlSource.Auto)
{
var needOpen = _needOpen.TryGetValue(key, out var open) && open;
target = needOpen || cars.Count > 0;
}
snapshot.Add(new DoorMonitorSnapshotItem
{
ControllerIndex = controller.Index,
DoorIndex = doorConfig.Index,
State = controller.GetDoorState(doorConfig.Index),
Target = target,
Source = source,
ManualRemainingSeconds = manualRemaining,
CarsInArea = cars,
ControlAddress = doorConfig.ControlAddress,
OpenStatusAddress = doorConfig.OpenStatusAddress
});
}
}
return snapshot.AsReadOnly();
}
}
/// <summary>
/// 解析门标识符字符串(格式:控制器索引.门索引)
/// </summary>
/// <param name="doorIdentifier">门标识符字符串,格式:控制器索引.门索引</param>
/// <returns>如果解析成功返回(控制器索引, 门索引),否则返回null</returns>
private (int ControllerIndex, int DoorIndex)? ParseDoorIdentifier(string doorIdentifier)
{
if (string.IsNullOrWhiteSpace(doorIdentifier))
{
return null;
}
var parts = doorIdentifier.Split('.');
if (parts.Length != 2)
{
return null;
}
if (int.TryParse(parts[0].Trim(), out int controllerIndex) &&
int.TryParse(parts[1].Trim(), out int doorIndex))
{
return (controllerIndex, doorIndex);
}
return null;
}
/// <summary>
/// 读取门状态
/// </summary>
/// <param name="controllerIndex">门控制器索引</param>
/// <param name="doorIndex">门索引</param>
/// <returns>true=打开,false=关闭</returns>
public bool GetDoorState(int controllerIndex, int doorIndex)
{
lock (_syncLock)
{
var controller = _doorControllers.FirstOrDefault(c => c.Index == controllerIndex);
if (controller != null && controller.DoorConfigs.ContainsKey(doorIndex))
{
// 只访问控制器中的门状态字段,不直接进行通信
var state = controller.GetDoorState(doorIndex);
return state == DoorState.Open;
}
}
return false;
}
/// <summary>
/// 写入门控制状态
/// </summary>
/// <param name="controllerIndex">门控制器索引</param>
/// <param name="doorIndex">门索引</param>
/// <param name="open">true=打开,false=关闭</param>
public void SetDoorControlTarget(int controllerIndex, int doorIndex, bool open)
{
lock (_syncLock)
{
var controller = _doorControllers.FirstOrDefault(c => c.Index == controllerIndex);
if (controller != null && controller.DoorConfigs.ContainsKey(doorIndex))
{
// 仅设置门的目标控制状态,由门控制器内部线程负责实际通信
controller.SetDoorControlTarget(doorIndex, open);
}
}
}
/// <summary>
/// 设置手动控制目标,支持保持时间,车辆占用时禁止手动关闭
/// </summary>
/// <param name="controllerIndex">控制器索引</param>
/// <param name="doorIndex">门索引</param>
/// <param name="open">目标开关</param>
/// <param name="holdSeconds">手动保持秒数,默认10秒</param>
/// <returns>成功返回true;当占用且尝试关闭时返回false</returns>
public bool SetManualDoorControl(int controllerIndex, int doorIndex, bool open, int? holdSeconds = null)
{
lock (_syncLock)
{
// 车辆占用时禁止手动关闭
var key = (controllerIndex, doorIndex);
if (!open && carsInAreas.TryGetValue(key, out var cars) && cars.Count > 0)
{
return false;
}
var seconds = holdSeconds.GetValueOrDefault(DefaultManualHoldSeconds);
_manualRequests[key] = new ManualControlRequest
{
Target = open,
ExpireAt = DateTime.Now.AddSeconds(seconds)
};
return true;
}
}
/// <summary>
/// 清除手动控制,恢复自动
/// </summary>
public void ClearManualDoorControl(int controllerIndex, int doorIndex)
{
lock (_syncLock)
{
var key = (controllerIndex, doorIndex);
_manualRequests.Remove(key);
}
}
/// <summary>
/// 清空车辆占用记录
/// </summary>
public void ClearCarsInArea(int controllerIndex, int doorIndex)
{
lock (_syncLock)
{
var key = (controllerIndex, doorIndex);
if (carsInAreas.ContainsKey(key))
{
carsInAreas[key].Clear();
}
}
}
/// <summary>
/// 获取门的控制状态(目标、来源、手动剩余时间、占用车辆)
/// </summary>
public DoorControlStatus GetDoorControlStatus(int controllerIndex, int doorIndex)
{
lock (_syncLock)
{
var key = (controllerIndex, doorIndex);
// 车辆占用
IReadOnlyList<int> cars;
if (carsInAreas.TryGetValue(key, out var list) && list != null)
{
cars = list.ToList().AsReadOnly();
}
else
{
cars = new List<int>().AsReadOnly();
}
// 当前目标来源与剩余
var source = ControlSource.Auto;
double? remaining = null;
bool target = false;
if (_manualRequests.TryGetValue(key, out var manual))
{
if (manual.ExpireAt > DateTime.Now)
{
source = ControlSource.Manual;
target = manual.Target;
remaining = (manual.ExpireAt - DateTime.Now).TotalSeconds;
}
else
{
_manualRequests.Remove(key);
}
}
if (source == ControlSource.Auto)
{
var needOpen = _needOpen.TryGetValue(key, out var open) && open;
var hasCars = cars.Count > 0;
target = needOpen || hasCars;
}
return new DoorControlStatus
{
Target = target,
Source = source,
ManualRemainingSeconds = remaining,
CarsInArea = cars
};
}
}
/// <summary>
/// 获取门的车辆占用情况
/// </summary>
/// <param name="controllerIndex">门控制器索引</param>
/// <param name="doorIndex">门索引</param>
/// <returns>车辆ID列表,如果没有车辆则返回空列表</returns>
public IReadOnlyList<int> GetCarsInArea(int controllerIndex, int doorIndex)
{
lock (_syncLock)
{
var key = (controllerIndex, doorIndex);
if (carsInAreas.TryGetValue(key, out var cars) && cars != null)
{
return cars.ToList().AsReadOnly();
}
return new List<int>().AsReadOnly();
}
}
private bool BeforeLockEvent(AbstractCar car, int siteId)
{
var site = SimpleLib.GetSite(siteId);
if (!site.fields.TryGetValue("EnterDoor", out string fieldStr)) return true;
var lastSiteId = car.status.holdingLocks.LastOrDefault();
var lastSite = SimpleLib.GetSite(lastSiteId);
if (lastSite == null || !lastSite.fields.TryGetValue("PreEnterDoor",out var value)) return true;
if (value != fieldStr) return true;
var doorInfo = ParseDoorIdentifier(fieldStr);
if (!doorInfo.HasValue) return true;
var (controllerIndex, doorIndex) = doorInfo.Value;
lock (_syncLock)
{
_needOpen[(controllerIndex, doorIndex)] = true;
return GetDoorState(controllerIndex, doorIndex);
}
}
private void AfterLeaveEvent(AbstractCar car, int siteId)
{
var site = SimpleLib.GetSite(siteId);
if (!site.fields.TryGetValue("LeaveDoor", out string fieldStr)) return;
var carSiteId = car.status.holdingLocks.FirstOrDefault();
var carSite = SimpleLib.GetSite(carSiteId);
if (carSite == null || !carSite.fields.TryGetValue("RearLeaveDoor",out var value)) return;
if (value != fieldStr) return;
var doorInfo = ParseDoorIdentifier(fieldStr);
if (!doorInfo.HasValue) return;
var (controllerIndex, doorIndex) = doorInfo.Value;
var key = (controllerIndex, doorIndex);
lock (_syncLock)
{
if (carsInAreas.TryGetValue(key, out var list))
{
list.Remove(car.id);
}
}
}
private void OnLockAcquiredEvent(AbstractCar car, int siteId)
{
var site = SimpleLib.GetSite(siteId);
if (!site.fields.TryGetValue("EnterDoor", out string fieldStr)) return;
if(car.status.holdingLocks.Length<2) return;
var preSiteId = car.status.holdingLocks[car.status.holdingLocks.Length-2];
var preSite = SimpleLib.GetSite(preSiteId);
if (preSite == null || !preSite.fields.TryGetValue("PreEnterDoor", out var value)) return ;
if (value != fieldStr) return ;
var doorInfo = ParseDoorIdentifier(fieldStr);
if (!doorInfo.HasValue) return;
var (controllerIndex, doorIndex) = doorInfo.Value;
var key = (controllerIndex, doorIndex);
lock (_syncLock)
{
if (!carsInAreas.TryGetValue(key, out var list))
{
list = new List<int>();
carsInAreas[key] = list;
}
if (!list.Contains(car.id))
{
list.Add(car.id);
}
_needOpen[key] = false;
}
}
/// <summary>
/// 打开门控制器管理界面
/// </summary>
[MethodMember(Name = "打开管理界面")]
[I18N.DocumentTranslation(Name = "Open Manager", locale = "en")]
public static void OpenViewer()
{
DoorManager.OpenViewer();
}
/// <summary>
/// 打开门控监控界面
/// </summary>
[MethodMember(Name = "打开监控界面")]
[I18N.DocumentTranslation(Name = "Open Monitor", locale = "en")]
public static void OpenMonitor()
{
try
{
DoorMonitor.Open();
}
catch (Exception ex)
{
CycleUiHelper.Alert("错误", $"打开门控监控界面失败: {ex.Message}");
}
}
}
}