namespace BmsMonitor.App.Can; public sealed class ControlCanReceiver : ICanReceiver { private CancellationTokenSource? _cts; private Task? _receiveTask; private uint _deviceIndex; private uint _channelIndex; public bool IsOpen { get; private set; } public event EventHandler? FrameReceived; public event EventHandler? ReceiveError; public void Open(uint deviceIndex, uint channelIndex, CanBaudRate baudRate = CanBaudRate.Kbps250) { if (IsOpen) return; _deviceIndex = deviceIndex; _channelIndex = channelIndex; if (ControlCanNative.VCI_OpenDevice(ControlCanNative.VciUsbCan2, deviceIndex, 0) != ControlCanNative.StatusOk) throw new InvalidOperationException("打开 USBCAN-II 失败,请检查驱动、设备索引及 ControlCAN.dll。"); var (timing0, timing1) = baudRate.ToTiming(); var config = new ControlCanNative.VciInitConfig { AccCode = 0, AccMask = 0xFFFFFFFF, Filter = 1, Timing0 = timing0, Timing1 = timing1, Mode = 1 // 硬件只听模式:仅接收,不发送 ACK }; var initResult = ControlCanNative.VCI_InitCAN(ControlCanNative.VciUsbCan2, deviceIndex, channelIndex, ref config); if (initResult != ControlCanNative.StatusOk) { ControlCanNative.VCI_CloseDevice(ControlCanNative.VciUsbCan2, deviceIndex); throw new InvalidOperationException($"VCI_InitCAN 失败(返回 {initResult},通道 CAN{channelIndex + 1},{baudRate.ToDisplayText()},只听模式)。"); } var startResult = ControlCanNative.VCI_StartCAN(ControlCanNative.VciUsbCan2, deviceIndex, channelIndex); if (startResult != ControlCanNative.StatusOk) { ControlCanNative.VCI_ResetCAN(ControlCanNative.VciUsbCan2, deviceIndex, channelIndex); ControlCanNative.VCI_CloseDevice(ControlCanNative.VciUsbCan2, deviceIndex); throw new InvalidOperationException($"VCI_StartCAN 失败(返回 {startResult},通道 CAN{channelIndex + 1})。"); } IsOpen = true; _cts = new CancellationTokenSource(); _receiveTask = Task.Run(() => ReceiveLoop(_cts.Token)); } private void ReceiveLoop(CancellationToken token) { var buffer = Enumerable.Range(0, 100).Select(_ => new ControlCanNative.VciCanObj { Data = new byte[8], Reserved = new byte[3] }).ToArray(); try { while (!token.IsCancellationRequested) { var count = ControlCanNative.VCI_Receive(ControlCanNative.VciUsbCan2, _deviceIndex, _channelIndex, buffer, (uint)buffer.Length, 50); if (count == 0 || count == uint.MaxValue) continue; for (var i = 0; i < Math.Min(count, (uint)buffer.Length); i++) { var item = buffer[i]; if (item.RemoteFlag != 0) continue; var length = Math.Min(item.DataLen, (byte)8); FrameReceived?.Invoke(this, new CanFrame(item.Id, item.ExternFlag != 0, item.Data.Take(length).ToArray(), DateTime.Now)); } } } catch (Exception ex) when (!token.IsCancellationRequested) { ReceiveError?.Invoke(this, ex.Message); } } public void Close() { if (!IsOpen) return; _cts?.Cancel(); _receiveTask?.Wait(300); ControlCanNative.VCI_ResetCAN(ControlCanNative.VciUsbCan2, _deviceIndex, _channelIndex); ControlCanNative.VCI_CloseDevice(ControlCanNative.VciUsbCan2, _deviceIndex); _cts?.Dispose(); _cts = null; IsOpen = false; } public void Dispose() => Close(); }