using System;
|
using System.Collections.Concurrent;
|
using System.Collections.Generic;
|
using System.Linq;
|
using log4net;
|
|
namespace CanSimulator
|
{
|
/// <summary>
|
/// 接收滤波器配置
|
/// </summary>
|
public sealed class AcceptanceFilter
|
{
|
/// <summary>
|
/// 滤波器 ID(匹配值)
|
/// </summary>
|
public uint FilterId { get; set; }
|
|
/// <summary>
|
/// 掩码(为 1 的位参与比较)
|
/// </summary>
|
public uint Mask { get; set; } = 0x7FF; // 默认标准帧全匹配
|
|
/// <summary>
|
/// 是否为扩展帧滤波器
|
/// </summary>
|
public bool IsExtended { get; set; }
|
|
/// <summary>
|
/// 检查给定帧 ID 是否通过此滤波器
|
/// </summary>
|
public bool Accepts(uint frameId, bool isExtended)
|
{
|
if (IsExtended != isExtended) return false;
|
return (frameId & Mask) == (FilterId & Mask);
|
}
|
}
|
|
/// <summary>
|
/// 虚拟 CAN 节点
|
/// 模拟一个 CAN 通讯卡的端点,具有发送和接收功能
|
/// </summary>
|
public sealed class CanNode
|
{
|
private static readonly ILog Log = LogManager.GetLogger(typeof(CanNode));
|
|
/// <summary>
|
/// 节点唯一标识
|
/// </summary>
|
public string NodeId { get; }
|
|
/// <summary>
|
/// 节点名称(可选描述)
|
/// </summary>
|
public string Name { get; set; }
|
|
/// <summary>
|
/// 节点是否已激活(连接到总线)
|
/// </summary>
|
public bool IsActive { get; private set; }
|
|
/// <summary>
|
/// 接收滤波器列表
|
/// </summary>
|
private readonly List<AcceptanceFilter> _filters = new();
|
|
/// <summary>
|
/// 接收邮箱(FIFO 队列)
|
/// </summary>
|
private readonly ConcurrentQueue<CanFrame> _receiveMailbox = new();
|
|
/// <summary>
|
/// 发送历史记录
|
/// </summary>
|
private readonly ConcurrentQueue<CanFrame> _sendHistory = new();
|
|
/// <summary>
|
/// 最大邮箱容量
|
/// </summary>
|
public int MaxMailboxSize { get; set; } = 1000;
|
|
/// <summary>
|
/// 最大历史记录容量
|
/// </summary>
|
public int MaxHistorySize { get; set; } = 500;
|
|
/// <summary>
|
/// 接收帧计数器
|
/// </summary>
|
public long RxCount { get; private set; }
|
|
/// <summary>
|
/// 发送帧计数器
|
/// </summary>
|
public long TxCount { get; private set; }
|
|
/// <summary>
|
/// 错误计数器
|
/// </summary>
|
public long ErrorCount { get; private set; }
|
|
/// <summary>
|
/// 是否启用自动响应模式(收到帧后原封不动回复)
|
/// </summary>
|
public bool AutoResponseEnabled { get; set; }
|
|
/// <summary>
|
/// 自动响应延迟(毫秒)- 模拟真实设备响应时间
|
/// </summary>
|
public int AutoResponseDelayMs { get; set; } = 0;
|
|
/// <summary>
|
/// 自动响应事件(用于日志/监控)
|
/// </summary>
|
public event EventHandler<CanFrame>? AutoResponseSent;
|
|
/// <summary>
|
/// 帧接收事件
|
/// </summary>
|
public event EventHandler<CanFrame>? FrameReceived;
|
|
/// <summary>
|
/// 帧发送事件
|
/// </summary>
|
public event EventHandler<CanFrame>? FrameSent;
|
|
public CanNode(string nodeId, string? name = null)
|
{
|
NodeId = nodeId ?? throw new ArgumentNullException(nameof(nodeId));
|
Name = name ?? nodeId;
|
}
|
|
/// <summary>
|
/// 激活节点(连接到总线)
|
/// </summary>
|
public void Activate()
|
{
|
IsActive = true;
|
Log.Info($"[CanNode] 节点 {NodeId} ({Name}) 已激活");
|
}
|
|
/// <summary>
|
/// 停用节点(从总线断开)
|
/// </summary>
|
public void Deactivate()
|
{
|
IsActive = false;
|
Log.Info($"[CanNode] 节点 {NodeId} ({Name}) 已停用");
|
}
|
|
/// <summary>
|
/// 添加接收滤波器
|
/// </summary>
|
public void AddFilter(AcceptanceFilter filter)
|
{
|
lock (_filters)
|
{
|
_filters.Add(filter);
|
}
|
Log.Debug($"[CanNode] 节点 {NodeId} 添加滤波器: ID=0x{filter.FilterId:X3}, Mask=0x{filter.Mask:X3}");
|
}
|
|
/// <summary>
|
/// 清除所有滤波器
|
/// </summary>
|
public void ClearFilters()
|
{
|
lock (_filters)
|
{
|
_filters.Clear();
|
}
|
Log.Debug($"[CanNode] 节点 {NodeId} 清除所有滤波器");
|
}
|
|
/// <summary>
|
/// 设置通配符滤波器(接收所有帧)
|
/// </summary>
|
public void SetPromiscuousMode()
|
{
|
lock (_filters)
|
{
|
_filters.Clear();
|
_filters.Add(new AcceptanceFilter { FilterId = 0, Mask = 0, IsExtended = false });
|
_filters.Add(new AcceptanceFilter { FilterId = 0, Mask = 0, IsExtended = true });
|
}
|
Log.Debug($"[CanNode] 节点 {NodeId} 设置为混杂模式(接收所有帧)");
|
}
|
|
/// <summary>
|
/// 检查帧是否通过滤波器
|
/// </summary>
|
internal bool AcceptsFrame(CanFrame frame)
|
{
|
lock (_filters)
|
{
|
// 如果没有设置滤波器,默认拒绝
|
if (_filters.Count == 0) return false;
|
|
// 任一滤波器匹配即接受
|
return _filters.Any(f => f.Accepts(frame.FrameId, frame.Format == CanFrameFormat.Extended));
|
}
|
}
|
|
/// <summary>
|
/// 接收帧(由总线调用)
|
/// </summary>
|
internal void ReceiveFrame(CanFrame frame)
|
{
|
if (!IsActive) return;
|
if (!AcceptsFrame(frame)) return;
|
|
// 入队接收邮箱
|
while (_receiveMailbox.Count >= MaxMailboxSize)
|
{
|
_receiveMailbox.TryDequeue(out _); // 丢弃最旧的帧
|
}
|
_receiveMailbox.Enqueue(frame);
|
RxCount++;
|
|
// 触发事件
|
FrameReceived?.Invoke(this, frame);
|
Log.Debug($"[CanNode] 节点 {NodeId} 接收帧: {frame}");
|
}
|
|
/// <summary>
|
/// 发送帧
|
/// </summary>
|
public void SendFrame(CanFrame frame)
|
{
|
if (!IsActive)
|
{
|
ErrorCount++;
|
throw new InvalidOperationException($"节点 {NodeId} 未激活,无法发送帧");
|
}
|
|
frame.SourceNodeId = NodeId;
|
frame.Timestamp = DateTime.Now;
|
|
// 记录发送历史
|
while (_sendHistory.Count >= MaxHistorySize)
|
{
|
_sendHistory.TryDequeue(out _);
|
}
|
_sendHistory.Enqueue(frame);
|
TxCount++;
|
|
// 触发事件(由总线处理路由)
|
FrameSent?.Invoke(this, frame);
|
Log.Debug($"[CanNode] 节点 {NodeId} 发送帧: {frame}");
|
}
|
|
/// <summary>
|
/// 从接收邮箱中取出一帧
|
/// </summary>
|
public bool TryDequeueReceived(out CanFrame? frame)
|
{
|
return _receiveMailbox.TryDequeue(out frame);
|
}
|
|
/// <summary>
|
/// 查看接收邮箱中的所有帧(不移除)
|
/// </summary>
|
public List<CanFrame> PeekReceiveMailbox()
|
{
|
return _receiveMailbox.ToList();
|
}
|
|
/// <summary>
|
/// 清空接收邮箱
|
/// </summary>
|
public void ClearReceiveMailbox()
|
{
|
while (_receiveMailbox.TryDequeue(out _)) { }
|
Log.Debug($"[CanNode] 节点 {NodeId} 清空接收邮箱");
|
}
|
|
/// <summary>
|
/// 获取发送历史
|
/// </summary>
|
public List<CanFrame> GetSendHistory()
|
{
|
return _sendHistory.ToList();
|
}
|
|
/// <summary>
|
/// 清空发送历史
|
/// </summary>
|
public void ClearSendHistory()
|
{
|
while (_sendHistory.TryDequeue(out _)) { }
|
}
|
|
/// <summary>
|
/// 重置节点统计
|
/// </summary>
|
public void ResetCounters()
|
{
|
RxCount = 0;
|
TxCount = 0;
|
ErrorCount = 0;
|
}
|
|
/// <summary>
|
/// 获取节点状态摘要
|
/// </summary>
|
public string GetStatusSummary()
|
{
|
return $"Node={NodeId}, Active={IsActive}, Rx={RxCount}, Tx={TxCount}, Err={ErrorCount}, Mailbox={_receiveMailbox.Count}";
|
}
|
}
|
}
|