wenxiangchao
4 天以前 559890c482f5973f06aafdfec9550fee5ba3c7f2
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
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(NODE_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(); // 自动启动:TCP 模式下总线生命周期由挂载驱动
                return b;
            });
            return bus;
        }
 
        /// <summary>节点在总线上的身份:以编号寻址(与真实 CAN 节点地址语义一致)</summary>
        public static string NodeIdOf(int nodeIndex) => $"{nodeIndex}";
    }
}