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}";
}
}