UI/TelemetryPanel.razor

Razor UI component that renders a telemetry overlay for a vehicle. It displays timing, last-run verdict and metrics, steer/pedals/grip indicators, and a sliding-window slip/angle graph sampled periodically from a VehicleController target.

File Access
@using Sandbox;
@using Sandbox.UI;
@inherits PanelComponent
@namespace VehicleProto

@* Instrumented overlay (design board 1b) — toggled by L ("Telemetry"). Read-only, no cursor.
   Rides a LETTER key: the standalone/published s&box client captures F1–F12 for its own
   overlays, so the old F4 bind toggled nothing outside the editor (same host-capture trap that
   put Help on I, World on M).
   Timing + run verdict come from UiFeed (harness-fed); pedals/steer/grip/slip are live. *@

<root>
	@if ( UiState.TelemetryOpen && Target is not null )
	{
		<div class="timing @(UiFeed.TimingRunning ? "running" : "")">
			<div class="thead">
				<div class="tt"><div class="dot @(UiFeed.TimingRunning ? "" : "idle")"></div><div class="tlabel">@UiFeed.TimingTitle</div></div>
				<div class="mono ctx">@UiFeed.TimingContext</div>
			</div>
			<div class="tvalrow">
				<div class="mono tval">@UiFeed.TimingValue<span class="tunit"> @UiFeed.TimingUnit</span></div>
				@if ( !string.IsNullOrEmpty( UiFeed.TimingBest ) )
				{
					<div class="mono tbest">@UiFeed.TimingBest</div>
				}
			</div>
			<div class="tfoot">
				<div class="mono">@UiFeed.TimingExtra1</div>
				<div class="mono">@UiFeed.TimingExtra2</div>
				<div class="mono">@UiFeed.TimingExtra3</div>
			</div>
		</div>

		@if ( UiFeed.HasLastRun )
		{
			<div class="verdict">
				<div class="vhead">
					<div class="vtitle">
						<div class="vkicker">last run</div>
						<div class="vname">@UiFeed.LastRunTitle</div>
					</div>
					<div class="mono badge @VerdictClass">@VerdictText</div>
				</div>
				<div class="rule"></div>
				<div class="metrics">
					@foreach ( var m in UiFeed.LastRunMetrics )
					{
						<div class="metric">
							<div class="mrow"><div class="mlbl">@m.Label</div><div class="mono mval @(m.Pass ? "pass" : "fail")">@m.Value</div></div>
							@if ( m.HasBar )
							{
								<div class="mbar">
									<div class="band @(m.Pass ? "pass" : "fail")" style="left: @UiFmt.Pct( m.BandLo * 100f )%; width: @UiFmt.Pct( (m.BandHi - m.BandLo) * 100f )%"></div>
									<div class="mark @(m.Pass ? "pass" : "fail")" style="left: @UiFmt.Pct( m.Frac * 100f )%"></div>
								</div>
								<div class="mono mband">@m.Band</div>
							}
						</div>
					}
					@if ( !string.IsNullOrEmpty( UiFeed.LastRunYaw ) )
					{
						<div class="mrow"><div class="mlbl">Yaw stability</div><div class="mono mval pass">@UiFeed.LastRunYaw</div></div>
					}
					@if ( !string.IsNullOrEmpty( UiFeed.LastRunEvents ) )
					{
						<div class="mrow"><div class="mlbl">Assist events</div><div class="mono mval">@UiFeed.LastRunEvents</div></div>
					}
				</div>
				@if ( !string.IsNullOrEmpty( UiFeed.LastRunLine ) )
				{
					<div class="mono runline">@UiFeed.LastRunLine</div>
				}
			</div>
		}

		<div class="detail">
			<div class="card steer">
				<div class="dhead"><div class="lbl">Steer</div><div class="mono sval">@SteerText</div></div>
				<div class="strack">
					<div class="scenter"></div>
					<div class="smark" style="left: @UiFmt.Pct( 48f + (Target?.Steer ?? 0f) * 44f )%"></div>
				</div>
			</div>

			<div class="card pedals">
				<div class="ped">
					<div class="bar"><div class="fill thr" style="height: @UiFmt.Pct( (Target?.Throttle ?? 0f) * 100f )%"></div></div>
					<div class="plbl">thr</div>
				</div>
				<div class="ped">
					<div class="bar @(BrakeAbs ? "absbar" : "")"><div class="fill brk @(BrakeAbs ? "abs" : "")" style="height: @UiFmt.Pct( (Target?.Brake ?? 0f) * 100f )%"></div></div>
					<div class="plbl @(BrakeAbs ? "hot" : "")">brk</div>
				</div>
				<div class="ped">
					<div class="bar"><div class="fill hbk" style="height: @UiFmt.Pct( Handbrake ? 100f : 0f )%"></div></div>
					<div class="plbl @(Handbrake ? "on" : "")">hbk</div>
				</div>
			</div>

			<div class="card gripcard">
				<div class="gplan">
					<div class="axle">
						<div class="wcell"><div class="wchip @WheelState( 0 )"></div><div class="mono wlbl">FL</div></div>
						<div class="wcell"><div class="wchip @WheelState( 1 )"></div><div class="mono wlbl">FR</div></div>
					</div>
					<div class="body"></div>
					<div class="axle">
						<div class="wcell"><div class="wchip @WheelState( 2 )"></div><div class="mono wlbl">RL</div></div>
						<div class="wcell"><div class="wchip @WheelState( 3 )"></div><div class="mono wlbl">RR</div></div>
					</div>
				</div>
				<div class="gcap">grip</div>
			</div>
		</div>

		<div class="slip">
			<div class="shead">
				<div class="skicker">slip · 10 s</div>
				<div class="leg">
					<div class="li"><span class="d cyan"></span>ratio</div>
					<div class="li"><span class="d amber"></span>angle</div>
				</div>
			</div>
			<div class="graph">
				<div class="mid"></div>
				@foreach ( var s in _samples )
				{
					<div class="col">
						<div class="up" style="height: @UiFmt.Pct( s.Ratio * 46f )%"></div>
						<div class="dn" style="height: @UiFmt.Pct( s.Angle * 46f )%"></div>
					</div>
				}
			</div>
		</div>
	}
