wenxiangchao
4 天以前 559890c482f5973f06aafdfec9550fee5ba3c7f2
CanSimulator/CanBusSimulator.cs
@@ -66,6 +66,12 @@
        public int SimulatedLatencyMs { get; set; } = 0;
        /// <summary>
        /// 总线级应答器:开启后,任何节点发出的帧会被原样回显给发送者自身
        /// (模拟单个从设备应答,无需第二个节点在线)。来源标记为 RESPONDER。
        /// </summary>
        public bool EchoResponderToSender { get; set; } = false;
        /// <summary>
        /// 已注册的节点
        /// </summary>
        private readonly ConcurrentDictionary<string, CanNode> _nodes = new();
@@ -233,7 +239,7 @@
            TotalFramesTransmitted++;
            LastActivityTime = DateTime.Now;
            // 路由到所有其他节点
            // 路由到所有其他节点;若接收节点启用自动响应,由其回复
            int routed = 0;
            foreach (var kvp in _nodes)
            {
@@ -244,6 +250,10 @@
                    kvp.Value.ReceiveFrame(frame);
                    routed++;
                    TotalFramesReceived++;
                    // 接收方自动响应:模拟从设备收到命令后答复(面板“自动响应”语义)
                    if (kvp.Value.AutoResponseEnabled)
                        SendAutoResponse(kvp.Value, frame);
                }
                catch (Exception ex)
                {
@@ -251,32 +261,60 @@
                }
            }
            // 检查源节点是否启用自动响应模式
            if (_nodes.TryGetValue(frame.SourceNodeId, out var sourceNode) && sourceNode.AutoResponseEnabled)
            // 总线级应答器:把该帧原样回显给发送者自身(绕过"不回源",直接投递)
            if (EchoResponderToSender && _nodes.TryGetValue(frame.SourceNodeId, out var senderNode))
            {
                try
                {
                    // 模拟响应延迟
                    if (sourceNode.AutoResponseDelayMs > 0)
                    {
                        Thread.Sleep(sourceNode.AutoResponseDelayMs);
                    }
                    // 创建响应帧(复制原始帧数据,来源为源节点)
                    var responseFrame = new CanFrame
                    var echo = new CanFrame
                    {
                        FrameId = frame.FrameId,
                        Format = frame.Format,
                        Type = frame.Type,
                        Data = (byte[])frame.Data.Clone(),
                        SourceNodeId = sourceNode.NodeId,
                        SourceNodeId = "RESPONDER",
                        Timestamp = DateTime.Now
                    };
                    senderNode.ReceiveFrame(echo);
                    TotalFramesReceived++;
                }
                catch (Exception ex)
                {
                    Log.Error($"[CanBus] 应答器回显给 {frame.SourceNodeId} 失败: {ex.Message}");
                }
            }
            Log.Debug($"[CanBus] 帧 {frame.FrameIdHex} 从 {frame.SourceNodeId} 路由到 {routed} 个节点");
            FrameTransmitted?.Invoke(this, new CanBusFrameEventArgs(frame, frame.SourceNodeId));
        }
        /// <summary>
        /// 接收节点自动响应:把收到帧原样回给其它节点(含发帧方),并记入自身邮箱与总线日志
        /// </summary>
        private void SendAutoResponse(CanNode responder, CanFrame incoming)
        {
            try
            {
                // 模拟响应延迟
                if (responder.AutoResponseDelayMs > 0)
                {
                    Thread.Sleep(responder.AutoResponseDelayMs);
                }
                var responseFrame = new CanFrame
                {
                    FrameId = incoming.FrameId,
                    Format = incoming.Format,
                    Type = incoming.Type,
                    Data = (byte[])incoming.Data.Clone(),
                    SourceNodeId = responder.NodeId,
                        Timestamp = DateTime.Now
                    };
                    // 将响应帧路由到所有其他节点(不包括响应源自身)
                // 响应帧路由到其它节点(含最初的发帧方)
                    foreach (var kvp in _nodes)
                    {
                        if (kvp.Key == sourceNode.NodeId) continue;
                    if (kvp.Key == responder.NodeId) continue;
                        try
                        {
                            kvp.Value.ReceiveFrame(responseFrame);
@@ -288,24 +326,20 @@
                        }
                    }
                    // 将响应帧送入源节点的接收邮箱(让源节点能看到自己的响应)
                    sourceNode.ReceiveFrame(responseFrame);
                // 响应帧送入应答节点自身接收邮箱(调试页可见自己发出的响应)
                responder.ReceiveFrame(responseFrame);
                    TotalFramesReceived++;
                    // 记录响应帧到总线日志
                    _busLog.Enqueue(responseFrame);
                    TotalFramesTransmitted++;
                    Log.Debug($"[CanBus] 节点 {sourceNode.NodeId} 自动响应帧 {responseFrame.FrameIdHex}");
                Log.Debug($"[CanBus] 接收节点 {responder.NodeId} 自动响应帧 {responseFrame.FrameIdHex}");
                }
                catch (Exception ex)
                {
                    Log.Error($"[CanBus] 源节点自动响应失败: {ex.Message}");
                Log.Error($"[CanBus] 节点 {responder.NodeId} 自动响应失败: {ex.Message}");
                }
            }
            Log.Debug($"[CanBus] 帧 {frame.FrameIdHex} 从 {frame.SourceNodeId} 路由到 {routed} 个节点");
            FrameTransmitted?.Invoke(this, new CanBusFrameEventArgs(frame, frame.SourceNodeId));
        }
        /// <summary>