using System; namespace MultiWheelC.TrajectoryPlanning.EMPlanner; /// 绑定一次滚动周期的不可变版本身份;所有字段必须同时匹配,旧结果才可发布。 public sealed class PlanningCycleIdentity : IEquatable { /// 创建完整周期身份。 /// 规划地图快照的非负版本 ID。 /// 平滑参考路径的非空版本 ID。 /// 调用方捕获的车辆状态非负序列号。 /// 上一条轨迹 ID;没有时使用空字符串。 /// 当前前进或倒车方向段的非负索引。 public PlanningCycleIdentity(long mapSnapshotId, string referencePathId, long vehicleStateSequenceId, string previousTrajectoryId, int segmentIndex) { if (mapSnapshotId < 0) throw new ArgumentOutOfRangeException(nameof(mapSnapshotId)); if (string.IsNullOrWhiteSpace(referencePathId)) throw new ArgumentException("A reference path ID is required.", nameof(referencePathId)); if (vehicleStateSequenceId < 0) throw new ArgumentOutOfRangeException(nameof(vehicleStateSequenceId)); if (segmentIndex < 0) throw new ArgumentOutOfRangeException(nameof(segmentIndex)); MapSnapshotId = mapSnapshotId; ReferencePathId = referencePathId; VehicleStateSequenceId = vehicleStateSequenceId; PreviousTrajectoryId = previousTrajectoryId ?? string.Empty; SegmentIndex = segmentIndex; } /// 规划地图快照版本 ID。 public long MapSnapshotId { get; } public string ReferencePathId { get; } public long VehicleStateSequenceId { get; } public string PreviousTrajectoryId { get; } public int SegmentIndex { get; } /// 从已冻结的 EM 请求派生发布身份。 /// 必须携带地图和车辆状态快照的 EM 请求。 /// 包含请求地图、路径、状态、上一轨迹和方向段字段的不可变身份。 public static PlanningCycleIdentity FromRequest(EmPlanningRequest request) { if (request == null) throw new ArgumentNullException(nameof(request)); if (request.Map == null) throw new ArgumentException("A planning map is required for a rolling cycle.", nameof(request)); if (request.VehicleState == null) throw new ArgumentException("A vehicle state is required for a rolling cycle.", nameof(request)); return new PlanningCycleIdentity(request.Map.SnapshotId, request.ReferencePathId, request.VehicleState.SequenceId, request.PreviousTrajectoryId, request.SegmentIndex); } public bool Equals(PlanningCycleIdentity other) { return other != null && MapSnapshotId == other.MapSnapshotId && string.Equals(ReferencePathId, other.ReferencePathId, StringComparison.Ordinal) && VehicleStateSequenceId == other.VehicleStateSequenceId && string.Equals(PreviousTrajectoryId, other.PreviousTrajectoryId, StringComparison.Ordinal) && SegmentIndex == other.SegmentIndex; } public override bool Equals(object obj) { return Equals(obj as PlanningCycleIdentity); } public override int GetHashCode() { unchecked { int hash = MapSnapshotId.GetHashCode(); hash = (hash * 397) ^ StringComparer.Ordinal.GetHashCode(ReferencePathId); hash = (hash * 397) ^ VehicleStateSequenceId.GetHashCode(); hash = (hash * 397) ^ StringComparer.Ordinal.GetHashCode(PreviousTrajectoryId); return (hash * 397) ^ SegmentIndex; } } }