using System; using System.Collections.Generic; using MyParking.Shared; namespace MultiWheelC.Fleet { // 主车已经接收并接受的一辆成员车安全状态。 public readonly struct FleetMemberSafetyStatus { public FleetMemberSafetyStatus( int vehicleId, long planId, bool isStateAvailable, bool isFaulted, int failureCode, double lastAcceptedReportTimeSeconds) { if (vehicleId <= 0) { throw new ArgumentOutOfRangeException( nameof(vehicleId), "成员车编号必须大于零。"); } if (planId < 0) { throw new ArgumentOutOfRangeException( nameof(planId), "任务编号不能为负数。"); } NumericGuard.EnsureFiniteNonNegative( lastAcceptedReportTimeSeconds, nameof(lastAcceptedReportTimeSeconds)); VehicleId = vehicleId; PlanId = planId; IsStateAvailable = isStateAvailable; IsFaulted = isFaulted; FailureCode = failureCode; LastAcceptedReportTimeSeconds = lastAcceptedReportTimeSeconds; } public int VehicleId { get; } public long PlanId { get; } public bool IsStateAvailable { get; } public bool IsFaulted { get; } // 零表示成员车没有报告结构化故障。 public int FailureCode { get; } // 使用主车本地单调时钟,不能直接填写从车上传的时间戳。 public double LastAcceptedReportTimeSeconds { get; } } // 一次安全检查的结果;ShouldStop可直接作为是否停车的判断标识。 public readonly struct FleetSafetyDecision { internal FleetSafetyDecision( bool shouldStop, int sourceVehicleId, string reason) { ShouldStop = shouldStop; SourceVehicleId = sourceVehicleId; Reason = reason ?? string.Empty; } public bool ShouldStop { get; } // 零表示原因属于整个车队,而不是某一辆成员车。 public int SourceVehicleId { get; } public string Reason { get; } } // 检查成员通信和健康状态,并锁存需要整队停车的首个原因。 public sealed class FleetSafetySupervisor { private readonly double _communicationTimeoutSeconds; private long _activePlanId; private FleetSafetyDecision _latchedDecision; public FleetSafetySupervisor( double communicationTimeoutSeconds) { NumericGuard.EnsureFinitePositive( communicationTimeoutSeconds, nameof(communicationTimeoutSeconds)); _communicationTimeoutSeconds = communicationTimeoutSeconds; Reset(); } public double CommunicationTimeoutSeconds => _communicationTimeoutSeconds; public long ActivePlanId => _activePlanId; public bool IsActive => _activePlanId > 0; public bool IsStopLatched => _latchedDecision.ShouldStop; public FleetSafetyDecision LastDecision => _latchedDecision; // 开始一次新任务,同时清除上一任务留下的停车锁存。 public void Start(long planId) { if (planId <= 0) { throw new ArgumentOutOfRangeException( nameof(planId), "活动任务编号必须大于零。"); } _activePlanId = planId; _latchedDecision = CreateContinueDecision(); } // 返回ShouldStop;本类只负责判定,实际停车由后续运行入口执行。 public FleetSafetyDecision Evaluate( FleetLayout layout, IReadOnlyList memberStatuses, double currentTimeSeconds) { if (layout == null) { throw new ArgumentNullException(nameof(layout)); } if (memberStatuses == null) { throw new ArgumentNullException( nameof(memberStatuses)); } NumericGuard.EnsureFiniteNonNegative( currentTimeSeconds, nameof(currentTimeSeconds)); if (!IsActive) { return new FleetSafetyDecision( true, 0, "车队安全监督器尚未启动活动任务。"); } if (IsStopLatched) { return _latchedDecision; } var statusesByVehicleId = new Dictionary(); for (var index = 0; index < memberStatuses.Count; index++) { var status = memberStatuses[index]; // 非当前编队成员的状态不参与本次任务安全判定。 if (!layout.TryGetVehicle( status.VehicleId, out _)) { continue; } if (statusesByVehicleId.ContainsKey( status.VehicleId)) { return LatchStop( status.VehicleId, $"成员车{status.VehicleId}存在重复状态报告。"); } statusesByVehicleId.Add( status.VehicleId, status); } for (var index = 0; index < layout.Vehicles.Count; index++) { var vehicleId = layout.Vehicles[index].VehicleId; if (!statusesByVehicleId.TryGetValue( vehicleId, out var status)) { return LatchStop( vehicleId, $"未收到成员车{vehicleId}的状态报告。"); } if (status.PlanId != _activePlanId) { return LatchStop( vehicleId, $"成员车{vehicleId}报告的任务编号" + $"{status.PlanId}与当前任务" + $"{_activePlanId}不一致。"); } if (status.LastAcceptedReportTimeSeconds > currentTimeSeconds) { return LatchStop( vehicleId, $"成员车{vehicleId}的主车接收时间晚于当前时间。"); } var reportAgeSeconds = currentTimeSeconds - status.LastAcceptedReportTimeSeconds; if (reportAgeSeconds > _communicationTimeoutSeconds) { return LatchStop( vehicleId, $"成员车{vehicleId}通信超时," + $"最近有效报告距今" + $"{reportAgeSeconds:F3}s。"); } if (status.IsFaulted || status.FailureCode != 0) { return LatchStop( vehicleId, $"成员车{vehicleId}报告故障," + $"故障码为{status.FailureCode}。"); } if (!status.IsStateAvailable) { return LatchStop( vehicleId, $"成员车{vehicleId}状态不可用。"); } } _latchedDecision = CreateContinueDecision(); return _latchedDecision; } // 结束当前任务并清除锁存;未开始新任务前Evaluate仍会要求停车。 public void Reset() { _activePlanId = 0; _latchedDecision = CreateContinueDecision(); } private FleetSafetyDecision LatchStop( int sourceVehicleId, string reason) { _latchedDecision = new FleetSafetyDecision( true, sourceVehicleId, reason); return _latchedDecision; } private static FleetSafetyDecision CreateContinueDecision() { return new FleetSafetyDecision( false, 0, string.Empty); } } }