419 lines
14 KiB
C#
419 lines
14 KiB
C#
using Common.Attributes;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.ComponentModel;
|
||
using System.Diagnostics;
|
||
using System.IO;
|
||
using System.Runtime.InteropServices;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using static Common.Attributes.ATSCommandAttribute;
|
||
|
||
namespace ZLGCANFD
|
||
{
|
||
/// <summary>
|
||
/// ZLG CAN 驱动类 (由 TSMaster CAN 改写)
|
||
/// </summary>
|
||
[ATSCommand]
|
||
[DeviceCategory("CAN卡驱动")]
|
||
public class CAN
|
||
{
|
||
public static event Action ConnectEvent;
|
||
public static event Action DisConnectEvent;
|
||
public static bool ConnectFlag { get; set; } = false;
|
||
|
||
// 存储 ZLG 的设备句柄和多通道句柄
|
||
private static nint _deviceHandle = 0;
|
||
private static Dictionary<uint, nint> _channelHandles = new Dictionary<uint, nint>();
|
||
|
||
/// <summary>
|
||
/// 用于接收 CAN FD 报文的队列
|
||
/// </summary>
|
||
private static readonly Queue<ZLGCAN.ZCAN_ReceiveFD_Data> _receivedMessages = new Queue<ZLGCAN.ZCAN_ReceiveFD_Data>();
|
||
private static readonly object _lock = new object(); // 线程安全锁
|
||
|
||
// 接收线程控制标志
|
||
private static bool _isReceiving = false;
|
||
public static Action<ZLGCAN.ZCAN_ReceiveFD_Data> listener; // ZLG 接收事件委托
|
||
|
||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||
public class ZCAN_CANFD_FRAME
|
||
{
|
||
public uint can_id; // 报文ID (包含扩展帧标志位)
|
||
public byte len; // 数据长度
|
||
public byte flags; // CANFD标志 (0x01:CANFD, 0x02:BRS, 0x04:ESI)
|
||
public byte __res0; // 保留
|
||
public byte __res1; // 保留
|
||
|
||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
|
||
public byte[] data; // 数据负载
|
||
|
||
// --- 添加以下属性以解决报错并适配旧代码 ---
|
||
|
||
/// <summary>
|
||
/// 模拟 eff 字段:1表示扩展帧,0表示标准帧
|
||
/// </summary>
|
||
public uint eff
|
||
{
|
||
get { return (can_id & 0x80000000) != 0 ? 1u : 0u; }
|
||
set
|
||
{
|
||
if (value != 0)
|
||
can_id |= 0x80000000; // 将最高位置1,表示扩展帧
|
||
else
|
||
can_id &= 0x7FFFFFFF; // 将最高位清0,表示标准帧
|
||
}
|
||
}
|
||
|
||
public ZCAN_CANFD_FRAME()
|
||
{
|
||
data = new byte[64];
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化 CAN 驱动
|
||
/// </summary>
|
||
[Browsable(false)]
|
||
public static int Init(uint deviceType, uint deviceIndex)
|
||
{
|
||
if (_deviceHandle != 0) return 0;
|
||
|
||
_deviceHandle = ZLGCAN.ZCAN_OpenDevice(deviceType, deviceIndex, 0);
|
||
|
||
if (_deviceHandle == 0)
|
||
{
|
||
Debug.WriteLine("ZLG 设备初始化失败!");
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 释放 CAN 驱动
|
||
/// </summary>
|
||
[Browsable(false)]
|
||
public static void Release()
|
||
{
|
||
DisConnect();
|
||
if (_deviceHandle != 0)
|
||
{
|
||
ZLGCAN.ZCAN_CloseDevice(_deviceHandle);
|
||
_deviceHandle = 0;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 注册监听器
|
||
/// </summary>
|
||
[Browsable(false)]
|
||
public static int RegisterListener(Action<ZLGCAN.ZCAN_ReceiveFD_Data> listenEvent)
|
||
{
|
||
listener = listenEvent;
|
||
|
||
if (!_isReceiving)
|
||
{
|
||
_isReceiving = true;
|
||
Task.Run(ReceiveThreadProcess);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 注销监听器
|
||
/// </summary>
|
||
[Browsable(false)]
|
||
public static int UnRegisterListener()
|
||
{
|
||
listener = null;
|
||
_isReceiving = false;
|
||
return 0;
|
||
}
|
||
|
||
private static void ReceiveThreadProcess()
|
||
{
|
||
while (_isReceiving)
|
||
{
|
||
bool hasData = false;
|
||
foreach (var chnHandle in _channelHandles.Values)
|
||
{
|
||
uint len = ZLGCAN.ZCAN_GetReceiveNum(chnHandle, ZDBC.FT_CANFD);
|
||
if (len > 0)
|
||
{
|
||
int size = Marshal.SizeOf(typeof(ZLGCAN.ZCAN_ReceiveFD_Data));
|
||
nint ptr = Marshal.AllocHGlobal((int)len * size);
|
||
|
||
uint readLen = ZLGCAN.ZCAN_ReceiveFD(chnHandle, ptr, len, 50);
|
||
|
||
for (int i = 0; i < readLen; i++)
|
||
{
|
||
nint pItem = ptr + i * size;
|
||
var frame = Marshal.PtrToStructure<ZLGCAN.ZCAN_ReceiveFD_Data>(pItem);
|
||
|
||
lock (_lock)
|
||
{
|
||
_receivedMessages.Enqueue(frame);
|
||
}
|
||
listener?.Invoke(frame);
|
||
}
|
||
Marshal.FreeHGlobal(ptr);
|
||
hasData = true;
|
||
}
|
||
}
|
||
|
||
if (!hasData) Thread.Sleep(10);
|
||
}
|
||
}
|
||
|
||
public static int Connect(uint channel, ZLGCAN.ZCAN_CHANNEL_INIT_CONFIG config)
|
||
{
|
||
if (_deviceHandle == 0) return -1;
|
||
|
||
nint chnHandle = ZLGCAN.ZCAN_InitCAN(_deviceHandle, channel, ref config);
|
||
if (chnHandle == 0)
|
||
{
|
||
Debug.WriteLine($"CAN 通道 {channel} 初始化失败");
|
||
return -1;
|
||
}
|
||
|
||
if (ZLGCAN.ZCAN_StartCAN(chnHandle) == 1)
|
||
{
|
||
_channelHandles[channel] = chnHandle;
|
||
ConnectFlag = true;
|
||
Task.Run(() => ConnectEvent?.Invoke());
|
||
return 0;
|
||
}
|
||
else
|
||
{
|
||
Debug.WriteLine($"CAN 通道 {channel} 启动失败");
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
public static int DisConnect()
|
||
{
|
||
foreach (var handle in _channelHandles.Values)
|
||
{
|
||
ZLGCAN.ZCAN_ResetCAN(handle);
|
||
}
|
||
_channelHandles.Clear();
|
||
ConnectFlag = false;
|
||
Task.Run(() => DisConnectEvent?.Invoke());
|
||
return 0;
|
||
}
|
||
[Browsable(false)]
|
||
public static int LoadDBC(string filePath, int[] channel, out uint databaseID)
|
||
{
|
||
// 1. 初始化 ZDBC 模块,获取句柄
|
||
databaseID = ZDBC.ZDBC_Init(0, 1);
|
||
|
||
if (databaseID == ZDBC.INVALID_DBC_HANDLE)
|
||
{
|
||
return -1; // 初始化失败
|
||
}
|
||
|
||
// 2. 构造文件信息结构体并加载
|
||
ZDBC.FileInfo fileInfo = new ZDBC.FileInfo();
|
||
fileInfo.strFilePath = new byte[ZDBC._MAX_FILE_PATH_ + 1];
|
||
byte[] pathBytes = System.Text.Encoding.Default.GetBytes(filePath);
|
||
Array.Copy(pathBytes, fileInfo.strFilePath, Math.Min(pathBytes.Length, ZDBC._MAX_FILE_PATH_));
|
||
|
||
fileInfo.type = ZDBC.PROTOCOL_OTHER; // 默认普通 CAN/CANFD
|
||
fileInfo.merge = 0; // 清除原有数据加载新文件
|
||
|
||
// 将结构体封送到非托管内存
|
||
nint pFileInfo = Marshal.AllocHGlobal(Marshal.SizeOf(fileInfo));
|
||
Marshal.StructureToPtr(fileInfo, pFileInfo, false);
|
||
|
||
bool success = ZDBC.ZDBC_LoadFile(databaseID, pFileInfo);
|
||
Marshal.FreeHGlobal(pFileInfo); // 释放临时内存
|
||
|
||
if (!success) return -2; // 加载文件失败
|
||
|
||
// 3. 将加载的数据同步到您本地的缓存(如 MsgDatabase)
|
||
// 注意:ZLG 的加载通常是全局的,如果您的业务逻辑需要分 channel 存储,
|
||
// 需要根据您的解析类进行填充
|
||
foreach (var ch in channel)
|
||
{
|
||
// 这里的 Parse 需要您根据 ZDBC 的 API 重新实现(见下方第2点)
|
||
DBCParse.Parse(databaseID, ch);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
[Browsable(false)]
|
||
public static int UnLoadDBC(uint? databaseID = null)
|
||
{
|
||
// 1. 如果传入了句柄,释放 ZDBC 引擎资源
|
||
if (databaseID.HasValue && databaseID.Value != ZDBC.INVALID_DBC_HANDLE)
|
||
{
|
||
ZDBC.ZDBC_Release(databaseID.Value);
|
||
}
|
||
|
||
// 2. 清除本地缓存
|
||
DBCParse.MsgDatabase.Clear();
|
||
|
||
return 0;
|
||
}
|
||
|
||
#region 信号值获取方法 (ZLG 适配版)
|
||
|
||
public static double GetSignalValue(byte channel, string AMsgName, string ASgnName)
|
||
{
|
||
double value = double.NaN;
|
||
var find = DBCParse.MsgDatabase[channel].FirstOrDefault(s => s.msg_name == AMsgName);
|
||
|
||
if (find != null)
|
||
{
|
||
int re = GetZlgSignalValue(ref find.ACANFD, AMsgName, ASgnName, ref value);
|
||
|
||
if (re != 1)
|
||
{
|
||
Debug.WriteLine($"GetSignalValue: 获取信号值失败, 消息: {AMsgName}, 信号: {ASgnName}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Debug.WriteLine($"GetSignalValue: 未找到消息定义, 消息: {AMsgName}, 通道: {channel}");
|
||
}
|
||
return value;
|
||
}
|
||
|
||
[Browsable(false)]
|
||
public static int GetSignalValue(byte channel, string AMsgName, string ASgnName, ref double value)
|
||
{
|
||
var find = DBCParse.MsgDatabase[channel].FirstOrDefault(s => s.msg_name == AMsgName);
|
||
if (find != null)
|
||
{
|
||
return GetZlgSignalValue(ref find.ACANFD, AMsgName, ASgnName, ref value);
|
||
}
|
||
else
|
||
{
|
||
Debug.WriteLine($"GetSignalValue: 未找到消息定义, 消息: {AMsgName}, 通道: {channel}");
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
[Browsable(false)]
|
||
public static int GetSignalValue(ref ZCAN_CANFD_FRAME ACANFD, string AMsgName, string ASgnName, ref double value)
|
||
{
|
||
return GetZlgSignalValue(ref ACANFD, AMsgName, ASgnName, ref value);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region ZLG 私有解析辅助
|
||
|
||
private static int GetZlgSignalValue(ref ZCAN_CANFD_FRAME frame, string msgName, string sigName, ref double val)
|
||
{
|
||
try
|
||
{
|
||
// 模拟或调用 ZLG 外部 DBC 库进行信号解析
|
||
return 0;
|
||
}
|
||
catch
|
||
{
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
private static Dictionary<string, System.Timers.Timer> _cyclicTimers = new Dictionary<string, System.Timers.Timer>();
|
||
|
||
#region 信号值设置方法 (ZLG 适配版)
|
||
|
||
public static int SetSignalValue(byte channel, string AMsgName, string ASgnName, double AValue, bool isSend = false, float sendPeriod = 0)
|
||
{
|
||
var find = DBCParse.MsgDatabase[channel].FirstOrDefault(s => s.msg_name == AMsgName);
|
||
if (find == null)
|
||
{
|
||
Debug.WriteLine($"SetSignalValue: 未找到消息定义, 消息: {AMsgName}, 通道: {channel}");
|
||
return -1;
|
||
}
|
||
|
||
int re = EncodeZlgSignal(ref find.ACANFD, AMsgName, ASgnName, AValue);
|
||
|
||
if (re != 0)
|
||
{
|
||
Debug.WriteLine($"设置信号值编码失败! 消息:{AMsgName}, 信号:{ASgnName}");
|
||
return re;
|
||
}
|
||
|
||
if (isSend)
|
||
{
|
||
return TransmitLogic(channel, find, sendPeriod);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
public static int SetSignalValue(uint channel, string AMsgName, float sendPeriod = 0)
|
||
{
|
||
var find = DBCParse.MsgDatabase[(byte)channel].FirstOrDefault(s => s.msg_name == AMsgName);
|
||
if (find == null)
|
||
{
|
||
Debug.WriteLine($"SetSignalValue: 未找到消息定义, 消息: {AMsgName}, 通道: {channel}");
|
||
return -1;
|
||
}
|
||
|
||
return TransmitLogic((byte)channel, find, sendPeriod);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 内部辅助逻辑
|
||
|
||
private static int TransmitLogic(byte channel, ZLG_Msg msg, float period)
|
||
{
|
||
if (period <= 0)
|
||
{
|
||
return SendZlgFrame(channel, msg.ACANFD);
|
||
}
|
||
else
|
||
{
|
||
string key = $"{channel}_{msg.msg_id}";
|
||
|
||
if (_cyclicTimers.ContainsKey(key))
|
||
{
|
||
_cyclicTimers[key].Stop();
|
||
_cyclicTimers[key].Dispose();
|
||
}
|
||
|
||
var timer = new System.Timers.Timer(period);
|
||
timer.Elapsed += (s, e) => SendZlgFrame(channel, msg.ACANFD);
|
||
timer.AutoReset = true;
|
||
timer.Enabled = true;
|
||
_cyclicTimers[key] = timer;
|
||
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
private static int SendZlgFrame(byte channel, ZCAN_CANFD_FRAME frame)
|
||
{
|
||
if (!_channelHandles.ContainsKey(channel)) return -1;
|
||
|
||
nint ptr = Marshal.AllocHGlobal(Marshal.SizeOf(frame));
|
||
try
|
||
{
|
||
Marshal.StructureToPtr(frame, ptr, false);
|
||
uint result = ZLGCAN.ZCAN_TransmitFD(_channelHandles[channel], ptr, 1);
|
||
return result == 1 ? 0 : -1;
|
||
}
|
||
finally
|
||
{
|
||
Marshal.FreeHGlobal(ptr);
|
||
}
|
||
}
|
||
|
||
private static int EncodeZlgSignal(ref ZCAN_CANFD_FRAME frame, string msgName, string sigName, double val)
|
||
{
|
||
// 信号编码逻辑实现
|
||
return 0;
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
} |