using System; using System.Collections.Generic; using System.Collections.ObjectModel; using MyParking.Shared; // 负责运动前:所有车辆舵轮是否准备完成 namespace MultiWheelC.Fleet { /// 表示主车侧车队运动准备的当前阶段。 public enum FleetPreparationCoordinatorState { Idle = 0, WaitingForMembers = 1, ReadyToActivate = 2, ActivationAuthorized = 3, Faulted = 4 } /// 保存一次滚动准备中分配给指定成员车的本地β目标。 public readonly struct FleetMemberPreparationTarget { /// 创建一条属于指定任务和成员车的准备目标。 public FleetMemberPreparationTarget( long planId, int vehicleId, double motionDirectionInBodyRadians) { if (planId <= 0) { throw new ArgumentOutOfRangeException( nameof(planId), "车队动作任务编号必须大于零。"); } if (vehicleId <= 0) { throw new ArgumentOutOfRangeException( nameof(vehicleId), "成员车号必须大于零。"); } NumericGuard.EnsureFinite( motionDirectionInBodyRadians, nameof(motionDirectionInBodyRadians)); PlanId = planId; VehicleId = vehicleId; MotionDirectionInBodyRadians = AngleMath.NormalizeRadians( motionDirectionInBodyRadians); } public long PlanId { get; } public int VehicleId { get; } public double MotionDirectionInBodyRadians { get; } } /// 保存成员车对某次准备任务上报的本地状态。 public readonly struct FleetMemberPreparationStatus { /// 创建一条成员车准备状态报告。 public FleetMemberPreparationStatus( long planId, int vehicleId, FleetMemberAgentState state, string failureReason = "") { if (planId <= 0) { throw new ArgumentOutOfRangeException( nameof(planId), "车队动作任务编号必须大于零。"); } if (vehicleId <= 0) { throw new ArgumentOutOfRangeException( nameof(vehicleId), "成员车号必须大于零。"); } if (!Enum.IsDefined( typeof(FleetMemberAgentState), state)) { throw new ArgumentOutOfRangeException( nameof(state), "成员车准备状态无效。"); } PlanId = planId; VehicleId = vehicleId; State = state; FailureReason = failureReason ?? string.Empty; } public long PlanId { get; } public int VehicleId { get; } public FleetMemberAgentState State { get; } public string FailureReason { get; } } /// 在主车侧分配成员β并管理全队Ready统一激活屏障。 public sealed class FleetPreparationCoordinator { private static readonly IReadOnlyList< FleetMemberPreparationTarget> EmptyTargets = Array.AsReadOnly( Array.Empty()); private readonly Dictionary _memberStates = new Dictionary(); private IReadOnlyList _targets = EmptyTargets; /// 创建尚未激活准备任务的主车侧协调器。 public FleetPreparationCoordinator() { State = FleetPreparationCoordinatorState.Idle; LastFailureReason = string.Empty; } public FleetPreparationCoordinatorState State { get; private set; } public long CurrentPlanId { get; private set; } public double MotionDirectionInFleetRadians { get; private set; } public IReadOnlyList Targets => _targets; public string LastFailureReason { get; private set; } /// 根据车队固定布局为全部成员建立本次滚动β准备目标。 public void StartRollingPreparation( long planId, FleetLayout layout, double motionDirectionInFleetRadians) { if (planId <= 0) { throw new ArgumentOutOfRangeException( nameof(planId), "车队动作任务编号必须大于零。"); } if (layout == null) { throw new ArgumentNullException(nameof(layout)); } NumericGuard.EnsureFinite( motionDirectionInFleetRadians, nameof(motionDirectionInFleetRadians)); var normalizedFleetDirection = AngleMath.NormalizeRadians( motionDirectionInFleetRadians); var targets = new FleetMemberPreparationTarget[ layout.VehicleCount]; _memberStates.Clear(); for (var index = 0; index < layout.Vehicles.Count; index++) { var vehicle = layout.Vehicles[index]; var rawDirectionInBody = AngleMath.NormalizeRadians( normalizedFleetDirection - vehicle.PoseInFleet.YawRadians); var equivalentDirectionInBody = SelectSteeringAxisEquivalent( rawDirectionInBody); targets[index] = new FleetMemberPreparationTarget( planId, vehicle.VehicleId, equivalentDirectionInBody); _memberStates.Add( vehicle.VehicleId, FleetMemberAgentState.Idle); } CurrentPlanId = planId; MotionDirectionInFleetRadians = normalizedFleetDirection; _targets = Array.AsReadOnly(targets); State = FleetPreparationCoordinatorState .WaitingForMembers; LastFailureReason = string.Empty; } /// 接收一辆成员车的状态并重新计算全队Ready状态。 public FleetPreparationCoordinatorState ReportMemberStatus( FleetMemberPreparationStatus status) { if (State == FleetPreparationCoordinatorState.Idle || State == FleetPreparationCoordinatorState.Faulted) { return State; } if (status.PlanId != CurrentPlanId) { return State; } if (!_memberStates.ContainsKey(status.VehicleId)) { throw new ArgumentException( $"车辆{status.VehicleId}不属于当前车队布局。", nameof(status)); } if (status.State == FleetMemberAgentState.Faulted) { return Fail( string.IsNullOrWhiteSpace( status.FailureReason) ? $"车辆{status.VehicleId}准备失败。" : $"车辆{status.VehicleId}准备失败:" + status.FailureReason); } if (State == FleetPreparationCoordinatorState .ActivationAuthorized) { return State; } if (status.State == FleetMemberAgentState.Active) { return Fail( $"车辆{status.VehicleId}在全队统一激活前已经进入Active。"); } _memberStates[status.VehicleId] = status.State; State = AreAllMembersReady() ? FleetPreparationCoordinatorState .ReadyToActivate : FleetPreparationCoordinatorState .WaitingForMembers; LastFailureReason = string.Empty; return State; } /// 在全部成员Ready后授权外层向全队广播同一任务的激活命令。 public bool TryAuthorizeActivation(long planId) { if (planId != CurrentPlanId) { return false; } if (State == FleetPreparationCoordinatorState .ActivationAuthorized) { return true; } if (State != FleetPreparationCoordinatorState .ReadyToActivate) { return false; } State = FleetPreparationCoordinatorState .ActivationAuthorized; LastFailureReason = string.Empty; return true; } /// 查找指定成员车在当前任务中的本地β准备目标。 public bool TryGetTarget( int vehicleId, out FleetMemberPreparationTarget target) { for (var index = 0; index < _targets.Count; index++) { if (_targets[index].VehicleId == vehicleId) { target = _targets[index]; return true; } } target = default; return false; } /// 取消当前准备任务并清除成员状态和β目标。 public void Cancel() { _memberStates.Clear(); _targets = EmptyTargets; CurrentPlanId = 0; MotionDirectionInFleetRadians = 0.0; State = FleetPreparationCoordinatorState.Idle; LastFailureReason = string.Empty; } /// 判断当前任务中的每辆成员车是否都已报告Ready。 private bool AreAllMembersReady() { foreach (var state in _memberStates.Values) { if (state != FleetMemberAgentState.Ready) { return false; } } return _memberStates.Count > 0; } /// 将有向β转换为±90°内的等效滚动轴,反向运动由轮速符号表达。 private static double SelectSteeringAxisEquivalent( double directionRadians) { var equivalent = AngleMath.NormalizeRadians( directionRadians); if (equivalent > Math.PI / 2.0) { equivalent -= Math.PI; } else if (equivalent < -Math.PI / 2.0) { equivalent += Math.PI; } return AngleMath.NormalizeRadians(equivalent); } /// 锁存准备故障,等待外层停止所有成员并取消任务。 private FleetPreparationCoordinatorState Fail( string reason) { State = FleetPreparationCoordinatorState.Faulted; LastFailureReason = reason ?? string.Empty; return State; } } }