Files

99 lines
3.8 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<CanFrame>? FrameReceived;
public event EventHandler<string>? 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();
}