using System;
|
|
namespace CanSimulator
|
{
|
/// <summary>
|
/// CAN 帧格式类型
|
/// </summary>
|
public enum CanFrameFormat
|
{
|
/// <summary>标准帧 (11-bit ID)</summary>
|
Standard,
|
/// <summary>扩展帧 (29-bit ID)</summary>
|
Extended
|
}
|
|
/// <summary>
|
/// CAN 帧类型
|
/// </summary>
|
public enum CanFrameType
|
{
|
/// <summary>数据帧</summary>
|
Data,
|
/// <summary>远程帧</summary>
|
Remote
|
}
|
|
/// <summary>
|
/// CAN 总线帧数据结构
|
/// </summary>
|
public sealed class CanFrame
|
{
|
/// <summary>
|
/// 帧 ID(标准帧 11-bit,扩展帧 29-bit)
|
/// </summary>
|
public uint FrameId { get; set; }
|
|
/// <summary>
|
/// 帧格式(标准/扩展)
|
/// </summary>
|
public CanFrameFormat Format { get; set; } = CanFrameFormat.Standard;
|
|
/// <summary>
|
/// 帧类型(数据帧/远程帧)
|
/// </summary>
|
public CanFrameType Type { get; set; } = CanFrameType.Data;
|
|
/// <summary>
|
/// 数据负载(最大 8 字节,CAN FD 可达 64 字节)
|
/// </summary>
|
public byte[] Data { get; set; } = Array.Empty<byte>();
|
|
/// <summary>
|
/// 时间戳
|
/// </summary>
|
public DateTime Timestamp { get; set; } = DateTime.Now;
|
|
/// <summary>
|
/// 发送节点标识
|
/// </summary>
|
public string SourceNodeId { get; set; } = string.Empty;
|
|
/// <summary>
|
/// DLC (Data Length Code)
|
/// </summary>
|
public int Dlc => Data.Length;
|
|
/// <summary>
|
/// 将帧 ID 格式化为十六进制字符串
|
/// </summary>
|
public string FrameIdHex => Format == CanFrameFormat.Extended
|
? $"0x{FrameId:X8}"
|
: $"0x{FrameId:X3}";
|
|
/// <summary>
|
/// 将数据格式化为十六进制字符串
|
/// </summary>
|
public string DataHex => BitConverter.ToString(Data).Replace("-", " ");
|
|
/// <summary>
|
/// 从十六进制字符串解析帧 ID
|
/// </summary>
|
public static uint ParseFrameId(string frameIdStr)
|
{
|
if (string.IsNullOrWhiteSpace(frameIdStr))
|
throw new ArgumentException("帧 ID 不能为空", nameof(frameIdStr));
|
|
if (frameIdStr.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
|
{
|
if (!uint.TryParse(frameIdStr.Substring(2), System.Globalization.NumberStyles.HexNumber, null, out uint id))
|
throw new ArgumentException($"无法解析帧 ID: {frameIdStr}");
|
return id;
|
}
|
else
|
{
|
if (!uint.TryParse(frameIdStr, out uint id))
|
throw new ArgumentException($"无法解析帧 ID: {frameIdStr}");
|
return id;
|
}
|
}
|
|
/// <summary>
|
/// 从十六进制字符串解析数据
|
/// </summary>
|
public static byte[] ParseData(string dataStr)
|
{
|
if (string.IsNullOrWhiteSpace(dataStr))
|
return Array.Empty<byte>();
|
|
try
|
{
|
var parts = dataStr.Split(new[] { ' ', '-', ',' }, StringSplitOptions.RemoveEmptyEntries);
|
var bytes = new byte[parts.Length];
|
for (int i = 0; i < parts.Length; i++)
|
{
|
bytes[i] = byte.Parse(parts[i], System.Globalization.NumberStyles.HexNumber);
|
}
|
return bytes;
|
}
|
catch (Exception ex)
|
{
|
throw new ArgumentException($"无法解析数据字符串: {dataStr}, 错误: {ex.Message}");
|
}
|
}
|
|
public override string ToString()
|
{
|
return $"[{Timestamp:HH:mm:ss.fff}] {FrameIdHex} [{Dlc}] {DataHex} (from {SourceNodeId})";
|
}
|
}
|
}
|