216 lines
6.1 KiB
Plaintext
216 lines
6.1 KiB
Plaintext
@page "/lidar"
|
|
@using RobotNet10.RobotApp.Devices
|
|
@using RobotNet10.Shared
|
|
@using RobotNet10.RobotApp.Client.Shared.Devices
|
|
@rendermode InteractiveServer
|
|
@attribute [Authorize]
|
|
@inject IDeviceProvider DeviceProvider
|
|
|
|
<PageTitle>Tim781S LiDAR Visualization</PageTitle>
|
|
|
|
<style>
|
|
.lidar-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: 100vh;
|
|
background: #1a1a1a;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.lidar-header {
|
|
padding: 20px;
|
|
background: #2a2a2a;
|
|
border-bottom: 2px solid #3a3a3a;
|
|
}
|
|
|
|
.lidar-stats {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 15px;
|
|
margin-top: 15px;
|
|
}
|
|
|
|
.stat-card {
|
|
background: #3a3a3a;
|
|
padding: 15px;
|
|
border-radius: 8px;
|
|
border-left: 4px solid #00ff88;
|
|
}
|
|
|
|
.stat-label {
|
|
font-size: 12px;
|
|
color: #888;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.stat-value {
|
|
font-size: 24px;
|
|
font-weight: bold;
|
|
color: #00ff88;
|
|
}
|
|
|
|
.canvas-container {
|
|
flex: 1;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
position: relative;
|
|
}
|
|
|
|
#lidarCanvas {
|
|
border: 2px solid #3a3a3a;
|
|
background: #0a0a0a;
|
|
}
|
|
|
|
.status-indicator {
|
|
display: inline-block;
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 50%;
|
|
margin-right: 8px;
|
|
}
|
|
|
|
.status-connected {
|
|
background: #00ff88;
|
|
box-shadow: 0 0 10px #00ff88;
|
|
}
|
|
|
|
.status-disconnected {
|
|
background: #ff4444;
|
|
}
|
|
</style>
|
|
|
|
<div class="lidar-container">
|
|
<div class="lidar-header">
|
|
<h1>
|
|
<span class="status-indicator @(isConnected ? "status-connected" : "status-disconnected")"></span>
|
|
Tim781S LiDAR Visualization
|
|
</h1>
|
|
|
|
<div class="lidar-stats">
|
|
<div class="stat-card">
|
|
<div class="stat-label">Scan Frequency</div>
|
|
<div class="stat-value">@scanFrequency Hz</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-label">Points Per Scan</div>
|
|
<div class="stat-value">@pointCount</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-label">Min Range</div>
|
|
<div class="stat-value">@minRange.ToString("F2") m</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-label">Max Range</div>
|
|
<div class="stat-value">@maxRange.ToString("F2") m</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-label">Valid Points</div>
|
|
<div class="stat-value">@validPoints</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="canvas-container">
|
|
<canvas id="lidarCanvas" width="800" height="800"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
@code {
|
|
private IJSRuntime? JS { get; set; }
|
|
private bool isConnected = false;
|
|
private int scanFrequency = 0;
|
|
private int pointCount = 0;
|
|
private float minRange = 0.0f;
|
|
private float maxRange = 0.0f;
|
|
private int validPoints = 0;
|
|
private ILidar? lidar;
|
|
private System.Threading.Timer? updateTimer;
|
|
|
|
[Inject]
|
|
public required IJSRuntime JSRuntime { get; set; }
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
JS = JSRuntime;
|
|
|
|
// Find Tim781S LiDAR device
|
|
var devices = DeviceProvider.GetDevicesByType(DeviceType.Lidar).OfType<ILidar>();
|
|
lidar = devices.FirstOrDefault(d => (d as DeviceBase)?.DeviceId.Contains("tim781s", StringComparison.OrdinalIgnoreCase) == true);
|
|
|
|
if (lidar != null)
|
|
{
|
|
lidar.ScanDataReceived += OnScanDataReceived;
|
|
var deviceBase = lidar as DeviceBase;
|
|
isConnected = deviceBase?.Status == DeviceStatus.Connected;
|
|
}
|
|
|
|
// Start update timer for stats
|
|
updateTimer = new System.Threading.Timer(async _ =>
|
|
{
|
|
await InvokeAsync(StateHasChanged);
|
|
}, null, TimeSpan.Zero, TimeSpan.FromMilliseconds(10));
|
|
}
|
|
|
|
private async void OnScanDataReceived(object? sender, LidarScanDataEventArgs e)
|
|
{
|
|
if (JS == null) return;
|
|
|
|
var scan = e.MeasurementData;
|
|
|
|
// Update stats
|
|
pointCount = scan.Ranges.Length;
|
|
minRange = (float)scan.RangeMin;
|
|
maxRange = (float)scan.RangeMax;
|
|
validPoints = scan.Ranges.Count(r => r >= scan.RangeMin && r <= scan.RangeMax);
|
|
|
|
// Calculate scan frequency (simple moving average)
|
|
if (lidar != null && lidar.ScanFrequencyHz.HasValue)
|
|
{
|
|
scanFrequency = (int)lidar.ScanFrequencyHz.Value;
|
|
}
|
|
|
|
// Prepare data for JavaScript visualization
|
|
var points = new List<object>();
|
|
for (int i = 0; i < scan.Ranges.Length; i++)
|
|
{
|
|
float angle = (float)(scan.AngleMin + (i * scan.AngleIncrement));
|
|
float range = (float)scan.Ranges[i];
|
|
|
|
// Only add valid points
|
|
if (range >= scan.RangeMin && range <= scan.RangeMax)
|
|
{
|
|
points.Add(new { angle, range });
|
|
}
|
|
}
|
|
|
|
// Call JavaScript to draw
|
|
try
|
|
{
|
|
await JS.InvokeVoidAsync("drawLidarScan", points);
|
|
}
|
|
catch
|
|
{
|
|
// Silently ignore if JS not ready
|
|
}
|
|
}
|
|
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
if (firstRender && JS != null)
|
|
{
|
|
// Initialize canvas
|
|
await JS.InvokeVoidAsync("initLidarCanvas");
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
updateTimer?.Dispose();
|
|
if (lidar != null)
|
|
{
|
|
lidar.ScanDataReceived -= OnScanDataReceived;
|
|
}
|
|
}
|
|
}
|