CAN卡添加
This commit is contained in:
400
USBCANFD/USBCANFD.cs
Normal file
400
USBCANFD/USBCANFD.cs
Normal file
@@ -0,0 +1,400 @@
|
||||
using Common.Attributes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using USBCANFD;
|
||||
|
||||
namespace DeviceCommand.Device
|
||||
{
|
||||
[ADPCommand]
|
||||
public class ZLGCANFD : IDisposable
|
||||
{
|
||||
// 硬件设备与通道句柄
|
||||
private IntPtr _deviceHandle = IntPtr.Zero;
|
||||
private readonly IntPtr[] _channelHandles;
|
||||
|
||||
// 【核心修改】每个通道分配独立的 DBC 引擎句柄与锁,彻底解决台架间DBC冲突与锁竞争
|
||||
private readonly uint[] _dbcHandles;
|
||||
private readonly bool[] _isDbcLoadedArray;
|
||||
private readonly object[] _channelLocks; // 通道级细粒度锁
|
||||
|
||||
// 异步高性能接收线程控制
|
||||
private volatile bool _isRunning = false;
|
||||
private readonly List<Thread> _receiveThreads = new List<Thread>();
|
||||
|
||||
// 动态硬件参数
|
||||
private readonly uint _deviceType; // 43: USBCANFD-400U
|
||||
private readonly uint _deviceIndex; // 设备索引
|
||||
private readonly int _maxChannels; // 动态通道数
|
||||
|
||||
/// <summary>
|
||||
/// 事件:当接收到 CAN/CANFD 报文并且通过 DBC 成功解析后触发
|
||||
/// 参数:uint 通道号 (0,1,2,3), ZDBC.DBCMessage 解析后的DBC消息结构体
|
||||
/// </summary>
|
||||
public event Action<uint, ZDBC.DBCMessage>? OnDbcMessageDecoded;
|
||||
|
||||
public ZLGCANFD(uint deviceType = 43, uint deviceIndex = 0, int maxChannels = 4)
|
||||
{
|
||||
_deviceType = deviceType;
|
||||
_deviceIndex = deviceIndex;
|
||||
_maxChannels = maxChannels;
|
||||
|
||||
// 初始化通道相关状态数组
|
||||
_channelHandles = new IntPtr[_maxChannels];
|
||||
_dbcHandles = new uint[_maxChannels];
|
||||
_isDbcLoadedArray = new bool[_maxChannels];
|
||||
_channelLocks = new object[_maxChannels];
|
||||
|
||||
for (int i = 0; i < _maxChannels; i++)
|
||||
{
|
||||
_channelHandles[i] = IntPtr.Zero;
|
||||
_dbcHandles[i] = 0;
|
||||
_isDbcLoadedArray[i] = false;
|
||||
_channelLocks[i] = new object();
|
||||
}
|
||||
}
|
||||
|
||||
#region 1. 硬件连接与通道初始化
|
||||
|
||||
/// <summary>
|
||||
/// 仅仅打开设备,不做具体的通道波特率配置(留给具体的台架去分别配置)
|
||||
/// </summary>
|
||||
public virtual bool 打开设备()
|
||||
{
|
||||
if (_deviceHandle != IntPtr.Zero) return true;
|
||||
_deviceHandle = ZLGCAN.ZCAN_OpenDevice(_deviceType, _deviceIndex, 0);
|
||||
return _deviceHandle != IntPtr.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 【核心修改】针对特定通道进行参数初始化并启动
|
||||
/// </summary>
|
||||
public virtual bool 初始化并启动通道(uint 通道号, string abitBaud = "500000", string dbitBaud = "2000000", bool 开启终端电阻 = true)
|
||||
{
|
||||
if (_deviceHandle == IntPtr.Zero) throw new InvalidOperationException("请先调用 '打开设备()' 才能初始化通道。");
|
||||
if (通道号 >= _maxChannels) return false;
|
||||
|
||||
lock (_channelLocks[通道号])
|
||||
{
|
||||
// 如果已经启动过,先复位
|
||||
if (_channelHandles[通道号] != IntPtr.Zero)
|
||||
{
|
||||
ZLGCAN.ZCAN_ResetCAN(_channelHandles[通道号]);
|
||||
}
|
||||
|
||||
// 1. 设置该通道专属的仲裁域与数据域波特率
|
||||
if (ZLGCAN.ZCAN_SetValue(_deviceHandle, $"{通道号}/canfd_abit_baud_rate", abitBaud) != 1) return false;
|
||||
if (ZLGCAN.ZCAN_SetValue(_deviceHandle, $"{通道号}/canfd_dbit_baud_rate", dbitBaud) != 1) return false;
|
||||
|
||||
// 2. 设置该通道专属内部终端电阻状态
|
||||
string resistanceStr = 开启终端电阻 ? "1" : "0";
|
||||
if (ZLGCAN.ZCAN_SetValue(_deviceHandle, $"{通道号}/initenal_resistance", resistanceStr) != 1) return false;
|
||||
|
||||
// 3. 规范配置通道结构体
|
||||
ZLGCAN.ZCAN_CHANNEL_INIT_CONFIG config = new ZLGCAN.ZCAN_CHANNEL_INIT_CONFIG();
|
||||
config.can_type = 1; // 1 代表 CANFD 模式
|
||||
config.config.canfd.mode = 0; // 0 代表正常工作模式
|
||||
|
||||
IntPtr pConfig = Marshal.AllocHGlobal(Marshal.SizeOf(config));
|
||||
Marshal.StructureToPtr(config, pConfig, true);
|
||||
_channelHandles[通道号] = ZLGCAN.ZCAN_InitCAN(_deviceHandle, 通道号, pConfig);
|
||||
Marshal.FreeHGlobal(pConfig);
|
||||
|
||||
if (_channelHandles[通道号] == IntPtr.Zero) return false;
|
||||
|
||||
// 4. 启动 CAN 通道
|
||||
if (ZLGCAN.ZCAN_StartCAN(_channelHandles[通道号]) != 1) return false;
|
||||
|
||||
// 5. 为该通道启动专属的独立后台高性能轮询接收线程(如果尚未启动轮询)
|
||||
if (!_isRunning) _isRunning = true;
|
||||
|
||||
int chnIdx = (int)通道号;
|
||||
Thread rxThread = new Thread(() => 接收轮询核心(_channelHandles[chnIdx], (uint)chnIdx))
|
||||
{
|
||||
IsBackground = true,
|
||||
Name = $"ZLGCANFD_Dev{_deviceIndex}_CH{chnIdx}_RxThread"
|
||||
};
|
||||
_receiveThreads.Add(rxThread);
|
||||
rxThread.Start();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void 关闭CAN卡设备()
|
||||
{
|
||||
_isRunning = false;
|
||||
Thread.Sleep(50); // 确保轮询线程安全退出
|
||||
_receiveThreads.Clear();
|
||||
|
||||
// 动态复位所有通道并释放DBC
|
||||
for (uint i = 0; i < _maxChannels; i++)
|
||||
{
|
||||
lock (_channelLocks[i])
|
||||
{
|
||||
if (_channelHandles[i] != IntPtr.Zero)
|
||||
{
|
||||
ZLGCAN.ZCAN_ResetCAN(_channelHandles[i]);
|
||||
_channelHandles[i] = IntPtr.Zero;
|
||||
}
|
||||
释放通道DBC(i);
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭设备主句柄
|
||||
if (_deviceHandle != IntPtr.Zero)
|
||||
{
|
||||
ZLGCAN.ZCAN_CloseDevice(_deviceHandle);
|
||||
_deviceHandle = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 2. DBC 矩阵文件操作
|
||||
|
||||
/// <summary>
|
||||
/// 【核心修改】指定通道加载特定的 DBC 文件(支持不同通道加载不同的DBC矩阵)
|
||||
/// </summary>
|
||||
public virtual bool 加载通道DBC文件(uint 通道号, string dbcFilePath)
|
||||
{
|
||||
if (通道号 >= _maxChannels) return false;
|
||||
|
||||
lock (_channelLocks[通道号])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_isDbcLoadedArray[通道号]) return true;
|
||||
|
||||
uint chDbcHandle = ZDBC.ZDBC_Init();
|
||||
if (chDbcHandle == 0) return false;
|
||||
|
||||
IntPtr ptrPath = Marshal.StringToHGlobalAnsi(dbcFilePath);
|
||||
bool success = ZDBC.ZDBC_LoadFile(chDbcHandle, ptrPath);
|
||||
Marshal.FreeHGlobal(ptrPath);
|
||||
|
||||
if (success)
|
||||
{
|
||||
_dbcHandles[通道号] = chDbcHandle;
|
||||
_isDbcLoadedArray[通道号] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ZDBC.ZDBC_Release(chDbcHandle);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void 释放通道DBC(uint 通道号)
|
||||
{
|
||||
if (通道号 >= _maxChannels) return;
|
||||
|
||||
lock (_channelLocks[通道号])
|
||||
{
|
||||
if (_isDbcLoadedArray[通道号] && _dbcHandles[通道号] != 0)
|
||||
{
|
||||
ZDBC.ZDBC_Release(_dbcHandles[通道号]);
|
||||
_dbcHandles[通道号] = 0;
|
||||
_isDbcLoadedArray[通道号] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 3. 轮询接收与实时自动化 DBC 解码
|
||||
|
||||
private void 接收轮询核心(IntPtr channelHandle, uint channelIndex)
|
||||
{
|
||||
const int bufferSize = 100;
|
||||
int canStructSize = Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Receive_Data));
|
||||
int canfdStructSize = Marshal.SizeOf(typeof(ZLGCAN.ZCAN_ReceiveFD_Data));
|
||||
int dbcMsgSize = Marshal.SizeOf(typeof(ZDBC.DBCMessage));
|
||||
|
||||
IntPtr ptrCanBuffer = Marshal.AllocHGlobal(canStructSize * bufferSize);
|
||||
IntPtr ptrCanFDBuffer = Marshal.AllocHGlobal(canfdStructSize * bufferSize);
|
||||
IntPtr ptrDbcMsg = Marshal.AllocHGlobal(dbcMsgSize);
|
||||
|
||||
try
|
||||
{
|
||||
while (_isRunning)
|
||||
{
|
||||
bool currentTurnHasData = false;
|
||||
|
||||
// 1. 自动提取并解析经典 CAN 帧
|
||||
uint canNum = ZLGCAN.ZCAN_GetReceiveNum(channelHandle, 0);
|
||||
if (canNum > 0)
|
||||
{
|
||||
uint actualRecv = ZLGCAN.ZCAN_Receive(channelHandle, ptrCanBuffer, bufferSize, 5);
|
||||
for (int i = 0; i < actualRecv; i++)
|
||||
{
|
||||
IntPtr framePtr = IntPtr.Add(ptrCanBuffer, i * canStructSize);
|
||||
|
||||
// 【通道级业务隔离隔离】只锁定当前通道的DBC句柄进行解码,高并发完全不卡顿
|
||||
lock (_channelLocks[channelIndex])
|
||||
{
|
||||
if (_isDbcLoadedArray[channelIndex] && ZDBC.ZDBC_Decode(_dbcHandles[channelIndex], ptrDbcMsg, framePtr, 1, 0))
|
||||
{
|
||||
var msg = (ZDBC.DBCMessage)Marshal.PtrToStructure(ptrDbcMsg, typeof(ZDBC.DBCMessage));
|
||||
OnDbcMessageDecoded?.Invoke(channelIndex, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
currentTurnHasData = true;
|
||||
}
|
||||
|
||||
// 2. 自动提取并解析高速 CANFD 帧
|
||||
uint canfdNum = ZLGCAN.ZCAN_GetReceiveNum(channelHandle, 1);
|
||||
if (canfdNum > 0)
|
||||
{
|
||||
uint actualRecvFd = ZLGCAN.ZCAN_ReceiveFD(channelHandle, ptrCanFDBuffer, bufferSize, 5);
|
||||
for (int i = 0; i < actualRecvFd; i++)
|
||||
{
|
||||
IntPtr framePtr = IntPtr.Add(ptrCanFDBuffer, i * canfdStructSize);
|
||||
|
||||
lock (_channelLocks[channelIndex])
|
||||
{
|
||||
if (_isDbcLoadedArray[channelIndex] && ZDBC.ZDBC_Decode(_dbcHandles[channelIndex], ptrDbcMsg, framePtr, 1, 1))
|
||||
{
|
||||
var msg = (ZDBC.DBCMessage)Marshal.PtrToStructure(ptrDbcMsg, typeof(ZDBC.DBCMessage));
|
||||
OnDbcMessageDecoded?.Invoke(channelIndex, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
currentTurnHasData = true;
|
||||
}
|
||||
|
||||
if (!currentTurnHasData)
|
||||
{
|
||||
Thread.Sleep(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(ptrCanBuffer);
|
||||
Marshal.FreeHGlobal(ptrCanFDBuffer);
|
||||
Marshal.FreeHGlobal(ptrDbcMsg);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 4. DBC 自动打包与智能报文发送
|
||||
|
||||
public virtual bool 发送DBC定义报文(uint 通道号, uint 帧ID, Action<ZDBC.DBCMessage> 配置信号动作, int 是否使用CANFD = 1)
|
||||
{
|
||||
if (通道号 >= _maxChannels || _channelHandles[通道号] == IntPtr.Zero) return false;
|
||||
if (!_isDbcLoadedArray[通道号]) throw new InvalidOperationException($"通道 {通道号} 的 DBC 协议未加载,无法执行打包发送指令。");
|
||||
|
||||
IntPtr ptrDbcMsg = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCMessage)));
|
||||
IntPtr ptrCount = Marshal.AllocHGlobal(sizeof(uint));
|
||||
Marshal.WriteInt32(ptrCount, 1);
|
||||
|
||||
try
|
||||
{
|
||||
// 使用通道级锁保护当前通道发送
|
||||
lock (_channelLocks[通道号])
|
||||
{
|
||||
uint chDbcHandle = _dbcHandles[通道号];
|
||||
|
||||
if (!ZDBC.ZDBC_GetMessageById(chDbcHandle, 帧ID, ptrDbcMsg)) return false;
|
||||
var msg = (ZDBC.DBCMessage)Marshal.PtrToStructure(ptrDbcMsg, typeof(ZDBC.DBCMessage));
|
||||
|
||||
配置信号动作(msg);
|
||||
Marshal.StructureToPtr(msg, ptrDbcMsg, true);
|
||||
|
||||
if (是否使用CANFD == 0)
|
||||
{
|
||||
IntPtr ptrCanFrame = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.can_frame)));
|
||||
if (!ZDBC.ZDBC_Encode(chDbcHandle, ptrCanFrame, ptrCount, ptrDbcMsg, 0)) return false;
|
||||
|
||||
ZLGCAN.can_frame canFrame = (ZLGCAN.can_frame)Marshal.PtrToStructure(ptrCanFrame, typeof(ZLGCAN.can_frame));
|
||||
canFrame.__pad |= 0x20;
|
||||
|
||||
ZLGCAN.ZCAN_Transmit_Data txData = new ZLGCAN.ZCAN_Transmit_Data { frame = canFrame, transmit_type = 0 };
|
||||
IntPtr pTx = Marshal.AllocHGlobal(Marshal.SizeOf(txData));
|
||||
Marshal.StructureToPtr(txData, pTx, true);
|
||||
|
||||
uint sendResult = ZLGCAN.ZCAN_Transmit(_channelHandles[通道号], pTx, 1);
|
||||
|
||||
Marshal.FreeHGlobal(ptrCanFrame);
|
||||
Marshal.FreeHGlobal(pTx);
|
||||
return sendResult == 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
IntPtr ptrCanFDFrame = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.canfd_frame)));
|
||||
if (!ZDBC.ZDBC_Encode(chDbcHandle, ptrCanFDFrame, ptrCount, ptrDbcMsg, 1)) return false;
|
||||
|
||||
ZLGCAN.canfd_frame canfdFrame = (ZLGCAN.canfd_frame)Marshal.PtrToStructure(ptrCanFDFrame, typeof(ZLGCAN.canfd_frame));
|
||||
canfdFrame.flags |= 0x20;
|
||||
|
||||
ZLGCAN.ZCAN_TransmitFD_Data txFdData = new ZLGCAN.ZCAN_TransmitFD_Data { frame = canfdFrame, transmit_type = 0 };
|
||||
IntPtr pTxFd = Marshal.AllocHGlobal(Marshal.SizeOf(txFdData));
|
||||
Marshal.StructureToPtr(txFdData, pTxFd, true);
|
||||
|
||||
uint sendResult = ZLGCAN.ZCAN_TransmitFD(_channelHandles[通道号], pTxFd, 1);
|
||||
|
||||
Marshal.FreeHGlobal(ptrCanFDFrame);
|
||||
Marshal.FreeHGlobal(pTxFd);
|
||||
return sendResult == 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(ptrDbcMsg);
|
||||
Marshal.FreeHGlobal(ptrCount);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 5. 辅助工具子系统
|
||||
|
||||
/// <summary>
|
||||
/// 【核心修改】计算物理值也需要传入对应通道,使用通道专属的DBC句柄进行计算
|
||||
/// </summary>
|
||||
public ulong 物理值转原始寄存器值(uint 通道号, ZDBC.DBCSignal 信号定义, double 实际物理值)
|
||||
{
|
||||
if (通道号 >= _maxChannels) return 0;
|
||||
|
||||
IntPtr pSignal = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCSignal)));
|
||||
IntPtr pValue = Marshal.AllocHGlobal(sizeof(double));
|
||||
|
||||
try
|
||||
{
|
||||
Marshal.StructureToPtr(信号定义, pSignal, true);
|
||||
Marshal.StructureToPtr(实际物理值, pValue, true);
|
||||
lock (_channelLocks[通道号])
|
||||
{
|
||||
return ZDBC.ZDBC_CalcRawValue(pSignal, pValue);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(pSignal);
|
||||
Marshal.FreeHGlobal(pValue);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
[Browsable(false)]
|
||||
public void Dispose()
|
||||
{
|
||||
关闭CAN卡设备();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user