CAN设置添加

This commit is contained in:
hsc
2026-07-02 15:36:55 +08:00
parent 63f43853fa
commit 60dc155ccf
20 changed files with 1250 additions and 413 deletions

729
ZLGUSBCANFD/USBCANFD.cs Normal file
View File

@@ -0,0 +1,729 @@
using Common.Attributes;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ZLGUSBCANFD
{
[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>();
// DBC 循环发送任务管理Key = (通道号, 帧ID)
private readonly ConcurrentDictionary<(uint , uint ID), CancellationTokenSource> _cyclicSenders = new ConcurrentDictionary<(uint, uint), CancellationTokenSource>();
// 动态硬件参数
private readonly uint _deviceType; // 76: 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 = 76, 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(ZLGCAN.ZCAN_USBCANFD_400U, _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
/// <summary>
/// 发送一条 DBC 定义报文。
/// </summary>
/// <param name="通道号">通道号</param>
/// <param name="帧ID">DBC 中定义的帧 ID</param>
/// <param name="信号物理值字典">信号名称 → 物理值;只设置字典中包含的信号,其余信号保持 DBC 默认值</param>
/// <param name="循环间隔毫秒">0 = 只发送一次;>0 = 按指定间隔循环发送(毫秒)</param>
/// <param name="是否使用CANFD">1 = CANFD0 = CAN</param>
/// <returns>是否成功启动/发送</returns>
public virtual bool DBC定义报文(
uint ,
uint ID,
Dictionary<string, double> ,
int = 0,
int 使CANFD = 1)
{
if ( >= _maxChannels || _channelHandles[] == IntPtr.Zero) return false;
if (!_isDbcLoadedArray[]) throw new InvalidOperationException($"通道 {通道号} 的 DBC 协议未加载,无法执行打包发送指令。");
if ( < 0) throw new ArgumentOutOfRangeException(nameof(), "循环间隔必须大于或等于 0。");
if ( == null || .Count == 0)
throw new ArgumentException("信号物理值字典不能为空。", nameof());
if ( == 0)
{
// 单次发送:同步执行
lock (_channelLocks[])
{
return DBC定义报文单次(, ID, , 使CANFD);
}
}
// 循环发送:先停止同通道同帧 ID 的旧循环,再启动新循环
(, ID);
var cts = new CancellationTokenSource();
_cyclicSenders[(, ID)] = cts;
Task.Run(async () =>
{
while (!cts.Token.IsCancellationRequested)
{
try
{
lock (_channelLocks[])
{
if (_channelHandles[] == IntPtr.Zero) break;
DBC定义报文单次(, ID, , 使CANFD);
}
await Task.Delay(, cts.Token);
}
catch (OperationCanceledException)
{
break;
}
catch (Exception ex)
{
// 循环发送出错时退出,避免刷屏
Console.WriteLine($"[ZLGCANFD] 循环发送 DBC 报文失败: {ex.Message}");
break;
}
}
_cyclicSenders.TryRemove((, ID), out var removedCts);
removedCts?.Dispose();
}, cts.Token);
return true;
}
/// <summary>
/// 停止指定通道、指定帧 ID 的循环发送。
/// </summary>
public virtual void (uint , uint ID)
{
if (_cyclicSenders.TryRemove((, ID), out var cts))
{
cts.Cancel();
cts.Dispose();
}
}
/// <summary>
/// 停止所有 DBC 循环发送任务。
/// </summary>
public virtual void ()
{
foreach (var kvp in _cyclicSenders)
{
kvp.Value.Cancel();
kvp.Value.Dispose();
}
_cyclicSenders.Clear();
}
/// <summary>
/// 单次发送 DBC 报文(调用方已持有通道锁)。
/// </summary>
private bool DBC定义报文单次(uint , uint ID, Dictionary<string, double> , int 使CANFD)
{
uint chDbcHandle = _dbcHandles[];
IntPtr ptrDbcMsg = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCMessage)));
IntPtr ptrCount = Marshal.AllocHGlobal(sizeof(uint));
Marshal.WriteInt32(ptrCount, 1);
try
{
if (!ZDBC.ZDBC_GetMessageById(chDbcHandle, ID, ptrDbcMsg)) return false;
var msg = (ZDBC.DBCMessage)Marshal.PtrToStructure(ptrDbcMsg, typeof(ZDBC.DBCMessage));
// 根据传入的物理值设置对应信号的原始值
for (int i = 0; i < msg.nSignalCount; i++)
{
var signal = msg.vSignals[i];
string signalName = Encoding.Default.GetString(signal.strName).TrimEnd('\0');
if (.TryGetValue(signalName, out double physicalValue))
{
signal.nRawvalue = (signal, physicalValue);
msg.vSignals[i] = signal;
}
}
Marshal.StructureToPtr(msg, ptrDbcMsg, true);
if (使CANFD == 0)
{
IntPtr ptrCanFrame = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.can_frame)));
try
{
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));
try
{
Marshal.StructureToPtr(txData, pTx, true);
return ZLGCAN.ZCAN_Transmit(_channelHandles[], pTx, 1) == 1;
}
finally
{
Marshal.FreeHGlobal(pTx);
}
}
finally
{
Marshal.FreeHGlobal(ptrCanFrame);
}
}
else
{
IntPtr ptrCanFDFrame = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.canfd_frame)));
try
{
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));
try
{
Marshal.StructureToPtr(txFdData, pTxFd, true);
return ZLGCAN.ZCAN_TransmitFD(_channelHandles[], pTxFd, 1) == 1;
}
finally
{
Marshal.FreeHGlobal(pTxFd);
}
}
finally
{
Marshal.FreeHGlobal(ptrCanFDFrame);
}
}
}
finally
{
Marshal.FreeHGlobal(ptrDbcMsg);
Marshal.FreeHGlobal(ptrCount);
}
}
/// <summary>
/// 不依赖通道锁的物理值到原始值转换。
/// </summary>
private static ulong (ZDBC.DBCSignal , double )
{
IntPtr pSignal = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCSignal)));
IntPtr pValue = Marshal.AllocHGlobal(sizeof(double));
try
{
Marshal.StructureToPtr(, pSignal, true);
Marshal.StructureToPtr(, pValue, true);
return ZDBC.ZDBC_CalcRawValue(pSignal, pValue);
}
finally
{
Marshal.FreeHGlobal(pSignal);
Marshal.FreeHGlobal(pValue);
}
}
#endregion
#region 4.1 仿/ ()
/// <summary>
/// 设置并发送指定信号值。
/// 其余未指定信号使用 DBC 中定义的初始值填充,避免总线数据被意外清零。
/// </summary>
/// <param name="通道号">CAN/CANFD 通道索引</param>
/// <param name="帧ID">对应报文的帧 ID</param>
/// <param name="信号名称">DBC 中定义的英文信号名称(不区分大小写)</param>
/// <param name="物理值">要写入的实际物理数值</param>
/// <param name="循环发送间隔毫秒">0 = 单次发送;>0 = 周期循环发送(单位毫秒)</param>
/// <returns>操作是否成功</returns>
public virtual bool (uint , uint ID, string , double , int = 0)
{
if ( >= _maxChannels || _channelHandles[] == IntPtr.Zero) return false;
if (!_isDbcLoadedArray[]) return false;
if (string.IsNullOrWhiteSpace()) return false;
var merged = (, ID, new Dictionary<string, double>(StringComparer.OrdinalIgnoreCase)
{
{ , }
});
return DBC定义报文(, ID, merged, );
}
/// <summary>
/// 发送报文:以 DBC 中所有信号的初始值作为默认值发送。
/// 如果之前调用 设置报文 保存过覆盖值,则会优先使用覆盖值。
/// </summary>
/// <param name="通道号">CAN/CANFD 通道索引</param>
/// <param name="帧ID">DBC 中定义的帧 ID</param>
/// <param name="循环发送间隔毫秒">0 = 单次发送;>0 = 周期循环发送(毫秒)</param>
/// <param name="是否使用CANFD">1 = 以 CANFD 格式发送0 = 经典 CAN 格式</param>
public virtual bool (uint , uint ID, int = 0, int 使CANFD = 1)
{
if ( >= _maxChannels || _channelHandles[] == IntPtr.Zero) return false;
if (!_isDbcLoadedArray[]) return false;
if ( < 0) throw new ArgumentOutOfRangeException(nameof());
var merged = (, ID);
return DBC定义报文(, ID, merged, , 使CANFD);
}
/// <summary>
/// 发送自定义报文(原始字节)。
/// </summary>
/// <param name="通道号">CAN/CANFD 通道索引</param>
/// <param name="帧ID">帧 ID扩展帧会自动置位 0x80000000</param>
/// <param name="原始数据">原始帧数据</param>
/// <param name="dlcLength">数据长度CANFD 仅允许 0-8,12,16,20,24,32,48,64</param>
/// <param name="是否是扩展帧">是否为 29 位扩展帧</param>
/// <param name="是否是CANFD">true = CANFDfalse = 经典 CAN</param>
/// <param name="开启波特率加速BRS">CANFD 是否开启波特率加速</param>
public virtual bool (
uint ,
uint ID,
byte[] ,
byte dlcLength,
bool = false,
bool CANFD = true,
bool BRS = true)
{
if ( >= _maxChannels || _channelHandles[] == IntPtr.Zero) return false;
if ( == null) return false;
// 扩展帧需要把最高位标志位置起来
uint canId = ID & 0x7FFFFFFF;
if () canId |= 0x80000000;
lock (_channelLocks[])
{
if (CANFD)
{
// CANFD 有效 DLC 校验
byte validFdDlc = CANFD的Dlc(dlcLength);
ZLGCAN.canfd_frame fdFrame = new ZLGCAN.canfd_frame
{
can_id = canId,
len = validFdDlc,
flags = (byte)(( ? 0x01 : 0x00) | (BRS ? 0x02 : 0x00) | 0x20), // 0x20 本地回显
data = new byte[64]
};
Array.Copy(, 0, fdFrame.data, 0, Math.Min(.Length, 64));
ZLGCAN.ZCAN_TransmitFD_Data txFdData = new ZLGCAN.ZCAN_TransmitFD_Data { frame = fdFrame, transmit_type = 0 };
IntPtr pTxFd = Marshal.AllocHGlobal(Marshal.SizeOf(txFdData));
try
{
Marshal.StructureToPtr(txFdData, pTxFd, true);
return ZLGCAN.ZCAN_TransmitFD(_channelHandles[], pTxFd, 1) == 1;
}
finally { Marshal.FreeHGlobal(pTxFd); }
}
else
{
ZLGCAN.can_frame standardFrame = new ZLGCAN.can_frame
{
can_id = canId,
can_dlc = dlcLength > 8 ? (byte)8 : dlcLength,
__pad = 0x20, // 发送回显
data = new byte[8]
};
Array.Copy(, 0, standardFrame.data, 0, Math.Min(.Length, 8));
ZLGCAN.ZCAN_Transmit_Data txData = new ZLGCAN.ZCAN_Transmit_Data { frame = standardFrame, transmit_type = 0 };
IntPtr pTx = Marshal.AllocHGlobal(Marshal.SizeOf(txData));
try
{
Marshal.StructureToPtr(txData, pTx, true);
return ZLGCAN.ZCAN_Transmit(_channelHandles[], pTx, 1) == 1;
}
finally { Marshal.FreeHGlobal(pTx); }
}
}
}
/// <summary>
/// 构建发送用信号字典DBC 初始值 + 额外覆盖。
/// 调用方需自行保证通道已初始化且 DBC 已加载。
/// </summary>
private Dictionary<string, double> (uint , uint ID, Dictionary<string, double>? = null)
{
uint chDbcHandle = _dbcHandles[];
IntPtr ptrDbcMsg = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCMessage)));
try
{
lock (_channelLocks[])
{
if (!ZDBC.ZDBC_GetMessageById(chDbcHandle, ID, ptrDbcMsg))
return new Dictionary<string, double>(StringComparer.OrdinalIgnoreCase);
var msg = (ZDBC.DBCMessage)Marshal.PtrToStructure(ptrDbcMsg, typeof(ZDBC.DBCMessage));
var dict = new Dictionary<string, double>(StringComparer.OrdinalIgnoreCase);
for (int i = 0; i < msg.nSignalCount; i++)
{
var signal = msg.vSignals[i];
string signalName = Encoding.Default.GetString(signal.strName).TrimEnd('\0');
if (!string.IsNullOrEmpty(signalName) && signal.initialValueValid != 0)
{
// ZDBC 文档标注 initialValue 为“原始值”,但实际按 DBC 规范这里存放的是物理值
dict[signalName] = signal.initialValue;
}
}
if ( != null)
{
foreach (var kvp in )
{
dict[kvp.Key] = kvp.Value;
}
}
return dict;
}
}
finally
{
Marshal.FreeHGlobal(ptrDbcMsg);
}
}
/// <summary>
/// CANFD 有效 DLC 校验,非法值自动就近取到下一个合法 DLC。
/// </summary>
private static byte CANFD的Dlc(byte dlc)
{
if (dlc <= 8) return dlc;
if (dlc <= 12) return 12;
if (dlc <= 16) return 16;
if (dlc <= 20) return 20;
if (dlc <= 24) return 24;
if (dlc <= 32) return 32;
if (dlc <= 48) return 48;
return 64;
}
#endregion
#region 5.
/// <summary>
/// 计算物理值也需要传入对应通道使用通道专属的DBC句柄进行计算
/// </summary>
[Browsable(false)]
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卡设备();
}
}
}

1150
ZLGUSBCANFD/ZLGAPI.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Common\Common.csproj" />
</ItemGroup>
</Project>