wenxiangchao
3 天以前 b544d5cccd76735e034c8055918a50927b437474
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
using log4net;
using Plugin.DeviceDebug.Entity;
using System;
using System.Windows;
using System.Windows.Controls;
 
namespace Plugin.DeviceDebug.Panels
{
    /// <summary>
    /// CAN 调试面板多通道壳:以 TabControl 承载若干 <see cref="CanChannelView"/>,
    /// 支持同屏并列多个 CAN 通道独立收发(验收场景 4)。
    /// 对外保持与旧单面板一致的属性契约(DeviceId/InitialBusName/InitialNodeIndex/
    /// AutoConnectOnLoad/ExistingDriver/IsMaintenanceMode/OnCommandExecute),
    /// DeviceDebug 选设备时这些配置被转发到"设备通道"(首个 Tab)。
    /// </summary>
    public partial class CanDebugPanel : UserControl
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(CanDebugPanel));
 
        public static readonly DependencyProperty IsMaintenanceModeProperty =
            DependencyProperty.Register(nameof(IsMaintenanceMode), typeof(bool), typeof(CanDebugPanel),
                new PropertyMetadata(false, OnMaintenanceModeChanged));
 
        public bool IsMaintenanceMode
        {
            get => (bool)GetValue(IsMaintenanceModeProperty);
            set => SetValue(IsMaintenanceModeProperty, value);
        }
 
        private static void OnMaintenanceModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var panel = (CanDebugPanel)d;
            if (panel._deviceChannel != null)
                panel._deviceChannel.IsMaintenanceMode = (bool)e.NewValue;
        }
 
        /// <summary>命令执行事件(任一通道触发均上抛,供 DeviceDebug 统一处理)</summary>
        public event EventHandler<DebugCommand>? OnCommandExecute;
 
        private CanChannelView? _deviceChannel;
        private int _manualSeq;
 
        // 待转发到设备通道的配置(可能早于通道创建)
        private string _deviceId = string.Empty;
        private string _initialBusName;
        private int? _initialNodeIndex;
        private bool _autoConnectOnLoad;
        private Aging.Infrastructure.Contracts.IDeviceDriver? _existingDriver;
 
        public CanDebugPanel()
        {
            InitializeComponent();
        }
 
        /// <summary>设备ID(转发到设备通道)</summary>
        public string DeviceId
        {
            get => _deviceId;
            set { _deviceId = value; EnsureDeviceChannel().DeviceId = value; }
        }
 
        /// <summary>总线名称(转发到设备通道)</summary>
        public string InitialBusName
        {
            get => _initialBusName;
            set { _initialBusName = value; EnsureDeviceChannel().InitialBusName = value; }
        }
 
        /// <summary>节点编号(转发到设备通道)</summary>
        public int? InitialNodeIndex
        {
            get => _initialNodeIndex;
            set { _initialNodeIndex = value; EnsureDeviceChannel().InitialNodeIndex = value; }
        }
 
        /// <summary>加载即自动连接(转发到设备通道)</summary>
        public bool AutoConnectOnLoad
        {
            get => _autoConnectOnLoad;
            set { _autoConnectOnLoad = value; EnsureDeviceChannel().AutoConnectOnLoad = value; }
        }
 
        /// <summary>外部已注册驱动(转发到设备通道,共享设备复用宿主通路)</summary>
        public Aging.Infrastructure.Contracts.IDeviceDriver? ExistingDriver
        {
            get => _existingDriver;
            set { _existingDriver = value; EnsureDeviceChannel().ExistingDriver = value; }
        }
 
        /// <summary>惰性创建"设备通道"(首个 Tab),并挂接命令事件与当前维护态。</summary>
        private CanChannelView EnsureDeviceChannel()
        {
            if (_deviceChannel != null) return _deviceChannel;
 
            _deviceChannel = new CanChannelView();
            WireChannel(_deviceChannel, MakeHeader(_deviceId, _initialBusName, _initialNodeIndex));
            // 应用已到达的维护态
            _deviceChannel.IsMaintenanceMode = IsMaintenanceMode;
            var devTab = MakeTab(_deviceChannel);
            devTab.Header = MakeHeader(_deviceId, _initialBusName, _initialNodeIndex);
            channelTabs.Items.Add(devTab);
            return _deviceChannel;
        }
 
        private void WireChannel(CanChannelView view, string header)
        {
            view.OnCommandExecute += (s, e) => OnCommandExecute?.Invoke(this, e);
        }
 
        private TabItem MakeTab(CanChannelView view)
        {
            // 复用视图自身的标题区不显示设备名,这里用 Tab 头标注通道身份
            return new TabItem { Content = view };
        }
 
        private static string MakeHeader(string deviceId, string bus, int? node)
        {
            string id = string.IsNullOrEmpty(deviceId) ? "通道" : deviceId;
            if (!string.IsNullOrEmpty(bus))
                return $"{id} @{bus}:{node}";
            return id;
        }
 
        /// <summary>手动添加一个可编辑的空白通道(Local TCP 直连,用户自填总线/节点后连接)。</summary>
        private void AddChannelBtn_Click(object sender, RoutedEventArgs e)
        {
            var view = new CanChannelView { AutoConnectOnLoad = false };
            _manualSeq++;
            string header = $"CAN 通道 #{_manualSeq}";   // 手动通道具体总线/节点由用户页内填写,标签不预设
            WireChannel(view, header);
            var tab = MakeTab(view);
            tab.Header = header;
            channelTabs.Items.Add(tab);
            channelTabs.SelectedItem = tab;
            log.Info($"[CanDebugPanel] 手动添加通道 {header}");
        }
 
        /// <summary>关闭当前选中的通道(设备通道不可关闭,避免误退出 DeviceDebug 会话视图)。</summary>
        private void CloseChannelBtn_Click(object sender, RoutedEventArgs e)
        {
            if (channelTabs.SelectedItem is not TabItem tab) return;
            if (tab.Content == _deviceChannel)
            {
                MessageBox.Show("设备通道不可在此关闭,请从设备选择切换。", "提示",
                    MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }
            if (tab.Content is CanChannelView view)
                view.DisconnectInternalSafe();
            channelTabs.Items.Remove(tab);
        }
 
        /// <summary>更新设备通道 Tab 头(在配置齐备后调用一次即可)。</summary>
        public void RefreshDeviceHeader()
        {
            if (_deviceChannel == null || channelTabs.Items.Count == 0) return;
            if (channelTabs.Items[0] is TabItem t)
                t.Header = MakeHeader(_deviceId, _initialBusName, _initialNodeIndex);
        }
    }
}