wenxiangchao
2026-11-02 9d1660270272180cd71147d6ddd53d1382643d2e
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
using Aging.Infrastructure.Drivers;
using log4net;
using Plugin.DeviceDebug.Entity;
using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
 
namespace Plugin.DeviceDebug.Panels
{
    /// <summary>
    /// CAN 帧显示模型
    /// </summary>
    public class CanFrameDisplay
    {
        public DateTime Timestamp { get; set; }
        public string FrameId { get; set; } = string.Empty;
        public int Dlc { get; set; }
        public string Data { get; set; } = string.Empty;
        public string Direction { get; set; } = string.Empty; // TX / RX
        public string Source { get; set; } = string.Empty;
    }
 
    /// <summary>
    /// CanDebugPanel.xaml 的交互逻辑
    /// </summary>
    public partial class CanDebugPanel : UserControl
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(CanDebugPanel));
 
        /// <summary>
        /// 是否为维护模式(控制输入可用性)
        /// </summary>
        public static readonly DependencyProperty IsMaintenanceModeProperty =
            DependencyProperty.Register(nameof(IsMaintenanceMode), typeof(bool), typeof(CanDebugPanel),
                new PropertyMetadata(false));
 
        public bool IsMaintenanceMode
        {
            get => (bool)GetValue(IsMaintenanceModeProperty);
            set => SetValue(IsMaintenanceModeProperty, value);
        }
 
        /// <summary>
        /// 命令执行事件
        /// </summary>
        public event EventHandler<DebugCommand>? OnCommandExecute;
 
        /// <summary>
        /// 接收帧列表
        /// </summary>
        public ObservableCollection<CanFrameDisplay> ReceivedFrames { get; } = new();
 
        /// <summary>
        /// CAN 驱动适配器
        /// </summary>
        private CanDriverAdapter? _driver;
 
        /// <summary>
        /// 轮询定时器(用于检查接收邮箱)
        /// </summary>
        private DispatcherTimer? _pollTimer;
 
        /// <summary>
        /// 是否已连接
        /// </summary>
        private bool _isConnected;
 
        /// <summary>
        /// 设备ID(从 DeviceDebug 传入,面板自动拼接 deviceId)
        /// </summary>
        public string DeviceId
        {
            get => _deviceId;
            set
            {
                _deviceId = value;
                if (!string.IsNullOrEmpty(value))
                {
                    // 将设备ID前缀拼接到默认设备标识中,避免与模拟器面板重名
                    UpdateDeviceIdDisplay();
                }
            }
        }
        private string _deviceId = string.Empty;
 
        /// <summary>
        /// 从设备 Endpoint 解析出的总线名称
        /// </summary>
        public string InitialBusName { get; set; }
 
        /// <summary>
        /// 从设备 Endpoint 解析出的节点编号
        /// </summary>
        public int? InitialNodeIndex { get; set; }
 
        /// <summary>
        /// 是否自动连接(从 DeviceDebug 打开时=true)
        /// </summary>
        public bool AutoConnectOnLoad { get; set; }
 
        public CanDebugPanel()
        {
            InitializeComponent();
            receiveList.ItemsSource = ReceivedFrames;
            InitializePollTimer();
            Loaded += CanDebugPanel_Loaded;
        }
 
        private void CanDebugPanel_Loaded(object sender, RoutedEventArgs e)
        {
            Loaded -= CanDebugPanel_Loaded;
 
            // 从设备信息初始化总线/节点输入
            if (!string.IsNullOrEmpty(InitialBusName))
            {
                busNameInput.Text = InitialBusName;
            }
            if (InitialNodeIndex.HasValue)
            {
                nodeIndexInput.Text = InitialNodeIndex.Value.ToString();
            }
 
            // 自动连接
            if (AutoConnectOnLoad)
            {
                Dispatcher.BeginInvoke(new Action(() =>
                {
                    try { DoConnect(); }
                    catch (Exception ex) { log.Warn($"自动连接失败: {ex.Message}"); }
                }), DispatcherPriority.Background);
            }
        }
 
        private void UpdateDeviceIdDisplay()
        {
            if (!string.IsNullOrEmpty(_deviceId) && busNameInput != null)
            {
                // 保留默认总线名,仅记录 deviceId 以供日志使用
                log.Info($"[CanDebugPanel] 关联设备: {_deviceId}");
            }
        }
 
        /// <summary>
        /// 初始化轮询定时器
        /// </summary>
        private void InitializePollTimer()
        {
            _pollTimer = new DispatcherTimer
            {
                Interval = TimeSpan.FromMilliseconds(100) // 100ms 轮询一次
            };
            _pollTimer.Tick += PollTimer_Tick;
        }
 
        /// <summary>
        /// 轮询定时器回调 - 仅更新统计信息(帧显示由事件驱动)
        /// </summary>
        private void PollTimer_Tick(object? sender, EventArgs e)
        {
            if (!_isConnected || _driver == null) return;
 
            try
            {
                // 更新统计
                UpdateStats();
            }
            catch (Exception ex)
            {
                log.Error($"轮询统计更新失败: {ex.Message}");
            }
        }
 
        /// <summary>
        /// 连接按钮点击
        /// </summary>
        private void ConnectBtn_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                DoConnect();
            }
            catch (Exception ex)
            {
                log.Error($"CAN 连接失败: {ex.Message}", ex);
                MessageBox.Show($"连接失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
                UpdateConnectionStatus(false);
            }
        }
 
        /// <summary>
        /// 实际连接逻辑(从 UI 或自动连接调用)
        /// </summary>
        private void DoConnect()
        {
            if (_isConnected) return;
 
            string busName = busNameInput.Text.Trim();
            if (!int.TryParse(nodeIndexInput.Text.Trim(), out int nodeIndex))
            {
                throw new ArgumentException("节点编号格式错误");
            }
 
            // 优先使用 DeviceDebug 传入的设备 ID,否则自动生成一个
            string deviceId = string.IsNullOrEmpty(_deviceId)
                ? $"CAN_Test_{nodeIndex}_{DateTime.Now:HHmmss}"
                : _deviceId;
 
            _driver = new CanDriverAdapter(deviceId, busName, nodeIndex);
 
            // 订阅驱动的帧接收事件(即时显示接收到的帧)
            _driver.DriverFrameReceived += OnDriverFrameReceived;
 
            // 连接
            _driver.ConnectAsync(CancellationToken.None).GetAwaiter().GetResult();
            _isConnected = true;
 
            // 模拟器模式下,连接后自动启用维护模式(允许发送)
            IsMaintenanceMode = true;
            if (maintenanceToggleBtn != null)
            {
                maintenanceToggleBtn.IsChecked = true;
                maintenanceToggleBtn.IsEnabled = true;
            }
 
            // 更新 UI
            UpdateConnectionStatus(true);
            _pollTimer?.Start();
 
            log.Info($"CAN 驱动已连接: Bus={busName}, Node={nodeIndex}, DeviceId={deviceId}");
        }
 
        /// <summary>
        /// 驱动帧接收事件处理 - 即时显示接收到的帧
        /// </summary>
        private void OnDriverFrameReceived(object? sender, CanSimulator.CanFrame frame)
        {
            try
            {
                // 判断方向:如果帧的来源是当前节点,则为自动响应(显示为 RX)
                string direction = "RX";
                string source = frame.SourceNodeId;
 
                AddReceivedFrame(
                    frame.FrameIdHex,
                    frame.DataHex,
                    direction,
                    source
                );
 
                log.Debug($"[即时显示] 接收帧: {frame}");
            }
            catch (Exception ex)
            {
                log.Error($"即时显示帧失败: {ex.Message}", ex);
            }
        }
 
        /// <summary>
        /// 断开按钮点击
        /// </summary>
        private void DisconnectBtn_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                _pollTimer?.Stop();
                if (_driver != null)
                {
                    _driver.DriverFrameReceived -= OnDriverFrameReceived;
                    _driver.DisconnectAsync().GetAwaiter().GetResult();
                    _driver.Dispose();
                }
                _driver = null;
                _isConnected = false;
                IsMaintenanceMode = false;
 
                UpdateConnectionStatus(false);
                log.Info("CAN 驱动已断开");
            }
            catch (Exception ex)
            {
                log.Error($"CAN 断开失败: {ex.Message}", ex);
            }
        }
 
        /// <summary>
        /// 混杂模式按钮点击
        /// </summary>
        private void PromiscuousBtn_Click(object sender, RoutedEventArgs e)
        {
            if (!_isConnected || _driver == null) return;
 
            try
            {
                _driver.QueryAsync("PROMISCUOUS", CancellationToken.None).GetAwaiter().GetResult();
                log.Info("已设置混杂模式(接收所有帧)");
                MessageBox.Show("已设置混杂模式,将接收所有 CAN 帧", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
            }
            catch (Exception ex)
            {
                log.Error($"设置混杂模式失败: {ex.Message}");
            }
        }
 
        /// <summary>
        /// 维护模式切换按钮点击
        /// </summary>
        private void MaintenanceToggleBtn_Click(object sender, RoutedEventArgs e)
        {
            bool isOn = maintenanceToggleBtn.IsChecked == true;
            IsMaintenanceMode = isOn;
 
            if (isOn)
            {
                maintenanceToggleBtn.Content = "维护模式: 开";
                maintenanceToggleBtn.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4CAF50"));
                log.Info("维护模式已开启(可发送 CAN 帧)");
            }
            else
            {
                maintenanceToggleBtn.Content = "维护模式: 关";
                maintenanceToggleBtn.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#9E9E9E"));
                log.Info("维护模式已关闭(只读监控)");
            }
        }
 
        /// <summary>
        /// 自动响应模式切换按钮点击
        /// </summary>
        private void AutoResponseToggleBtn_Click(object sender, RoutedEventArgs e)
        {
            if (!_isConnected || _driver == null) return;
 
            try
            {
                bool isOn = autoResponseToggleBtn.IsChecked == true;
 
                // 通过驱动发送命令启用/禁用自动响应
                string cmd = isOn ? "AUTO_RESPONSE:ON" : "AUTO_RESPONSE:OFF";
                _driver.QueryAsync(cmd, CancellationToken.None).GetAwaiter().GetResult();
 
                if (isOn)
                {
                    autoResponseToggleBtn.Content = "自动响应: 开";
                    autoResponseToggleBtn.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF9800"));
                    log.Info("自动响应模式已开启(收到帧将自动回复)");
                }
                else
                {
                    autoResponseToggleBtn.Content = "自动响应: 关";
                    autoResponseToggleBtn.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#9E9E9E"));
                    log.Info("自动响应模式已关闭");
                }
            }
            catch (Exception ex)
            {
                log.Error($"切换自动响应模式失败: {ex.Message}");
                MessageBox.Show($"切换失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
 
        /// <summary>
        /// 更新连接状态显示
        /// </summary>
        private void UpdateConnectionStatus(bool connected)
        {
            _isConnected = connected;
            if (connected)
            {
                statusIndicator.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4CAF50"));
                statusText.Text = "已连接";
                connectBtn.IsEnabled = false;
                disconnectBtn.IsEnabled = true;
                promiscuousBtn.IsEnabled = true;
                maintenanceToggleBtn.IsEnabled = true;
                autoResponseToggleBtn.IsEnabled = true;
                busNameInput.IsEnabled = false;
                nodeIndexInput.IsEnabled = false;
            }
            else
            {
                statusIndicator.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#9E9E9E"));
                statusText.Text = "未连接";
                connectBtn.IsEnabled = true;
                disconnectBtn.IsEnabled = false;
                promiscuousBtn.IsEnabled = false;
                maintenanceToggleBtn.IsEnabled = false;
                autoResponseToggleBtn.IsEnabled = false;
                busNameInput.IsEnabled = true;
                nodeIndexInput.IsEnabled = true;
            }
        }
 
        /// <summary>
        /// 更新统计信息
        /// </summary>
        private void UpdateStats()
        {
            if (_driver == null) return;
 
            try
            {
                var status = _driver.GetStatus();
                var response = _driver.QueryAsync("STATUS", CancellationToken.None).GetAwaiter().GetResult();
                // 解析状态: "STATUS:Node=..., Active=True, Rx=0, Tx=0, Err=0, Mailbox=0|Bus=..."
                statsText.Text = $"TX: {GetStatValue(response, "Tx")}  RX: {GetStatValue(response, "Rx")}  Err: {GetStatValue(response, "Err")}";
            }
            catch
            {
                // 忽略统计更新失败
            }
        }
 
        /// <summary>
        /// 从状态字符串中提取数值值
        /// </summary>
        private string GetStatValue(string statusStr, string key)
        {
            try
            {
                var parts = statusStr.Split(',');
                foreach (var part in parts)
                {
                    var kv = part.Trim().Split('=');
                    if (kv.Length == 2 && kv[0].Trim() == key)
                    {
                        return kv[1].Trim();
                    }
                }
            }
            catch { }
            return "0";
        }
 
        /// <summary>
        /// 添加接收帧
        /// </summary>
        public void AddReceivedFrame(string frameId, string data, string direction = "RX", string source = "")
        {
            try
            {
                var frame = new CanFrameDisplay
                {
                    Timestamp = DateTime.Now,
                    FrameId = frameId,
                    Dlc = data.Split(new[] { ' ', '-' }, StringSplitOptions.RemoveEmptyEntries).Length,
                    Data = data,
                    Direction = direction,
                    Source = source
                };
 
                Application.Current.Dispatcher.Invoke(() =>
                {
                    ReceivedFrames.Insert(0, frame);
 
                    // 限制显示数量
                    if (ReceivedFrames.Count > 500)
                    {
                        var toRemove = ReceivedFrames.Skip(500).ToList();
                        foreach (var item in toRemove)
                        {
                            ReceivedFrames.Remove(item);
                        }
                    }
                });
            }
            catch (Exception ex)
            {
                log.Error($"添加接收帧失败: {ex.Message}", ex);
            }
        }
 
        /// <summary>
        /// 发送按钮点击
        /// </summary>
        private void SendBtn_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if (!IsMaintenanceMode)
                {
                    MessageBox.Show("当前为只读模式,无法发送数据", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;
                }
 
                if (!_isConnected || _driver == null)
                {
                    MessageBox.Show("请先连接 CAN 总线", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;
                }
 
                string frameIdStr = frameIdInput.Text.Trim();
                string dataStr = dataInput.Text.Trim();
 
                // 验证帧 ID
                uint frameId = CanSimulator.CanFrame.ParseFrameId(frameIdStr);
 
                // 验证数据
                byte[] dataBytes = CanSimulator.CanFrame.ParseData(dataStr);
                if (dataBytes.Length > 8)
                {
                    MessageBox.Show("数据长度不能超过 8 字节", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }
 
                // 通过驱动发送
                string command = $"SEND:{frameIdStr}:{dataStr}";
                var response = _driver.SendCommandAsync(command, CancellationToken.None).GetAwaiter().GetResult();
 
                // 添加到显示列表(TX)
                AddReceivedFrame($"0x{frameId:X3}", BitConverter.ToString(dataBytes).Replace("-", " "), "TX", "LOCAL");
 
                log.Info($"发送 CAN 帧: ID=0x{frameId:X3}, Data={BitConverter.ToString(dataBytes)}, Response={response}");
            }
            catch (Exception ex)
            {
                log.Error($"发送 CAN 帧失败: {ex.Message}", ex);
                MessageBox.Show($"发送失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
 
        /// <summary>
        /// 清空按钮点击
        /// </summary>
        private void ClearBtn_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                ReceivedFrames.Clear();
                log.Info("清空 CAN 接收记录");
            }
            catch (Exception ex)
            {
                log.Error($"清空接收记录失败: {ex.Message}", ex);
            }
        }
    }
}