fgh
This commit is contained in:
@@ -10,18 +10,73 @@
|
||||
|
||||
<h1>CAN Monitor</h1>
|
||||
|
||||
@* ── Status bar ─────────────────────────────────────────────────────────────── *@
|
||||
<div class="d-flex align-items-center gap-3 mb-3">
|
||||
@if (CanService.IsConnected)
|
||||
{
|
||||
<span class="badge bg-success fs-6">● Connected</span>
|
||||
<span class="text-muted">Channel: <strong>@CanService.ChannelName</strong></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge bg-danger fs-6">○ Not connected</span>
|
||||
<span class="text-muted small">Check that a PCAN USB adapter is plugged in and CanOptions are set in appsettings.json.</span>
|
||||
}
|
||||
@* ── Connection ──────────────────────────────────────────────────────────────── *@
|
||||
<div class="card mb-3">
|
||||
<div class="card-body py-2">
|
||||
@if (CanService.IsConnected && !showConnectionForm)
|
||||
{
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<span class="badge bg-success">● Connected</span>
|
||||
<span class="font-monospace fw-semibold">@CanService.ChannelName</span>
|
||||
<span class="text-muted">@BitrateLabel(CanService.CurrentBitrate)</span>
|
||||
<button class="btn btn-sm btn-outline-secondary ms-auto"
|
||||
@onclick="() => { showConnectionForm = true; ScanChannels(); }">
|
||||
Change
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row g-2 align-items-end">
|
||||
<div class="col-auto">
|
||||
<label class="form-label small mb-0">Channel</label>
|
||||
<select class="form-select form-select-sm font-monospace" @bind="selectedChannel"
|
||||
style="min-width:100px">
|
||||
@if (availableChannels.Count == 0)
|
||||
{
|
||||
<option value="">— none found —</option>
|
||||
}
|
||||
@foreach (var ch in availableChannels)
|
||||
{
|
||||
<option value="@ch.ToString()">@ch.ToString()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<label class="form-label small mb-0">Bitrate</label>
|
||||
<select class="form-select form-select-sm" @bind="selectedBitrate"
|
||||
style="min-width:120px">
|
||||
@foreach (var (val, label) in BitrateOptions)
|
||||
{
|
||||
<option value="@val.ToString()">@label</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-auto d-flex gap-2">
|
||||
<button class="btn btn-sm btn-outline-secondary" @onclick="ScanChannels">Scan</button>
|
||||
<button class="btn btn-sm btn-primary" @onclick="Connect"
|
||||
disabled="@(availableChannels.Count == 0 || isConnecting)">
|
||||
@(isConnecting ? "Connecting…" : CanService.IsConnected ? "Reconnect" : "Connect")
|
||||
</button>
|
||||
@if (CanService.IsConnected)
|
||||
{
|
||||
<button class="btn btn-sm btn-outline-secondary"
|
||||
@onclick="() => showConnectionForm = false">Cancel</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@if (availableChannels.Count == 0)
|
||||
{
|
||||
<div class="text-warning small mt-1">
|
||||
No PCAN USB channels detected. Plug in your adapter and click Scan.
|
||||
</div>
|
||||
}
|
||||
@if (connectError != null)
|
||||
{
|
||||
<div class="text-danger small mt-1">@connectError</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* ── Live messages ──────────────────────────────────────────────────────────── *@
|
||||
@@ -294,6 +349,28 @@
|
||||
</div>
|
||||
|
||||
@code {
|
||||
// ── Connection form ───────────────────────────────────────────────────────
|
||||
private List<PcanChannel> availableChannels = [];
|
||||
private string selectedChannel = "";
|
||||
private string selectedBitrate = Bitrate.Pcan500.ToString();
|
||||
private bool showConnectionForm;
|
||||
private bool isConnecting;
|
||||
private string? connectError;
|
||||
|
||||
private static readonly (Bitrate Value, string Label)[] BitrateOptions =
|
||||
[
|
||||
(Bitrate.Pcan1000, "1000 kbps"),
|
||||
(Bitrate.Pcan800, "800 kbps"),
|
||||
(Bitrate.Pcan500, "500 kbps"),
|
||||
(Bitrate.Pcan250, "250 kbps"),
|
||||
(Bitrate.Pcan125, "125 kbps"),
|
||||
(Bitrate.Pcan100, "100 kbps"),
|
||||
(Bitrate.Pcan50, "50 kbps"),
|
||||
(Bitrate.Pcan20, "20 kbps"),
|
||||
(Bitrate.Pcan10, "10 kbps"),
|
||||
(Bitrate.Pcan5, "5 kbps"),
|
||||
];
|
||||
|
||||
// ── Data ──────────────────────────────────────────────────────────────────
|
||||
private readonly List<CanMessageDto> messages = [];
|
||||
private readonly Dictionary<uint, CanMessageDto> latestMessages = new();
|
||||
@@ -333,6 +410,17 @@
|
||||
filters = CanService.Filters.ToList();
|
||||
bitmasks = CanService.Bitmasks.ToList();
|
||||
CanService.MessageReceived += OnMessageReceived;
|
||||
|
||||
ScanChannels();
|
||||
if (CanService.IsConnected)
|
||||
{
|
||||
selectedChannel = CanService.ChannelName;
|
||||
selectedBitrate = CanService.CurrentBitrate.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
showConnectionForm = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMessageReceived(CanMessageDto msg)
|
||||
@@ -345,14 +433,70 @@
|
||||
}
|
||||
lock (latestMessages)
|
||||
{
|
||||
latestMessages[msg.Id] = msg;
|
||||
updateCounts[msg.Id] = updateCounts.GetValueOrDefault(msg.Id) + 1;
|
||||
if (!latestMessages.TryGetValue(msg.Id, out var existing) ||
|
||||
!msg.Data.SequenceEqual(existing.Data))
|
||||
{
|
||||
latestMessages[msg.Id] = msg;
|
||||
updateCounts[msg.Id] = updateCounts.GetValueOrDefault(msg.Id) + 1;
|
||||
}
|
||||
}
|
||||
InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
public void Dispose() => CanService.MessageReceived -= OnMessageReceived;
|
||||
|
||||
// ── Connection ────────────────────────────────────────────────────────────
|
||||
|
||||
private void ScanChannels()
|
||||
{
|
||||
availableChannels = CanService.GetAvailableChannels().ToList();
|
||||
if (availableChannels.Count > 0 &&
|
||||
!availableChannels.Any(c => c.ToString() == selectedChannel))
|
||||
selectedChannel = availableChannels[0].ToString();
|
||||
}
|
||||
|
||||
private async Task Connect()
|
||||
{
|
||||
connectError = null;
|
||||
if (!Enum.TryParse<PcanChannel>(selectedChannel, out var channel))
|
||||
{ connectError = "Please select a channel."; return; }
|
||||
if (!Enum.TryParse<Bitrate>(selectedBitrate, out var bitrate))
|
||||
{ connectError = "Please select a bitrate."; return; }
|
||||
|
||||
isConnecting = true;
|
||||
CanService.RequestReinitialize(channel, bitrate);
|
||||
|
||||
// Poll until the worker completes the reinit (typically < 50 ms)
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
await Task.Delay(100);
|
||||
if (CanService.IsConnected) break;
|
||||
}
|
||||
|
||||
isConnecting = false;
|
||||
if (CanService.IsConnected)
|
||||
showConnectionForm = false;
|
||||
else
|
||||
connectError = "Connection failed. Check the adapter is plugged in and try again.";
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private static string BitrateLabel(Bitrate b) => b switch
|
||||
{
|
||||
Bitrate.Pcan1000 => "1000 kbps",
|
||||
Bitrate.Pcan800 => "800 kbps",
|
||||
Bitrate.Pcan500 => "500 kbps",
|
||||
Bitrate.Pcan250 => "250 kbps",
|
||||
Bitrate.Pcan125 => "125 kbps",
|
||||
Bitrate.Pcan100 => "100 kbps",
|
||||
Bitrate.Pcan50 => "50 kbps",
|
||||
Bitrate.Pcan20 => "20 kbps",
|
||||
Bitrate.Pcan10 => "10 kbps",
|
||||
Bitrate.Pcan5 => "5 kbps",
|
||||
_ => b.ToString()
|
||||
};
|
||||
|
||||
// ── Messages ──────────────────────────────────────────────────────────────
|
||||
|
||||
private void ClearMessages()
|
||||
|
||||
Reference in New Issue
Block a user