添加xcpcore,添加加载dbc至单一通道
This commit is contained in:
@@ -10,9 +10,13 @@
|
||||
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\TSMasterCAN\TSMasterCAN.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Interop.TSMasterAPI">
|
||||
<HintPath>..\TOSUNCAN\Interop.TSMasterAPI.dll</HintPath>
|
||||
<HintPath>..\..\TSMasterCAN\Interop.TSMasterAPI.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,34 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TSMaster;
|
||||
using TSMasterCAN;
|
||||
using ZLGCANAPI;
|
||||
using ZLG_USB_CAN;
|
||||
|
||||
namespace XCPCore
|
||||
{
|
||||
/// <summary>
|
||||
/// CAN 传输层后端类型
|
||||
/// </summary>
|
||||
public enum CanBackendType
|
||||
{
|
||||
/// <summary>同星 TSCAN</summary>
|
||||
TSCAN,
|
||||
/// <summary>周立功 ZLGCAN</summary>
|
||||
ZLGCAN
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// XCP over CAN 传输层实现。
|
||||
/// 支持同星 CAN(TSMaster)和周立功 CAN(ZLGCAN)两种后端。
|
||||
/// XCP over CAN 传输层实现(同星 TSMaster 后端)。
|
||||
/// 命令发送复用 <see cref="CAN.SendMsg"/>(TSMaster 通道报文 API),
|
||||
/// 接收走 TSMaster 接收 FIFO(tsfifo_receive_canfd_msgs_list / tsfifo_receive_can_msgs_list),
|
||||
/// 由后台轮询任务按 DTO ID 过滤后推入队列。
|
||||
/// </summary>
|
||||
public class XcpCanTransport : IXcpTransport
|
||||
{
|
||||
private readonly CanBackendType _backend;
|
||||
private readonly byte _channel;
|
||||
private readonly uint _croId;
|
||||
private readonly uint _dtoId;
|
||||
@@ -51,23 +38,18 @@ namespace XCPCore
|
||||
/// <summary>DTO ID(接收响应的 CAN ID)</summary>
|
||||
public uint DtoId => _dtoId;
|
||||
|
||||
/// <summary>CAN 后端类型</summary>
|
||||
public CanBackendType Backend => _backend;
|
||||
|
||||
/// <summary>
|
||||
/// 创建 CAN XCP 传输层实例
|
||||
/// 创建 CAN XCP 传输层实例(同星 TSMaster 后端)
|
||||
/// </summary>
|
||||
/// <param name="backend">CAN 后端(TSCAN / ZLGCAN)</param>
|
||||
/// <param name="channel">CAN 通道号</param>
|
||||
/// <param name="croId">CRO CAN ID(发送命令)</param>
|
||||
/// <param name="dtoId">DTO CAN ID(接收响应)</param>
|
||||
/// <param name="isExtended">是否使用扩展帧(29位 ID)</param>
|
||||
/// <param name="isCanFd">是否使用 CAN FD 帧</param>
|
||||
/// <param name="timeoutMs">默认超时时间(毫秒)</param>
|
||||
public XcpCanTransport(CanBackendType backend, byte channel,
|
||||
public XcpCanTransport(byte channel,
|
||||
uint croId, uint dtoId, bool isExtended = false, bool isCanFd = false, int timeoutMs = 3000)
|
||||
{
|
||||
_backend = backend;
|
||||
_channel = channel;
|
||||
_croId = croId;
|
||||
_dtoId = dtoId;
|
||||
@@ -77,21 +59,25 @@ namespace XCPCore
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打开传输层(启动 DTO 接收轮询任务)
|
||||
/// 打开传输层(启用 TSMaster 接收 FIFO 并启动 DTO 接收轮询任务)
|
||||
/// </summary>
|
||||
public Task<bool> OpenAsync(CancellationToken ct = default)
|
||||
{
|
||||
if (IsOpen) return Task.FromResult(true);
|
||||
|
||||
// 启用 TSMaster 接收 FIFO(全局开关,幂等)并清空本通道残留帧
|
||||
TsMasterApi.tsfifo_enable_receive_fifo();
|
||||
ClearFifoBuffers();
|
||||
|
||||
_receiveCts = new CancellationTokenSource();
|
||||
_receiveTask = Task.Run(() => ReceiveLoop(_receiveCts.Token), _receiveCts.Token);
|
||||
IsOpen = true;
|
||||
Debug.WriteLine($"XCP CAN 传输层已打开: Backend={_backend}, CH={_channel}, CRO=0x{_croId:X}, DTO=0x{_dtoId:X}");
|
||||
Debug.WriteLine($"XCP CAN 传输层已打开: CH={_channel}, CRO=0x{_croId:X}, DTO=0x{_dtoId:X}, FD={_isCanFd}");
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送 CTO 帧(构造 CAN 帧并发送)
|
||||
/// 发送 CTO 帧(构造 CAN 帧并经 CAN.SendMsg 发送)
|
||||
/// </summary>
|
||||
public Task SendCtoAsync(byte[] data, CancellationToken ct = default)
|
||||
{
|
||||
@@ -103,30 +89,22 @@ namespace XCPCore
|
||||
byte[] sendData = new byte[maxLen];
|
||||
Array.Copy(data, sendData, sendLen);
|
||||
|
||||
if (_backend == CanBackendType.TSCAN)
|
||||
{
|
||||
// 同星 CAN: 使用 CAN.SendMsg 发送
|
||||
int result = CAN.SendMsg(
|
||||
(APP_CHANNEL)_channel,
|
||||
(int)_croId,
|
||||
true, // AIsTx
|
||||
_isExtended, // AIsExt
|
||||
false, // AIsRemote
|
||||
(byte)sendLen, // ADLC
|
||||
sendData,
|
||||
_isCanFd, // AIsFD
|
||||
false, // AIsBRS
|
||||
false, // isUpdateDBCDatabase
|
||||
0 // sendPeriod (单次发送)
|
||||
);
|
||||
int result = CAN.SendMsg(
|
||||
(APP_CHANNEL)_channel,
|
||||
(int)_croId,
|
||||
true, // AIsTx
|
||||
_isExtended, // AIsExt
|
||||
false, // AIsRemote
|
||||
(byte)sendLen, // ADLC
|
||||
sendData,
|
||||
_isCanFd, // AIsFD
|
||||
false, // AIsBRS
|
||||
false, // isUpdateDBCDatabase
|
||||
0 // sendPeriod (单次发送)
|
||||
);
|
||||
|
||||
if (result != 0)
|
||||
throw new InvalidOperationException($"XCP CAN 发送失败: TSCAN 返回值={result}");
|
||||
}
|
||||
else // ZLGCAN
|
||||
{
|
||||
SendViaZlgCan(sendData, sendLen);
|
||||
}
|
||||
if (result != 0)
|
||||
throw new InvalidOperationException($"XCP CAN 发送失败: TSCAN 返回值={result} ({CAN.GetErrorDescription(result)})");
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
@@ -176,6 +154,7 @@ namespace XCPCore
|
||||
|
||||
// 清空队列
|
||||
while (_responseQueue.TryDequeue(out _)) { }
|
||||
ClearFifoBuffers();
|
||||
|
||||
IsOpen = false;
|
||||
Debug.WriteLine("XCP CAN 传输层已关闭");
|
||||
@@ -186,7 +165,18 @@ namespace XCPCore
|
||||
// ============================================================
|
||||
|
||||
/// <summary>
|
||||
/// 后台接收轮询任务:持续从 CAN 总线读取报文,按 DTO ID 过滤后推入队列
|
||||
/// 清空 TSMaster 本通道接收 FIFO 残留帧
|
||||
/// </summary>
|
||||
private void ClearFifoBuffers()
|
||||
{
|
||||
if (_isCanFd)
|
||||
TsMasterApi.tsfifo_clear_canfd_receive_buffers(_channel);
|
||||
else
|
||||
TsMasterApi.tsfifo_clear_can_receive_buffers(_channel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 后台接收轮询任务:从 TSMaster 接收 FIFO 读取报文,按 DTO ID 过滤后推入队列
|
||||
/// </summary>
|
||||
private async Task ReceiveLoop(CancellationToken ct)
|
||||
{
|
||||
@@ -194,14 +184,10 @@ namespace XCPCore
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_backend == CanBackendType.TSCAN)
|
||||
{
|
||||
ReceiveFromTsCan();
|
||||
}
|
||||
if (_isCanFd)
|
||||
ReceiveFdFromFifo();
|
||||
else
|
||||
{
|
||||
ReceiveFromZlgCan();
|
||||
}
|
||||
ReceiveClassicFromFifo();
|
||||
|
||||
// 轮询间隔:1ms 足以满足大多数 XCP 场景
|
||||
await Task.Delay(1, ct).ContinueWith(_ => { }, TaskContinuationOptions.OnlyOnCanceled);
|
||||
@@ -219,171 +205,59 @@ namespace XCPCore
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从同星 CAN 接收队列中读取 DTO 响应帧
|
||||
/// 从 TSMaster 接收 FIFO 读取 CAN FD 帧并按 DTO ID 过滤
|
||||
/// </summary>
|
||||
private void ReceiveFromTsCan()
|
||||
private void ReceiveFdFromFifo()
|
||||
{
|
||||
while (CAN.TryGetReceivedMessage(out var msg))
|
||||
int count = 0;
|
||||
if (TsMasterApi.tsfifo_read_canfd_rx_buffer_frame_count(_channel, ref count) != 0 || count <= 0)
|
||||
return;
|
||||
|
||||
var buffers = new TLIBCANFD[count];
|
||||
int size = count;
|
||||
if (TsMasterApi.tsfifo_receive_canfd_msgs_list(ref buffers, ref size, _channel, false) != 0)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
// 按 DTO ID 过滤
|
||||
if ((uint)msg.FIdentifier == _dtoId && msg.FIdxChn == _channel)
|
||||
{
|
||||
int len = Math.Min((int)msg.FDLC, msg.FData.Length);
|
||||
var payload = new byte[len];
|
||||
Array.Copy(msg.FData, payload, len);
|
||||
_responseQueue.Enqueue(payload);
|
||||
_responseSignal.Release();
|
||||
}
|
||||
EnqueueIfDto(buffers[i].FIdxChn, (uint)buffers[i].FIdentifier, buffers[i].FDLC, buffers[i].FData);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从周立功 CAN 接收缓冲区中读取 DTO 响应帧
|
||||
/// 从 TSMaster 接收 FIFO 读取经典 CAN 帧并按 DTO ID 过滤
|
||||
/// </summary>
|
||||
private void ReceiveFromZlgCan()
|
||||
private void ReceiveClassicFromFifo()
|
||||
{
|
||||
IntPtr curChn = ZLG_USBCANFD.chnHandle[ZLG_USBCANFD.channel_index];
|
||||
if (curChn == IntPtr.Zero) return;
|
||||
int count = 0;
|
||||
if (TsMasterApi.tsfifo_read_can_rx_buffer_frame_count(_channel, ref count) != 0 || count <= 0)
|
||||
return;
|
||||
|
||||
if (_isCanFd)
|
||||
var buffers = new TLIBCAN[count];
|
||||
int size = count;
|
||||
if (TsMasterApi.tsfifo_receive_can_msgs_list(ref buffers, ref size, _channel, false) != 0)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
uint recNum = ZLGCAN.ZCAN_GetReceiveNum(curChn, 1);
|
||||
if (recNum == 0) return;
|
||||
|
||||
int fdSize = Marshal.SizeOf(typeof(ZLGCAN.ZCAN_ReceiveFD_Data));
|
||||
IntPtr ptrData = Marshal.AllocHGlobal(fdSize * (int)Math.Min(recNum, 100));
|
||||
try
|
||||
{
|
||||
uint count = ZLGCAN.ZCAN_ReceiveFD(curChn, ptrData, Math.Min(recNum, 100), 0);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
IntPtr curPtr = IntPtr.Add(ptrData, i * fdSize);
|
||||
var recvData = (ZLGCAN.ZCAN_ReceiveFD_Data)Marshal.PtrToStructure(
|
||||
curPtr, typeof(ZLGCAN.ZCAN_ReceiveFD_Data))!;
|
||||
|
||||
if (recvData.frame.can_id == _dtoId)
|
||||
{
|
||||
int len = Math.Min((int)recvData.frame.len, 64);
|
||||
var payload = new byte[len];
|
||||
Array.Copy(recvData.frame.data, payload, len);
|
||||
_responseQueue.Enqueue(payload);
|
||||
_responseSignal.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(ptrData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint recNum = ZLGCAN.ZCAN_GetReceiveNum(curChn, 0);
|
||||
if (recNum == 0) return;
|
||||
|
||||
int canSize = Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Receive_Data));
|
||||
IntPtr ptrData = Marshal.AllocHGlobal(canSize * (int)Math.Min(recNum, 100));
|
||||
try
|
||||
{
|
||||
uint count = ZLGCAN.ZCAN_Receive(curChn, ptrData, Math.Min(recNum, 100), 0);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
IntPtr curPtr = IntPtr.Add(ptrData, i * canSize);
|
||||
var recvData = (ZLGCAN.ZCAN_Receive_Data)Marshal.PtrToStructure(
|
||||
curPtr, typeof(ZLGCAN.ZCAN_Receive_Data))!;
|
||||
|
||||
if (recvData.frame.can_id == _dtoId)
|
||||
{
|
||||
int len = Math.Min((int)recvData.frame.can_dlc, 8);
|
||||
var payload = new byte[len];
|
||||
Array.Copy(recvData.frame.data, payload, len);
|
||||
_responseQueue.Enqueue(payload);
|
||||
_responseSignal.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(ptrData);
|
||||
}
|
||||
EnqueueIfDto(buffers[i].FIdxChn, (uint)buffers[i].FIdentifier, buffers[i].FDLC, buffers[i].FData);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过周立功 CAN 发送帧
|
||||
/// 校验帧(通道 + DTO ID 匹配)后截取有效数据入队并释放等待信号
|
||||
/// </summary>
|
||||
private void SendViaZlgCan(byte[] data, int dataLen)
|
||||
private void EnqueueIfDto(byte frameChannel, uint frameId, byte dlc, byte[] data)
|
||||
{
|
||||
IntPtr curChn = ZLG_USBCANFD.chnHandle[ZLG_USBCANFD.channel_index];
|
||||
if (curChn == IntPtr.Zero)
|
||||
throw new InvalidOperationException("ZLGCAN 通道句柄无效");
|
||||
if (frameChannel != _channel || frameId != _dtoId)
|
||||
return;
|
||||
|
||||
if (_isCanFd)
|
||||
{
|
||||
var frame = new ZLGCAN.canfd_frame
|
||||
{
|
||||
can_id = _croId,
|
||||
len = (byte)dataLen,
|
||||
flags = 0,
|
||||
__res0 = 0,
|
||||
__res1 = 0,
|
||||
data = new byte[64]
|
||||
};
|
||||
Array.Copy(data, frame.data, dataLen);
|
||||
|
||||
var txData = new ZLGCAN.ZCAN_TransmitFD_Data
|
||||
{
|
||||
frame = frame,
|
||||
transmit_type = 0 // 正常发送
|
||||
};
|
||||
|
||||
int size = Marshal.SizeOf(txData);
|
||||
IntPtr ptr = Marshal.AllocHGlobal(size);
|
||||
try
|
||||
{
|
||||
Marshal.StructureToPtr(txData, ptr, true);
|
||||
uint ret = ZLGCAN.ZCAN_TransmitFD(curChn, ptr, 1);
|
||||
if (ret != 1)
|
||||
throw new InvalidOperationException($"XCP CAN FD 发送失败: ZLGCAN 返回值={ret}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(ptr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var frame = new ZLGCAN.can_frame
|
||||
{
|
||||
can_id = _croId,
|
||||
can_dlc = (byte)dataLen,
|
||||
__pad = 0,
|
||||
__res0 = 0,
|
||||
__res1 = 0,
|
||||
data = new byte[8]
|
||||
};
|
||||
Array.Copy(data, frame.data, Math.Min(dataLen, 8));
|
||||
|
||||
var txData = new ZLGCAN.ZCAN_Transmit_Data
|
||||
{
|
||||
frame = frame,
|
||||
transmit_type = 0
|
||||
};
|
||||
|
||||
int size = Marshal.SizeOf(txData);
|
||||
IntPtr ptr = Marshal.AllocHGlobal(size);
|
||||
try
|
||||
{
|
||||
Marshal.StructureToPtr(txData, ptr, true);
|
||||
uint ret = ZLGCAN.ZCAN_Transmit(curChn, ptr, 1);
|
||||
if (ret != 1)
|
||||
throw new InvalidOperationException($"XCP CAN 发送失败: ZLGCAN 返回值={ret}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(ptr);
|
||||
}
|
||||
}
|
||||
int len = Math.Min((int)dlc, data?.Length ?? 0);
|
||||
var payload = new byte[len];
|
||||
if (len > 0)
|
||||
Array.Copy(data, payload, len);
|
||||
_responseQueue.Enqueue(payload);
|
||||
_responseSignal.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user