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 化 + 多设备并发调试 + 后台生命周期管理

---
 CanSimulatorServer/CanSimulatorServer.Core/CanBusManager.cs |   71 +++++++++++++++++++++++++++++++++++
 1 files changed, 71 insertions(+), 0 deletions(-)

diff --git a/CanSimulatorServer/CanSimulatorServer.Core/CanBusManager.cs b/CanSimulatorServer/CanSimulatorServer.Core/CanBusManager.cs
new file mode 100644
index 0000000..b599e8d
--- /dev/null
+++ b/CanSimulatorServer/CanSimulatorServer.Core/CanBusManager.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Concurrent;
+using CanSimulator;
+
+namespace CanSimulatorServer
+{
+    /// <summary>
+    /// 鏈嶅姟绔�荤嚎娉ㄥ唽琛細鎸夊悕绉版噿鍒涘缓 CanBusSimulator锛堝鐢ㄨ繘绋嬪唴璺敱/鑷姩鍝嶅簲閫昏緫锛夛紝
+    /// 骞朵互 (bus, nodeIndex) 涓哄叏灞�鍞竴閿徊瑁佽妭鐐规寕杞姐��
+    /// 鎬荤嚎鍦ㄩ娆¤鎸傝浇鏃惰嚜鍔ㄥ惎鍔紙涓庢棫杩涚▼鍐呴┍鍔ㄨ涓轰竴鑷达紱UI 鍚仠鎸夐挳鍙悗缁帴鍏ワ級銆�
+    /// </summary>
+    public sealed class CanBusManager
+    {
+        private readonly ConcurrentDictionary<string, CanBusSimulator> _buses =
+            new(StringComparer.OrdinalIgnoreCase);
+
+        /// <summary>鎸傝浇鑺傜偣锛氬悓 (bus,nodeIndex) 宸茶鍗犵敤鏃惰繑鍥� false锛圢ODE_BUSY 璇箟锛夈��</summary>
+        /// <param name="onFrameReceived">甯у埌杈捐鑺傜偣鏃剁殑鍥炶皟锛堟湇鍔$鐢ㄤ簬 TCP 鎺ㄩ�侊級</param>
+        public bool TryAttach(string busName, int nodeIndex, string clientTag,
+            Action<CanFrame> onFrameReceived, out CanNode? node, out CanBusSimulator? bus)
+        {
+            bus = GetOrCreateBus(busName);
+            string nodeId = NodeIdOf(nodeIndex);
+
+            // 鍞竴鎬т徊瑁侊細宸插瓨鍦ㄥ悓缂栧彿鑺傜偣鍒欐嫆缁�
+            var existing = bus.GetNode(nodeId);
+            if (existing != null)
+            {
+                node = null;
+                return false;
+            }
+
+            var n = bus.RegisterNode(nodeId, $"{busName}/N{nodeIndex}({clientTag})");
+            n.SetPromiscuousMode();   // 鏈嶅姟绔妭鐐归粯璁ゆ敹鎵�鏈夊抚锛堟护娉㈠櫒鍛戒护鍙湪鍏跺悗瑕嗙洊锛�
+            n.Activate();
+            n.FrameReceived += (_, f) => onFrameReceived(f);
+
+            node = n;
+            return true;
+        }
+
+        /// <summary>娉ㄩ攢鑺傜偣锛堣繛鎺ユ柇寮�鏃惰皟鐢級锛岀┖鎬荤嚎淇濈暀锛堝啀娆℃寕杞介浂寮�閿�锛夈��</summary>
+        public void Detach(string busName, CanNode node)
+        {
+            if (_buses.TryGetValue(busName, out var bus))
+            {
+                bus.UnregisterNode(node.NodeId);
+            }
+        }
+
+        public CanBusSimulator? FindBus(string busName)
+            => _buses.TryGetValue(busName, out var b) ? b : null;
+
+        public System.Collections.Generic.IReadOnlyCollection<CanBusSimulator> AllBuses()
+            => (System.Collections.Generic.IReadOnlyCollection<CanBusSimulator>)_buses.Values;
+
+        private CanBusSimulator GetOrCreateBus(string busName)
+        {
+            var bus = _buses.GetOrAdd(busName, name =>
+            {
+                var b = new CanBusSimulator(name);
+                b.Start(); // 鑷姩鍚姩锛歍CP 妯″紡涓嬫�荤嚎鐢熷懡鍛ㄦ湡鐢辨寕杞介┍鍔�
+                return b;
+            });
+            return bus;
+        }
+
+        /// <summary>鑺傜偣鍦ㄦ�荤嚎涓婄殑韬唤锛氫互缂栧彿瀵诲潃锛堜笌鐪熷疄 CAN 鑺傜偣鍦板潃璇箟涓�鑷达級</summary>
+        public static string NodeIdOf(int nodeIndex) => $"{nodeIndex}";
+    }
+}

--
Gitblit v1.9.1