From 559890c482f5973f06aafdfec9550fee5ba3c7f2 Mon Sep 17 00:00:00 2001
From: wenxiangchao <wenxiangchao@ihengxin.com.cn>
Date: 星期一, 09 十一月 2026 17:02:15 +0800
Subject: [PATCH] feat: CAN 模拟器 TCP 化 + 多设备并发调试 + 后台生命周期管理

---
 Plugin.DeviceDebug/Model/DebugSessionManager.cs |  107 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 101 insertions(+), 6 deletions(-)

diff --git a/Plugin.DeviceDebug/Model/DebugSessionManager.cs b/Plugin.DeviceDebug/Model/DebugSessionManager.cs
index 334c0b1..16cd94d 100644
--- a/Plugin.DeviceDebug/Model/DebugSessionManager.cs
+++ b/Plugin.DeviceDebug/Model/DebugSessionManager.cs
@@ -19,6 +19,20 @@
         // 鍐呭瓨涓殑娲昏穬浼氳瘽缂撳瓨
         private readonly Dictionary<string, DebugSession> _activeSessions = new();
 
+        /// <summary>
+        /// 缁存姢绉熺害蹇冭烦锛氫笌 LayeredResourceManager.DefaultTtlSeconds(300s) 瀵归綈锛�
+        /// 姣� 120s 缁湡涓�娆★紝闃叉闀挎椂闂寸淮鎶や細璇濈殑 arb.db 绉熺害杩囨湡杩涘叆 RecoveryRequired 鍚庣姝� I/O銆�
+        /// </summary>
+        private System.Threading.Timer _renewTimer;
+        private const int MaintenanceTtlSeconds = 300;
+        private const int RenewIntervalMs = 120_000;
+
+        /// <summary>
+        /// 浼氳瘽鈫掕祫婧愪护鐗岀紦瀛橈細LayeredResourceManager 鐨勭绾︽寜 TokenId 绱㈠紩锛�
+        /// 鑰� ResourceOwnerToken.TokenId 姣忔 new 閮芥槸鏂� GUID锛屽繀椤诲鐢ㄥ悓涓�瀹炰緥鎵嶈兘閲婃斁鍒扮绾�
+        /// </summary>
+        private readonly Dictionary<string, ResourceOwnerToken> _sessionOwnerTokens = new();
+
         public DebugSessionManager(DeviceDebugModel model, IResourceManager? resourceManager = null)
         {
             _model = model;
@@ -36,8 +50,22 @@
                 var existingSession = GetActiveSessionByDevice(deviceId);
                 if (existingSession != null)
                 {
-                    log.Warn($"璁惧 {deviceId} 宸叉湁娲昏穬浼氳瘽: {existingSession.SessionId}");
-                    return existingSession;
+                    // 杩涚▼鍐呭瓨涓瓨鍦� = 鏈細璇濈湡娲昏穬锛岀洿鎺ュ鐢�
+                    if (_activeSessions.ContainsKey(existingSession.SessionId))
+                    {
+                        log.Warn($"璁惧 {deviceId} 宸叉湁娲昏穬浼氳瘽: {existingSession.SessionId}");
+                        return existingSession;
+                    }
+
+                    // 浠呭瓨鍦ㄤ簬鏁版嵁搴撲笖涓嶅湪鏈繘绋嬪唴瀛� = 鏃ц繘绋嬮��鍑哄墠鐨勬畫鐣欎細璇濓細
+                    // 鍏跺唴瀛樹笂涓嬫枃/缁存姢绉熺害锛坅rb.db 渚ч潬 TTL 杩囨湡+鍥炴敹锛夊凡闅忚繘绋嬫秷浜★紝
+                    // 蹇呴』鍥炴敹涓� Closed 鍐嶆柊寤猴紝鍚﹀垯 RequestMaintenance 姘歌繙鈥滀細璇濅笉瀛樺湪鈥濇嫆缁�
+                    _activeSessions[existingSession.SessionId] = existingSession;
+                    existingSession.State = DebugSessionState.Closed;
+                    existingSession.ClosedAt = DateTime.Now;
+                    _model.UpdateSession(existingSession);
+                    _activeSessions.Remove(existingSession.SessionId);
+                    log.Warn($"宸插洖鏀惰澶� {deviceId} 鐨勬畫鐣欎細璇� {existingSession.SessionId}锛堣繘绋嬮噸鍚鑷存浼氳瘽锛夛紝鏂板缓浼氳瘽鏇夸唬");
                 }
 
                 var session = new DebugSession
@@ -75,8 +103,15 @@
             {
                 if (!_activeSessions.TryGetValue(sessionId, out var session))
                 {
-                    log.Warn($"浼氳瘽涓嶅瓨鍦�: {sessionId}");
-                    return false;
+                    // 鍏滃簳锛氫細璇濆彲鑳藉湪鏁版嵁搴擄紙濡� UI 鎸佹湁鐨� sessionID 灞炰簬閲嶅惎鍓嶆畫鐣欙級鈫� 鎭㈠杩涚紦瀛樺悗缁х画
+                    session = _model.GetSession(sessionId);
+                    if (session == null || session.State == DebugSessionState.Closed)
+                    {
+                        log.Warn($"浼氳瘽涓嶅瓨鍦�: {sessionId}");
+                        return false;
+                    }
+                    _activeSessions[sessionId] = session;
+                    log.Warn($"浼氳瘽 {sessionId} 浠庢暟鎹簱鎭㈠杩涘唴瀛樼紦瀛樺悗缁х画缁存姢璇锋眰");
                 }
 
                 // 楠岃瘉鐘舵�佽浆鎹�
@@ -90,7 +125,12 @@
                 if (_resourceManager != null)
                 {
                     var resourceKey = new ResourceKey(session.DeviceId, "Generic");
-                    var ownerToken = new ResourceOwnerToken(OwnerType.Maintenance, session.SessionId);
+                    // 澶嶇敤宸茬紦瀛樹护鐗岋紙娈嬬暀浼氳瘽浠� DB 鎭㈠鍚庨娆¤繘鍏ユ椂鍚屾牱鏂板缓骞跺瓨鍏ワ級
+                    if (!_sessionOwnerTokens.TryGetValue(sessionId, out var ownerToken))
+                    {
+                        ownerToken = new ResourceOwnerToken(OwnerType.Maintenance, session.SessionId);
+                        _sessionOwnerTokens[sessionId] = ownerToken;
+                    }
 
                     try
                     {
@@ -100,6 +140,7 @@
                     }
                     catch (ResourceConflictException)
                     {
+                        _sessionOwnerTokens.Remove(sessionId);
                         log.Warn($"璁惧 {session.DeviceId} 宸茶鐢熶骇棰勭害鍗犵敤锛屾嫆缁濈淮鎶よ姹�");
                         return false;
                     }
@@ -152,6 +193,9 @@
                 // 鎸佷箙鍖�
                 _model.UpdateSession(session);
 
+                // 鍚姩缁存姢绉熺害蹇冭烦缁湡锛堥槻姝� TTL 杩囨湡锛�
+                EnsureRenewHeartbeat();
+
                 log.Info($"浼氳瘽 {sessionId} 婵�娲荤淮鎶ゆā寮�");
             }
             catch (Exception ex)
@@ -159,6 +203,48 @@
                 log.Error($"婵�娲荤淮鎶ゆā寮忓け璐�: {ex.Message}", ex);
                 throw;
             }
+        }
+
+        /// <summary>棣栨杩涘叆缁存姢鏃跺惎鍔ㄥ懆鏈熺画鏈熷畾鏃跺櫒锛堝箓绛夛級銆�</summary>
+        private void EnsureRenewHeartbeat()
+        {
+            if (_renewTimer != null) return;
+            _renewTimer = new System.Threading.Timer(_ => RenewActiveMaintenance(),
+                null, RenewIntervalMs, RenewIntervalMs);
+            log.Info("缁存姢绉熺害蹇冭烦宸插惎鍔�");
+        }
+
+        /// <summary>鍛ㄦ湡鍥炶皟锛氬鎵�鏈� MaintenanceActive 浼氳瘽缁湡鍏� arb.db 绉熺害銆�</summary>
+        private void RenewActiveMaintenance()
+        {
+            var lrm = _resourceManager as Aging.Infrastructure.Core.LayeredResourceManager;
+            if (lrm == null) return; // 闈炲垎灞傚疄鐜帮紙鏃� Coordinator锛夋椂闄嶇骇涓嶇画鏈�
+            try
+            {
+                foreach (var kv in _activeSessions.ToArray())
+                {
+                    var s = kv.Value;
+                    if (s.State != DebugSessionState.MaintenanceActive) continue;
+                    if (!_sessionOwnerTokens.TryGetValue(s.SessionId, out var token)) continue;
+                    if (!lrm.TryGetGrants(token.TokenId, out var grants) || grants == null) continue;
+                    foreach (var g in grants)
+                    {
+                        try { lrm.Coordinator.Renew(g, MaintenanceTtlSeconds); }
+                        catch (Exception ex) { log.Warn($"缁湡璁惧 {s.DeviceId} 绉熺害澶辫触: {ex.Message}"); }
+                    }
+                }
+            }
+            catch (Exception ex) { log.Error($"缁存姢绉熺害蹇冭烦寮傚父: {ex.Message}", ex); }
+        }
+
+        /// <summary>鏃犵淮鎶や腑浼氳瘽鏃跺仠姝㈠績璺筹紝閬垮厤绌鸿浆銆�</summary>
+        private void StopRenewHeartbeatIfIdle()
+        {
+            if (_renewTimer == null) return;
+            if (_activeSessions.Values.Any(s => s.State == DebugSessionState.MaintenanceActive)) return;
+            try { _renewTimer.Dispose(); } catch { }
+            _renewTimer = null;
+            log.Info("鏃犵淮鎶や腑浼氳瘽锛岀淮鎶ょ绾﹀績璺冲凡鍋滄");
         }
 
         /// <summary>