</root>

@code {
	public VehicleController Target { get; set; }

	struct Sample { public float Ratio; public float Angle; }
	readonly List<Sample> _samples = new();
	const int MaxSamples = 50;
	TimeSince _sinceSample;
	int _pushCount;

	bool Handbrake => Target?.Handbrake ?? false;
	string SteerText => (Target?.Steer ?? 0f).ToString( "F2" );

	// ABS is inferred: braking hard while a front wheel is locking under a non-Sim assist
	bool BrakeAbs
	{
		get
		{
			if ( Target is null || (Target.Brake) < 0.1f || Target.Assists == AssistLevel.Sim ) return false;
			foreach ( var w in Target.Wheels )
				if ( w.IsSteering && w.IsGrounded && w.SlipRatio < -0.3f ) return true;
			return false;
		}
	}

	string VerdictText => UiFeed.LastRunOutOfBand ? "OUT OF BAND" : UiFeed.LastRunPass ? "PASS" : "FAIL";
	string VerdictClass => UiFeed.LastRunOutOfBand ? "warn" : UiFeed.LastRunPass ? "pass" : "fail";

	string WheelState( int i )
	{
		if ( Target is null || i >= Target.Wheels.Count ) return "air";
		var w = Target.Wheels[i];
		if ( !w.IsGrounded ) return "air";
		float slip = MathF.Max( MathF.Abs( w.SlipRatio ), MathF.Abs( w.SlipAngle ) * 2f );
		return slip > 0.5f ? "sliding" : slip > 0.15f ? "working" : "grip";
	}

	protected override void OnUpdate()
	{
		if ( Input.Pressed( "Telemetry" ) )
			UiState.TelemetryOpen = !UiState.TelemetryOpen;

		if ( !UiState.TelemetryOpen || Target is null )
			return;

		if ( _sinceSample > 0.2f )
		{
			_sinceSample = 0;
			var fronts = Target.Wheels.Where( w => w.IsSteering && w.IsGrounded ).ToList();
			float ratio = fronts.Count > 0 ? MathF.Abs( (float)fronts.Average( w => w.SlipRatio ) ) : 0f;
			float angle = fronts.Count > 0 ? MathF.Abs( (float)fronts.Average( w => w.SlipAngle ) ) * 3f : 0f;
			_samples.Add( new Sample { Ratio = Math.Clamp( ratio, 0f, 1f ), Angle = Math.Clamp( angle, 0f, 1f ) } );
			if ( _samples.Count > MaxSamples ) _samples.RemoveAt( 0 );
			_pushCount++;
		}
	}

	protected override int BuildHash()
	{
		if ( !UiState.TelemetryOpen )
			return 0;
		return System.HashCode.Combine(
			(int)((Target?.Throttle ?? 0f) * 20f), (int)((Target?.Brake ?? 0f) * 20f),
			(int)((Target?.Steer ?? 0f) * 40f), Handbrake, BrakeAbs,
			Target?.Wheels.Sum( w => (int)(MathF.Abs( w.SlipRatio ) * 10f) + (w.IsGrounded ? 100 : 0) ) ?? 0,
			_pushCount, UiFeed.RunHash() );
	}
}