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
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
using Aging.Infrastructure.Contracts;
using CommunityToolkit.Mvvm.ComponentModel;
using HMI.Base;
using HMI.Core.Util;
using HMI.UI.Util;
using log4net;
using Plugin.DeviceDebug.Model;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
 
namespace Plugin.DeviceDebug
{
    /// <summary>
    /// 硬件调试插件(壳):装配并持有共享基础设施(会话管理器/设备服务/配置库路径),
    /// 以 TabControl 承载多个 <see cref="DeviceDebugTab"/>,支持同时调试多个设备,
    /// 每个 Tab 拥有独立会话与维护租约。设备下拉仅列出尚未打开的设备。
    /// </summary>
    [ObservableObject]
    public partial class DeviceDebug : SimpleMaskControl
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(DeviceDebug));
        private DeviceDebugModel _debugModel;
        private DebugSessionManager _sessionManager;
 
        private IDeviceManager _deviceManager;
        private IDeviceService _deviceService;
        private IResourceManager _resourceManager;
 
        private string _deviceConfigDbPath;
 
        // 全量设备("id [kind]")与已打开标签映射(deviceId → TabItem)
        private readonly List<string> _allDevices = new();
        private readonly Dictionary<string, TabItem> _openTabs = new(StringComparer.OrdinalIgnoreCase);
 
        [ObservableProperty] private ObservableCollection<string> _deviceList = new();
        [ObservableProperty] private string _selectedDevice;
 
        public DeviceDebug()
        {
            DataContext = this;
            InitializeComponent();
        }
 
        public void ForceRefreshForTest()
        {
            try { LoadDeviceList(); }
            catch (Exception ex) { log.Error("ForceRefreshForTest Error.", ex); }
        }
 
        public override void InitFinish()
        {
            base.InitFinish();
            try
            {
                _debugModel = new DeviceDebugModel();
                string dbPath = Path.Combine(BaseFolder, "debug.db");
                _debugModel.InitDatabase(dbPath);
 
                var container = new UtilContainer();
                _deviceManager = container.GetUtil<IDeviceManager>("DeviceManager");
                _deviceService = container.GetUtil<IDeviceService>("DeviceService");
                _resourceManager = container.GetUtil<IResourceManager>("ResourceManager");
 
                _sessionManager = new DebugSessionManager(_debugModel, _resourceManager);
                _deviceConfigDbPath = Path.Combine(BaseFolder, "data.db");
 
                log.Info("硬件调试模块初始化完成");
            }
            catch (Exception ex)
            {
                log.Error("硬件调试模块初始化失败", ex);
                ToastHelper.Error($"硬件调试初始化失败: {ex.Message}");
            }
        }
 
        private void SimpleMaskControl_Loaded(object sender, RoutedEventArgs e)
        {
            LoadDeviceList();
            Loaded -= SimpleMaskControl_Loaded;
        }
 
        private void RfhBtn_Click(object sender, RoutedEventArgs e) => LoadDeviceList();
 
        private void LoadDeviceList()
        {
            try
            {
                if (!File.Exists(_deviceConfigDbPath))
                {
                    log.Warn($"设备配置数据库不存在: {_deviceConfigDbPath}");
                    ToastHelper.Warning("设备配置数据库不存在,请先在设备配置模块中添加设备");
                    return;
                }
 
                var sqliteHelper = new SqliteHelper(_deviceConfigDbPath);
                var dt = sqliteHelper.GetDataTable("SELECT DeviceId, Kind FROM DeviceDefinition ORDER BY DeviceId");
 
                _allDevices.Clear();
                foreach (System.Data.DataRow row in dt.Rows)
                    _allDevices.Add($"{row["DeviceId"]} [{row["Kind"]}]");
 
                RefreshAvailableList();
                log.Info($"加载设备列表完成,共 {_allDevices.Count} 个设备");
            }
            catch (Exception ex)
            {
                log.Error("加载设备列表失败", ex);
                ToastHelper.Error($"加载设备列表失败: {ex.Message}");
            }
        }
 
        /// <summary>下拉仅列出未打开的设备(一会一设备)。</summary>
        private void RefreshAvailableList()
        {
            var avail = _allDevices
                .Where(d => !_openTabs.ContainsKey(ParseDeviceId(d)))
                .ToList();
            DeviceList = new ObservableCollection<string>(avail);
        }
 
        private static string ParseDeviceId(string selection)
        {
            if (string.IsNullOrEmpty(selection)) return string.Empty;
            int idx = selection.IndexOf(" [");
            return idx > 0 ? selection.Substring(0, idx) : selection;
        }
 
        private static string ParseDeviceKind(string selection)
        {
            if (string.IsNullOrEmpty(selection)) return string.Empty;
            int start = selection.IndexOf(" [");
            int end = selection.IndexOf("]");
            if (start >= 0 && end > start)
                return selection.Substring(start + 2, end - start - 2);
            return string.Empty;
        }
 
        private void AddDeviceBtn_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if (string.IsNullOrEmpty(SelectedDevice))
                {
                    ToastHelper.Warning("请先在设备下拉中选择设备");
                    return;
                }
                string deviceId = ParseDeviceId(SelectedDevice);
                string kind = ParseDeviceKind(SelectedDevice);
 
                // 已打开则聚焦其 Tab(防御性:下拉本已过滤)
                if (_openTabs.TryGetValue(deviceId, out var existing))
                {
                    debugTabs.SelectedItem = existing;
                    SelectedDevice = null;
                    return;
                }
 
                var tabContent = new DeviceDebugTab();
                tabContent.Initialize(_debugModel, _sessionManager, _deviceManager, _deviceService,
                    _deviceConfigDbPath, deviceId, kind);
 
                var tab = new TabItem { Header = deviceId, Content = tabContent, Tag = deviceId };
                debugTabs.Items.Add(tab);
                debugTabs.SelectedItem = tab;
                _openTabs[deviceId] = tab;
 
                RefreshAvailableList();
                SelectedDevice = null;
                log.Info($"已加入调试设备: {deviceId}(当前 {debugTabs.Items.Count} 个标签)");
            }
            catch (Exception ex)
            {
                log.Error("加入调试设备失败", ex);
                ToastHelper.Error($"加入调试失败: {ex.Message}");
            }
        }
 
        private void CloseTabBtn_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if (debugTabs.SelectedItem is not TabItem tab) return;
                string deviceId = tab.Tag as string ?? "";
                if (tab.Content is DeviceDebugTab content)
                    content.CloseSessionSilently();   // 关闭即释放维护租约,杜绝泄漏
                debugTabs.Items.Remove(tab);
                if (!string.IsNullOrEmpty(deviceId)) _openTabs.Remove(deviceId);
                RefreshAvailableList();
                log.Info($"已关闭调试标签并释放会话: {deviceId}");
            }
            catch (Exception ex)
            {
                log.Error("关闭调试标签失败", ex);
                ToastHelper.Error($"关闭失败: {ex.Message}");
            }
        }
    }
}