using System; namespace MultiWheelC.TrajectoryPlanning.EMPlanner; /// Immutable identity bound to one rolling planning cycle. public sealed class PlanningCycleIdentity : IEquatable { 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; } public long MapSnapshotId { get; } public string ReferencePathId { get; } public long VehicleStateSequenceId { get; } public string PreviousTrajectoryId { get; } public int SegmentIndex { get; } 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; } } }