Initial commit
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
@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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user