@@ -193,11 +279,17 @@
                     try
                     {
                         var resourceKey = new ResourceKey(session.DeviceId, "Generic");
-                        var ownerToken = new ResourceOwnerToken(OwnerType.Maintenance, session.SessionId);
+                        // 蹇呴』澶嶇敤杩涘叆缁存姢鏃剁殑鍚屼竴浠ょ墝瀹炰緥锛歀ayeredResourceManager 鎸� TokenId(GUID) 绱㈠紩绉熺害锛�
+                        // 閲嶆柊 new 浼氬鑷� FindGrant 钀界┖銆佺绾︽案涓嶉噴鏀锯啋杩囨湡鍚� RecoveryRequired 姘镐箙闃诲鍚庣画缁存姢
+                        if (!_sessionOwnerTokens.TryGetValue(sessionId, out var ownerToken))
+                        {
+                            ownerToken = new ResourceOwnerToken(OwnerType.Maintenance, session.SessionId);
+                        }
 
                         _resourceManager.ReleaseReservationAsync(
                             ownerToken, resourceKey, CancellationToken.None).GetAwaiter().GetResult();
 
+                        _sessionOwnerTokens.Remove(sessionId);
                         log.Info($"宸查噴鏀捐澶� {session.DeviceId} 鐨勮祫婧愰绾�");
                     }
                     catch (Exception ex)
@@ -215,6 +307,9 @@
                 // 浠庣紦瀛樹腑绉婚櫎
                 _activeSessions.Remove(sessionId);
 
+                // 鑻ユ棤缁存姢涓細璇濆垯鍋滄蹇冭烦
+                StopRenewHeartbeatIfIdle();
+
                 log.Info($"浼氳瘽 {sessionId} 宸插叧闂�");
             }
             catch (Exception ex)

--
Gitblit v1.9.1