1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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})";
        }
    }
}