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;
}
}
}