feat: add planning visualization contracts
This commit is contained in:
+101
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TrajectoryPlanningVisualization;
|
||||
|
||||
public sealed class PlanningVisualizationStaticSnapshot
|
||||
{
|
||||
public PlanningVisualizationStaticSnapshot(string sessionNameChinese, VisualizationBounds worldBounds,
|
||||
VisualizationOccupancyGrid occupancyGrid, IReadOnlyList<VisualizationPolyline> staticPolylines,
|
||||
IReadOnlyList<VisualizationMarker> staticMarkers,
|
||||
IReadOnlyList<VisualizationDirectionSegment> directionSegments,
|
||||
IReadOnlyList<VisualizationConfigurationGroup> configurationGroups)
|
||||
{
|
||||
SessionNameChinese = VisualizationContract.RequireText(sessionNameChinese, nameof(sessionNameChinese));
|
||||
WorldBounds = worldBounds ?? throw new ArgumentNullException(nameof(worldBounds));
|
||||
OccupancyGrid = occupancyGrid;
|
||||
StaticPolylines = VisualizationContract.Copy(staticPolylines, nameof(staticPolylines));
|
||||
StaticMarkers = VisualizationContract.Copy(staticMarkers, nameof(staticMarkers));
|
||||
DirectionSegments = VisualizationContract.Copy(directionSegments, nameof(directionSegments));
|
||||
ConfigurationGroups = VisualizationContract.Copy(configurationGroups, nameof(configurationGroups));
|
||||
}
|
||||
|
||||
public string SessionNameChinese { get; }
|
||||
public VisualizationBounds WorldBounds { get; }
|
||||
public VisualizationOccupancyGrid OccupancyGrid { get; }
|
||||
public IReadOnlyList<VisualizationPolyline> StaticPolylines { get; }
|
||||
public IReadOnlyList<VisualizationMarker> StaticMarkers { get; }
|
||||
public IReadOnlyList<VisualizationDirectionSegment> DirectionSegments { get; }
|
||||
public IReadOnlyList<VisualizationConfigurationGroup> ConfigurationGroups { get; }
|
||||
}
|
||||
|
||||
public sealed class PlanningVisualizationDynamicSnapshot
|
||||
{
|
||||
public PlanningVisualizationDynamicSnapshot(long sequence, DateTimeOffset observedAtUtc,
|
||||
string sessionStateChinese, int activeSegmentIndex, string activeDirection,
|
||||
VisualizationPose vehiclePose, IReadOnlyList<VisualizationPolyline> dynamicPolylines,
|
||||
IReadOnlyList<VisualizationMarker> dynamicMarkers, IReadOnlyList<VisualizationChart> charts,
|
||||
IReadOnlyList<VisualizationValue> statusValues, VisualizationCycleSummary cycleSummary)
|
||||
{
|
||||
Sequence = sequence;
|
||||
ObservedAtUtc = observedAtUtc;
|
||||
SessionStateChinese = VisualizationContract.RequireText(sessionStateChinese, nameof(sessionStateChinese));
|
||||
ActiveSegmentIndex = activeSegmentIndex;
|
||||
ActiveDirection = VisualizationContract.RequireText(activeDirection, nameof(activeDirection));
|
||||
VehiclePose = vehiclePose ?? throw new ArgumentNullException(nameof(vehiclePose));
|
||||
DynamicPolylines = VisualizationContract.Copy(dynamicPolylines, nameof(dynamicPolylines));
|
||||
DynamicMarkers = VisualizationContract.Copy(dynamicMarkers, nameof(dynamicMarkers));
|
||||
Charts = VisualizationContract.Copy(charts, nameof(charts));
|
||||
StatusValues = VisualizationContract.Copy(statusValues, nameof(statusValues));
|
||||
CycleSummary = cycleSummary ?? throw new ArgumentNullException(nameof(cycleSummary));
|
||||
}
|
||||
|
||||
public long Sequence { get; }
|
||||
public DateTimeOffset ObservedAtUtc { get; }
|
||||
public string SessionStateChinese { get; }
|
||||
public int ActiveSegmentIndex { get; }
|
||||
public string ActiveDirection { get; }
|
||||
public VisualizationPose VehiclePose { get; }
|
||||
public IReadOnlyList<VisualizationPolyline> DynamicPolylines { get; }
|
||||
public IReadOnlyList<VisualizationMarker> DynamicMarkers { get; }
|
||||
public IReadOnlyList<VisualizationChart> Charts { get; }
|
||||
public IReadOnlyList<VisualizationValue> StatusValues { get; }
|
||||
public VisualizationCycleSummary CycleSummary { get; }
|
||||
}
|
||||
|
||||
public sealed class VisualizationCycleSummary
|
||||
{
|
||||
public VisualizationCycleSummary(long cycleVersion, DateTimeOffset occurredAtUtc, string status, bool published,
|
||||
double planningElapsedMilliseconds, int segmentIndex, string direction, string longitudinalMode,
|
||||
string terminalType, double? terminalVelocity, double? terminalAcceleration, string failureReason)
|
||||
{
|
||||
VisualizationContract.RequireFinite(planningElapsedMilliseconds, nameof(planningElapsedMilliseconds));
|
||||
VisualizationContract.RequireFinite(terminalVelocity, nameof(terminalVelocity));
|
||||
VisualizationContract.RequireFinite(terminalAcceleration, nameof(terminalAcceleration));
|
||||
CycleVersion = cycleVersion;
|
||||
OccurredAtUtc = occurredAtUtc;
|
||||
Status = VisualizationContract.RequireText(status, nameof(status));
|
||||
Published = published;
|
||||
PlanningElapsedMilliseconds = planningElapsedMilliseconds;
|
||||
SegmentIndex = segmentIndex;
|
||||
Direction = VisualizationContract.RequireText(direction, nameof(direction));
|
||||
LongitudinalMode = VisualizationContract.RequireText(longitudinalMode, nameof(longitudinalMode));
|
||||
TerminalType = VisualizationContract.RequireText(terminalType, nameof(terminalType));
|
||||
TerminalVelocity = terminalVelocity;
|
||||
TerminalAcceleration = terminalAcceleration;
|
||||
FailureReason = VisualizationContract.RequireText(failureReason, nameof(failureReason));
|
||||
}
|
||||
|
||||
public long CycleVersion { get; }
|
||||
public DateTimeOffset OccurredAtUtc { get; }
|
||||
public string Status { get; }
|
||||
public bool Published { get; }
|
||||
public double PlanningElapsedMilliseconds { get; }
|
||||
public int SegmentIndex { get; }
|
||||
public string Direction { get; }
|
||||
public string LongitudinalMode { get; }
|
||||
public string TerminalType { get; }
|
||||
public double? TerminalVelocity { get; }
|
||||
public double? TerminalAcceleration { get; }
|
||||
public string FailureReason { get; }
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TrajectoryPlanningVisualization;
|
||||
|
||||
public enum VisualizationLineStyle
|
||||
{
|
||||
Solid,
|
||||
Dashed,
|
||||
Limit
|
||||
}
|
||||
|
||||
public sealed class VisualizationSeries
|
||||
{
|
||||
public VisualizationSeries(string id, string legend, VisualizationLineStyle lineStyle,
|
||||
IReadOnlyList<VisualizationPoint> points)
|
||||
{
|
||||
Id = VisualizationContract.RequireText(id, nameof(id));
|
||||
Legend = VisualizationContract.RequireText(legend, nameof(legend));
|
||||
LineStyle = lineStyle;
|
||||
Points = VisualizationContract.Copy(points, nameof(points));
|
||||
}
|
||||
|
||||
public string Id { get; }
|
||||
public string Legend { get; }
|
||||
public VisualizationLineStyle LineStyle { get; }
|
||||
public IReadOnlyList<VisualizationPoint> Points { get; }
|
||||
}
|
||||
|
||||
public sealed class VisualizationChart
|
||||
{
|
||||
public VisualizationChart(string id, string chineseTitle, string xAxisLabel, string yAxisLabel,
|
||||
IReadOnlyList<VisualizationSeries> series, string noteChinese = "")
|
||||
{
|
||||
Id = VisualizationContract.RequireText(id, nameof(id));
|
||||
ChineseTitle = VisualizationContract.RequireText(chineseTitle, nameof(chineseTitle));
|
||||
XAxisLabel = VisualizationContract.RequireText(xAxisLabel, nameof(xAxisLabel));
|
||||
YAxisLabel = VisualizationContract.RequireText(yAxisLabel, nameof(yAxisLabel));
|
||||
Series = VisualizationContract.Copy(series, nameof(series));
|
||||
NoteChinese = VisualizationContract.RequireText(noteChinese, nameof(noteChinese));
|
||||
}
|
||||
|
||||
public string Id { get; }
|
||||
public string ChineseTitle { get; }
|
||||
public string XAxisLabel { get; }
|
||||
public string YAxisLabel { get; }
|
||||
public IReadOnlyList<VisualizationSeries> Series { get; }
|
||||
public string NoteChinese { get; }
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TrajectoryPlanningVisualization;
|
||||
|
||||
public sealed class VisualizationPolyline
|
||||
{
|
||||
public VisualizationPolyline(string id, string legendChinese, string kind, VisualizationLineStyle lineStyle,
|
||||
IReadOnlyList<VisualizationPoint> points)
|
||||
{
|
||||
Id = VisualizationContract.RequireText(id, nameof(id));
|
||||
LegendChinese = VisualizationContract.RequireText(legendChinese, nameof(legendChinese));
|
||||
Kind = VisualizationContract.RequireText(kind, nameof(kind));
|
||||
LineStyle = lineStyle;
|
||||
Points = VisualizationContract.Copy(points, nameof(points));
|
||||
}
|
||||
|
||||
public string Id { get; }
|
||||
public string LegendChinese { get; }
|
||||
public string Kind { get; }
|
||||
public VisualizationLineStyle LineStyle { get; }
|
||||
public IReadOnlyList<VisualizationPoint> Points { get; }
|
||||
}
|
||||
|
||||
public sealed class VisualizationMarker
|
||||
{
|
||||
public VisualizationMarker(string id, string kind, string labelChinese, VisualizationPoint position)
|
||||
{
|
||||
Id = VisualizationContract.RequireText(id, nameof(id));
|
||||
Kind = VisualizationContract.RequireText(kind, nameof(kind));
|
||||
LabelChinese = VisualizationContract.RequireText(labelChinese, nameof(labelChinese));
|
||||
Position = position ?? throw new ArgumentNullException(nameof(position));
|
||||
}
|
||||
|
||||
public string Id { get; }
|
||||
public string Kind { get; }
|
||||
public string LabelChinese { get; }
|
||||
public VisualizationPoint Position { get; }
|
||||
}
|
||||
|
||||
public sealed class VisualizationDirectionSegment
|
||||
{
|
||||
public VisualizationDirectionSegment(int segmentIndex, string direction, bool startsAtGearSwitch,
|
||||
bool endsAtGearSwitch, IReadOnlyList<VisualizationPoint> points)
|
||||
{
|
||||
SegmentIndex = segmentIndex;
|
||||
Direction = VisualizationContract.RequireText(direction, nameof(direction));
|
||||
StartsAtGearSwitch = startsAtGearSwitch;
|
||||
EndsAtGearSwitch = endsAtGearSwitch;
|
||||
Points = VisualizationContract.Copy(points, nameof(points));
|
||||
}
|
||||
|
||||
public int SegmentIndex { get; }
|
||||
public string Direction { get; }
|
||||
public bool StartsAtGearSwitch { get; }
|
||||
public bool EndsAtGearSwitch { get; }
|
||||
public IReadOnlyList<VisualizationPoint> Points { get; }
|
||||
}
|
||||
|
||||
public sealed class VisualizationOccupancyGrid
|
||||
{
|
||||
public VisualizationOccupancyGrid(VisualizationBounds bounds, double resolutionMeters, int rows, int columns,
|
||||
byte[] occupancyBits)
|
||||
{
|
||||
Bounds = bounds ?? throw new ArgumentNullException(nameof(bounds));
|
||||
VisualizationContract.RequireFinite(resolutionMeters, nameof(resolutionMeters));
|
||||
if (resolutionMeters <= 0d)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(resolutionMeters));
|
||||
}
|
||||
|
||||
if (rows <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(rows));
|
||||
}
|
||||
|
||||
if (columns <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(columns));
|
||||
}
|
||||
|
||||
if (occupancyBits == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(occupancyBits));
|
||||
}
|
||||
|
||||
long cellCount = (long)rows * columns;
|
||||
long requiredByteCount = (cellCount + 7L) / 8L;
|
||||
if (occupancyBits.Length != requiredByteCount)
|
||||
{
|
||||
throw new ArgumentException("Occupancy bit count does not match the grid dimensions.", nameof(occupancyBits));
|
||||
}
|
||||
|
||||
var bitsCopy = new byte[occupancyBits.Length];
|
||||
Buffer.BlockCopy(occupancyBits, 0, bitsCopy, 0, bitsCopy.Length);
|
||||
ResolutionMeters = resolutionMeters;
|
||||
Rows = rows;
|
||||
Columns = columns;
|
||||
OccupancyBitsBase64 = Convert.ToBase64String(bitsCopy);
|
||||
}
|
||||
|
||||
public VisualizationBounds Bounds { get; }
|
||||
public double ResolutionMeters { get; }
|
||||
public int Rows { get; }
|
||||
public int Columns { get; }
|
||||
public string OccupancyBitsBase64 { get; }
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace TrajectoryPlanningVisualization;
|
||||
|
||||
public sealed class VisualizationPoint
|
||||
{
|
||||
public VisualizationPoint(double x, double y)
|
||||
{
|
||||
VisualizationContract.RequireFinite(x, nameof(x));
|
||||
VisualizationContract.RequireFinite(y, nameof(y));
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
|
||||
public double X { get; }
|
||||
public double Y { get; }
|
||||
}
|
||||
|
||||
public sealed class VisualizationPose
|
||||
{
|
||||
public VisualizationPose(double x, double y, double headingRadians)
|
||||
{
|
||||
VisualizationContract.RequireFinite(x, nameof(x));
|
||||
VisualizationContract.RequireFinite(y, nameof(y));
|
||||
VisualizationContract.RequireFinite(headingRadians, nameof(headingRadians));
|
||||
X = x;
|
||||
Y = y;
|
||||
HeadingRadians = headingRadians;
|
||||
}
|
||||
|
||||
public double X { get; }
|
||||
public double Y { get; }
|
||||
public double HeadingRadians { get; }
|
||||
}
|
||||
|
||||
public sealed class VisualizationBounds
|
||||
{
|
||||
public VisualizationBounds(double xMin, double xMax, double yMin, double yMax)
|
||||
{
|
||||
VisualizationContract.RequireFinite(xMin, nameof(xMin));
|
||||
VisualizationContract.RequireFinite(xMax, nameof(xMax));
|
||||
VisualizationContract.RequireFinite(yMin, nameof(yMin));
|
||||
VisualizationContract.RequireFinite(yMax, nameof(yMax));
|
||||
if (xMax < xMin || yMax < yMin)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(xMax), "Bounds maxima must not precede minima.");
|
||||
}
|
||||
|
||||
XMin = xMin;
|
||||
XMax = xMax;
|
||||
YMin = yMin;
|
||||
YMax = yMax;
|
||||
}
|
||||
|
||||
public double XMin { get; }
|
||||
public double XMax { get; }
|
||||
public double YMin { get; }
|
||||
public double YMax { get; }
|
||||
}
|
||||
|
||||
public sealed class VisualizationValue
|
||||
{
|
||||
public VisualizationValue(string chineseName, string rawName, string value, string unit, string severity)
|
||||
{
|
||||
ChineseName = VisualizationContract.RequireText(chineseName, nameof(chineseName));
|
||||
RawName = VisualizationContract.RequireText(rawName, nameof(rawName));
|
||||
Value = VisualizationContract.RequireText(value, nameof(value));
|
||||
Unit = VisualizationContract.RequireText(unit, nameof(unit));
|
||||
if (severity != "normal" && severity != "notice" && severity != "failure")
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(severity), "Severity must be normal, notice, or failure.");
|
||||
}
|
||||
|
||||
Severity = severity;
|
||||
}
|
||||
|
||||
public string ChineseName { get; }
|
||||
public string RawName { get; }
|
||||
public string Value { get; }
|
||||
public string Unit { get; }
|
||||
public string Severity { get; }
|
||||
}
|
||||
|
||||
public sealed class VisualizationConfigurationGroup
|
||||
{
|
||||
public VisualizationConfigurationGroup(string chineseTitle, IReadOnlyList<VisualizationValue> entries)
|
||||
{
|
||||
ChineseTitle = VisualizationContract.RequireText(chineseTitle, nameof(chineseTitle));
|
||||
Entries = VisualizationContract.Copy(entries, nameof(entries));
|
||||
}
|
||||
|
||||
public string ChineseTitle { get; }
|
||||
public IReadOnlyList<VisualizationValue> Entries { get; }
|
||||
}
|
||||
|
||||
internal static class VisualizationContract
|
||||
{
|
||||
public static string RequireText(string value, string parameterName)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(parameterName);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static void RequireFinite(double value, string parameterName)
|
||||
{
|
||||
if (double.IsNaN(value) || double.IsInfinity(value))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(parameterName, "Value must be finite.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void RequireFinite(double? value, string parameterName)
|
||||
{
|
||||
if (value.HasValue)
|
||||
{
|
||||
RequireFinite(value.Value, parameterName);
|
||||
}
|
||||
}
|
||||
|
||||
public static IReadOnlyList<T> Copy<T>(IReadOnlyList<T> values, string parameterName)
|
||||
{
|
||||
if (values == null)
|
||||
{
|
||||
throw new ArgumentNullException(parameterName);
|
||||
}
|
||||
|
||||
var copy = new List<T>(values.Count);
|
||||
for (var index = 0; index < values.Count; index++)
|
||||
{
|
||||
T value = values[index];
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentException("Collections cannot contain null elements.", parameterName);
|
||||
}
|
||||
|
||||
copy.Add(value);
|
||||
}
|
||||
|
||||
return new ReadOnlyCollection<T>(copy);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user