using System; using System.Collections.Concurrent; using CanSimulator; namespace CanSimulatorServer { /// /// 服务端总线注册表:按名称懒创建 CanBusSimulator(复用进程内路由/自动响应逻辑), /// 并以 (bus, nodeIndex) 为全局唯一键仲裁节点挂载。 /// 总线在首次被挂载时自动启动(与旧进程内驱动行为一致;UI 启停按钮可后续接入)。 /// public sealed class CanBusManager { private readonly ConcurrentDictionary _buses = new(StringComparer.OrdinalIgnoreCase); /// 挂载节点:同 (bus,nodeIndex) 已被占用时返回 false(NODE_BUSY 语义)。 /// 帧到达该节点时的回调(服务端用于 TCP 推送) public bool TryAttach(string busName, int nodeIndex, string clientTag, Action 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; } /// 注销节点(连接断开时调用),空总线保留(再次挂载零开销)。 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 AllBuses() => (System.Collections.Generic.IReadOnlyCollection)_buses.Values; private CanBusSimulator GetOrCreateBus(string busName) { var bus = _buses.GetOrAdd(busName, name => { var b = new CanBusSimulator(name); b.Start(); // 自动启动:TCP 模式下总线生命周期由挂载驱动 return b; }); return bus; } /// 节点在总线上的身份:以编号寻址(与真实 CAN 节点地址语义一致) public static string NodeIdOf(int nodeIndex) => $"{nodeIndex}"; } }