BDU/ZLGCANFD/CAN.cs

419 lines
14 KiB
C#
Raw 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.

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
}
}