报文设置扩展

This commit is contained in:
hsc
2026-07-06 09:28:29 +08:00
parent 2820b2f300
commit 6c2b975999
12 changed files with 527 additions and 15 deletions

View File

@@ -185,6 +185,7 @@ namespace ZLGUSBCANFD
{
_dbcHandles[] = chDbcHandle;
_isDbcLoadedArray[] = true;
ZLGDBCParser.ParseAndLoadToDatabase(chDbcHandle, 0);
}
else
{
@@ -507,20 +508,25 @@ namespace ZLGUSBCANFD
#endregion
#region 4.1 仿/ ()
#region 4.1 ()
/// <summary>
/// 设置并发送指定信号值。
/// 设置并发送指定信号值(帧 ID 支持 string 兼容模式)
/// 其余未指定信号使用 DBC 中定义的初始值填充,避免总线数据被意外清零。
/// </summary>
/// <param name="通道号">CAN/CANFD 通道索引</param>
/// <param name="帧ID">对应报文的帧 ID</param>
/// <param name="帧IDStr">对应报文的帧 ID(可以是 "26"、"0x1A"、"1A"</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)
public virtual bool (uint , string IDStr, string , double , int = 0)
{
if (!TryParseFrameId(IDStr, out uint ID))
{
Console.WriteLine($"[ZLGCANFD] 设置报文失败: 无法解析的帧 ID '{帧IDStr}'");
return false;
}
if ( >= _maxChannels || _channelHandles[] == IntPtr.Zero) return false;
if (!_isDbcLoadedArray[]) return false;
if (string.IsNullOrWhiteSpace()) return false;
@@ -534,15 +540,21 @@ namespace ZLGUSBCANFD
}
/// <summary>
/// 发送报文:以 DBC 中所有信号的初始值作为默认值发送。
/// 发送报文:以 DBC 中所有信号的初始值作为默认值发送(帧 ID 支持 string 兼容模式)
/// 如果之前调用 设置报文 保存过覆盖值,则会优先使用覆盖值。
/// </summary>
/// <param name="通道号">CAN/CANFD 通道索引</param>
/// <param name="帧ID">DBC 中定义的帧 ID</param>
/// <param name="帧IDStr">DBC 中定义的帧 ID(可以是 "26"、"0x1A"、"1A"</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)
public virtual bool (uint , string IDStr, int = 0, int 使CANFD = 1)
{
if (!TryParseFrameId(IDStr, out uint ID))
{
Console.WriteLine($"[ZLGCANFD] 发送报文失败: 无法解析的帧 ID '{帧IDStr}'");
return false;
}
if ( >= _maxChannels || _channelHandles[] == IntPtr.Zero) return false;
if (!_isDbcLoadedArray[]) return false;
if ( < 0) throw new ArgumentOutOfRangeException(nameof());
@@ -552,10 +564,10 @@ namespace ZLGUSBCANFD
}
/// <summary>
/// 发送自定义报文(原始字节)。
/// 发送自定义报文(原始字节,帧 ID 支持 string 兼容模式)。
/// </summary>
/// <param name="通道号">CAN/CANFD 通道索引</param>
/// <param name="帧ID">帧 ID扩展帧会自动置位 0x80000000</param>
/// <param name="帧IDStr">帧 ID(可以是 "26"、"0x1A"、"1A";扩展帧会自动置位 0x80000000</param>
/// <param name="原始数据">原始帧数据</param>
/// <param name="dlcLength">数据长度CANFD 仅允许 0-8,12,16,20,24,32,48,64</param>
/// <param name="是否是扩展帧">是否为 29 位扩展帧</param>
@@ -563,13 +575,19 @@ namespace ZLGUSBCANFD
/// <param name="开启波特率加速BRS">CANFD 是否开启波特率加速</param>
public virtual bool (
uint ,
uint ID,
string IDStr,
byte[] ,
byte dlcLength,
bool = false,
bool CANFD = true,
bool BRS = true)
{
if (!TryParseFrameId(IDStr, out uint ID))
{
Console.WriteLine($"[ZLGCANFD] 发送自定义报文失败: 无法解析的帧 ID '{帧IDStr}'");
return false;
}
if ( >= _maxChannels || _channelHandles[] == IntPtr.Zero) return false;
if ( == null) return false;
@@ -649,7 +667,6 @@ namespace ZLGUSBCANFD
string signalName = Encoding.Default.GetString(signal.strName).TrimEnd('\0');
if (!string.IsNullOrEmpty(signalName) && signal.initialValueValid != 0)
{
// ZDBC 文档标注 initialValue 为“原始值”,但实际按 DBC 规范这里存放的是物理值
dict[signalName] = signal.initialValue;
}
}
@@ -693,6 +710,49 @@ namespace ZLGUSBCANFD
/// <summary>
/// 计算物理值也需要传入对应通道使用通道专属的DBC句柄进行计算
/// </summary>
/// <summary>
/// 智能解析帧 ID 字符串,兼容 10 进制、16 进制(如 "0x1A", "1A", "26"
/// </summary>
/// <param name="idStr">输入的帧 ID 字符串</param>
/// <param name="frameId">解析成功后的 uint 帧 ID</param>
/// <returns>是否解析成功</returns>
[Browsable(false)]
public static bool TryParseFrameId(string idStr, out uint frameId)
{
frameId = 0;
if (string.IsNullOrWhiteSpace(idStr)) return false;
idStr = idStr.Trim();
try
{
// 1. 处理带 0x 或 0X 的标准 16 进制
if (idStr.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
frameId = Convert.ToUInt32(idStr.Substring(2), 16);
return true;
}
// 2. 尝试当做纯 10 进制解析(如 "26"
if (uint.TryParse(idStr, out frameId))
{
return true;
}
// 3. 如果不是纯数字,但符合 16 进制特征(如 "1A"、"ABC"),尝试按 16 进制解析
// 使用 System.Globalization.NumberStyles.HexNumber
if (uint.TryParse(idStr, System.Globalization.NumberStyles.HexNumber, null, out frameId))
{
return true;
}
}
catch
{
return false;
}
return false;
}
[Browsable(false)]
public ulong (uint , ZDBC.DBCSignal , double )

120
ZLGUSBCANFD/ZLGDBCParser.cs Normal file
View File

@@ -0,0 +1,120 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ZLGUSBCANFD
{
public class _Msg_
{
public int msg_type { get; set; }
public int msg_id { get; set; }
public int msg_cyclic { get; set; }
public byte msg_dlc { get; set; }
public string msg_name { get; set; }
public string[] signal_Name { get; set; }
public int msg_brs { get; set; }
}
public enum Msg_Type
{
CAN = 0,
CANFD = 1,
}
public class ZLGDBCParser
{
// 补齐这个属性4个通道的静态报文数据库缓存供 ViewModel 直接读取
public static List<List<_Msg_>> MsgDatabase { get; set; } =
[.. Enumerable.Range(0, 4).Select(_ => new List<_Msg_>())];
/// <summary>
/// 外部调用:解析特定通道的 DBC 并自动存入对应的 MsgDatabase 缓存中
/// </summary>
/// <param name="chDbcHandle">ZDBC句柄</param>
/// <param name="channel">通道号索引 (0-11)</param>
public static void ParseAndLoadToDatabase(uint chDbcHandle, int channel)
{
if (channel < 0 || channel >= MsgDatabase.Count) return;
// 清空当前通道旧缓存
MsgDatabase[channel].Clear();
// 解析并填充
var parsedList = ParseZlgDbc(chDbcHandle);
MsgDatabase[channel].AddRange(parsedList);
}
/// <summary>
/// 全手工解析周立功 DBC 句柄下的所有消息和信号
/// </summary>
/// <param name="chDbcHandle">对应通道的 ZDBC 句柄</param>
/// <returns>返回对齐同星业务层的自定义报文列表</returns>
public static List<_Msg_> ParseZlgDbc(uint chDbcHandle)
{
List<_Msg_> msgList = new List<_Msg_>();
// 显式指定周立功命名空间下的 INVALID_DBC_HANDLE 校验
if (chDbcHandle == 0 || chDbcHandle == ZLGUSBCANFD.ZDBC.INVALID_DBC_HANDLE)
return msgList;
// 显式指定周立功命名空间下的结构体大小
int msgSize = Marshal.SizeOf(typeof(ZLGUSBCANFD.ZDBC.DBCMessage));
IntPtr pMsg = Marshal.AllocHGlobal(msgSize);
try
{
// 1. 获取周立功 DBC 的第一条报文
bool hasMessage = ZLGUSBCANFD.ZDBC.ZDBC_GetFirstMessage(chDbcHandle, pMsg);
while (hasMessage)
{
// 强制指定周立功底层 DBCMessage 结构映射
var zlgMsg = (ZLGUSBCANFD.ZDBC.DBCMessage)Marshal.PtrToStructure(pMsg, typeof(ZLGUSBCANFD.ZDBC.DBCMessage));
// 1. 优先在字节数组中找到第一个 \0 的位置
int nullIndex = Array.IndexOf(zlgMsg.strName, (byte)0);
_Msg_ myMsg = new _Msg_
{
msg_id = (int)zlgMsg.nID,
// 2. 只翻译 \0 之前的有效字节,彻底解决乱码与规避不必要的内存开销
msg_name = nullIndex >= 0
? Encoding.Default.GetString(zlgMsg.strName, 0, nullIndex)
: Encoding.Default.GetString(zlgMsg.strName).TrimEnd('\0'),
msg_dlc = (byte)zlgMsg.nSize,
msg_cyclic = (int)zlgMsg.nCycleTime,
// 如果底层识别出是扩展帧(nExtend==1),这里对齐映射为你的业务类型
msg_type = zlgMsg.nExtend == 1 ? (int)Msg_Type.CANFD : (int)Msg_Type.CAN,
signal_Name = new string[zlgMsg.nSignalCount]
};
// 2. 提取该报文下的所有信号名称
for (int i = 0; i < zlgMsg.nSignalCount; i++)
{
var signal = zlgMsg.vSignals[i];
string sigName = Encoding.Default.GetString(signal.strName).TrimEnd('\0');
myMsg.signal_Name[i] = sigName;
}
msgList.Add(myMsg);
// 3. 移向下一条报文
hasMessage = ZLGUSBCANFD.ZDBC.ZDBC_GetNextMessage(chDbcHandle, pMsg);
}
}
finally
{
// 彻底释放非托管内存,避免上位机高频切换加载 DBC 时导致内存泄漏
Marshal.FreeHGlobal(pMsg);
}
// 按 ID 排序,对齐你的树状展现或者二分查找业务逻辑
msgList.Sort((a, b) => a.msg_id.CompareTo(b.msg_id));
return msgList;
}
}
}