using System;
|
using System.Collections.Generic;
|
using System.Diagnostics;
|
using System.Linq;
|
using System.Threading;
|
using System.Threading.Tasks;
|
using Aging.Infrastructure.Contracts;
|
using CanSimulator;
|
using log4net;
|
|
namespace Aging.Infrastructure.Drivers
|
{
|
/// <summary>
|
/// CAN 通讯驱动适配器。
|
/// 封装 CanSimulator,实现统一的 IDeviceDriver 接口。
|
/// 命令格式约定:
|
/// 发送: "SEND:frameId:data" 如 "SEND:0x100:01 02 03 04"
|
/// 查询:
|
/// "RECV" - 获取接收邮箱中的下一帧
|
/// "RECV_ALL" - 获取接收邮箱中的所有帧
|
/// "STATUS" - 获取节点/总线状态
|
/// "FILTER:id:mask" - 设置接收滤波器
|
/// "FILTER_CLEAR" - 清除所有滤波器
|
/// "PROMISCUOUS" - 设置混杂模式(接收所有帧)
|
/// "LOG" - 获取总线日志
|
/// "BUS_STATUS" - 获取总线状态摘要
|
/// </summary>
|
public sealed class CanDriverAdapter : IDeviceDriver
|
{
|
private static readonly ILog Log = LogManager.GetLogger(typeof(CanDriverAdapter));
|
|
/// <summary>
|
/// 全局 CAN 总线模拟器实例(所有 CAN 驱动共享)
|
/// </summary>
|
private static readonly Dictionary<string, CanBusSimulator> _sharedBuses = new();
|
private static readonly object _busLock = new();
|
|
private readonly string _ip; // 总线名称(用 ip 字段映射)
|
private readonly int _port; // 节点编号(用 port 字段映射)
|
private CanBusSimulator? _bus;
|
private CanNode? _node;
|
private bool _disposed;
|
private readonly Stopwatch _latencyStopwatch = new();
|
private double _lastLatencyMs;
|
private string _lastError = string.Empty;
|
private DateTime? _lastErrorTime;
|
private int _reconnectCount;
|
|
/// <summary>
|
/// 接收到的帧缓存(用于 RECV 查询)
|
/// </summary>
|
private readonly Queue<CanFrame> _recvBuffer = new();
|
|
/// <inheritdoc/>
|
public string DeviceId { get; }
|
|
/// <inheritdoc/>
|
public bool IsConnected => _node != null && _node.IsActive;
|
|
/// <inheritdoc/>
|
public event EventHandler<DeviceConnectionEventArgs>? ConnectionChanged;
|
|
/// <summary>
|
/// 初始化 CAN 驱动适配器
|
/// </summary>
|
/// <param name="deviceId">设备唯一标识符</param>
|
/// <param name="busName">总线名称(如 CAN_BUS_1)</param>
|
/// <param name="nodeIndex">节点编号(用于区分同一总线上的不同设备)</param>
|
public CanDriverAdapter(string deviceId, string busName, int nodeIndex)
|
{
|
DeviceId = deviceId ?? throw new ArgumentNullException(nameof(deviceId));
|
_ip = busName ?? "CAN_BUS_1";
|
_port = nodeIndex;
|
}
|
|
/// <summary>
|
/// 获取或创建共享总线
|
/// </summary>
|
private CanBusSimulator GetOrCreateBus(string busName)
|
{
|
lock (_busLock)
|
{
|
if (!_sharedBuses.TryGetValue(busName, out var bus))
|
{
|
bus = new CanBusSimulator(busName);
|
_sharedBuses[busName] = bus;
|
Log.Info($"[CAN] 创建新的虚拟总线: {busName}");
|
}
|
return bus;
|
}
|
}
|
|
/// <inheritdoc/>
|
public Task ConnectAsync(CancellationToken ct)
|
{
|
ThrowIfDisposed();
|
Log.Info($"[CAN] 设备 {DeviceId} 正在连接到总线 {_ip} (节点: {_port})...");
|
|
try
|
{
|
// 获取或创建共享总线
|
_bus = GetOrCreateBus(_ip);
|
|
// 如果总线未启动,自动启动
|
if (_bus.State != CanBusState.Running)
|
{
|
_bus.Start();
|
}
|
|
// 注册节点
|
string nodeId = $"{DeviceId}_node{_port}";
|
_node = _bus.RegisterNode(nodeId, DeviceId);
|
|
// 设置默认滤波器(接收所有帧)
|
_node.SetPromiscuousMode();
|
|
// 激活节点
|
_node.Activate();
|
|
// 订阅接收事件
|
_node.FrameReceived += OnFrameReceived;
|
|
_lastError = string.Empty;
|
Log.Info($"[CAN] 设备 {DeviceId} 已成功连接到总线 {_ip} (节点: {_node.NodeId})");
|
OnConnectionChanged(true, "连接成功");
|
}
|
catch (Exception ex)
|
{
|
_lastError = ex.Message;
|
_lastErrorTime = DateTime.Now;
|
Log.Error($"[CAN] 设备 {DeviceId} 连接异常: {ex.Message}", ex);
|
_node?.Deactivate();
|
_node = null;
|
throw;
|
}
|
|
return Task.CompletedTask;
|
}
|
|
/// <inheritdoc/>
|
public Task DisconnectAsync()
|
{
|
ThrowIfDisposed();
|
Log.Info($"[CAN] 设备 {DeviceId} 正在断开连接...");
|
|
try
|
{
|
if (_node != null)
|
{
|
_node.FrameReceived -= OnFrameReceived;
|
_node.Deactivate();
|
_bus?.UnregisterNode(_node.NodeId);
|
_node = null;
|
}
|
|
Log.Info($"[CAN] 设备 {DeviceId} 已断开连接");
|
OnConnectionChanged(false, "主动断开");
|
}
|
catch (Exception ex)
|
{
|
_lastError = ex.Message;
|
_lastErrorTime = DateTime.Now;
|
Log.Error($"[CAN] 设备 {DeviceId} 断开连接异常: {ex.Message}", ex);
|
_node = null;
|
}
|
|
return Task.CompletedTask;
|
}
|
|
/// <inheritdoc/>
|
public Task<string> SendCommandAsync(string command, CancellationToken ct)
|
{
|
ThrowIfDisposed();
|
if (!IsConnected) throw new InvalidOperationException("[CAN] 设备未连接");
|
|
_latencyStopwatch.Restart();
|
try
|
{
|
Log.Debug($"[CAN] 设备 {DeviceId} 发送命令: {command}");
|
|
// 解析命令格式: "SEND:frameId:data"
|
string[] parts = command.Split(':');
|
if (parts.Length < 3)
|
{
|
throw new ArgumentException($"[CAN] 命令格式错误,应为 SEND:frameId:data,实际: {command}");
|
}
|
|
string operation = parts[0].ToUpper();
|
if (operation != "SEND")
|
{
|
throw new ArgumentException($"[CAN] 不支持的操作类型: {operation},应为 SEND");
|
}
|
|
// 解析帧 ID
|
string frameIdStr = parts[1];
|
uint frameId = CanFrame.ParseFrameId(frameIdStr);
|
|
// 解析数据
|
string dataStr = parts[2];
|
byte[] data = CanFrame.ParseData(dataStr);
|
|
// 验证数据长度
|
if (data.Length > 8)
|
{
|
throw new ArgumentException($"[CAN] 数据长度超限: {data.Length} 字节(最大 8 字节)");
|
}
|
|
// 创建并发送帧
|
var frame = new CanFrame
|
{
|
FrameId = frameId,
|
Format = frameId > 0x7FF ? CanFrameFormat.Extended : CanFrameFormat.Standard,
|
Type = CanFrameType.Data,
|
Data = data
|
};
|
|
_node!.SendFrame(frame);
|
|
_latencyStopwatch.Stop();
|
_lastLatencyMs = _latencyStopwatch.Elapsed.TotalMilliseconds;
|
|
string response = $"OK:TX:{frame.FrameIdHex}:{frame.Dlc}";
|
Log.Debug($"[CAN] 设备 {DeviceId} 发送成功 (延迟 {_lastLatencyMs:F2}ms): {response}");
|
return Task.FromResult(response);
|
}
|
catch (Exception ex)
|
{
|
_latencyStopwatch.Stop();
|
_lastLatencyMs = _latencyStopwatch.Elapsed.TotalMilliseconds;
|
_lastError = ex.Message;
|
_lastErrorTime = DateTime.Now;
|
Log.Error($"[CAN] 设备 {DeviceId} 发送命令失败: {ex.Message}", ex);
|
throw;
|
}
|
}
|
|
/// <inheritdoc/>
|
public Task<string> QueryAsync(string query, CancellationToken ct)
|
{
|
ThrowIfDisposed();
|
if (!IsConnected) throw new InvalidOperationException("[CAN] 设备未连接");
|
|
_latencyStopwatch.Restart();
|
try
|
{
|
Log.Debug($"[CAN] 设备 {DeviceId} 查询: {query}");
|
|
string[] parts = query.Split(':');
|
string operation = parts[0].ToUpper();
|
|
string response;
|
switch (operation)
|
{
|
case "RECV":
|
response = HandleRecv();
|
break;
|
|
case "RECV_ALL":
|
response = HandleRecvAll();
|
break;
|
|
case "STATUS":
|
response = HandleStatus();
|
break;
|
|
case "FILTER":
|
response = HandleFilter(parts);
|
break;
|
|
case "FILTER_CLEAR":
|
_node!.ClearFilters();
|
response = "OK:滤波器已清除";
|
break;
|
|
case "PROMISCUOUS":
|
_node!.SetPromiscuousMode();
|
response = "OK:混杂模式已启用";
|
break;
|
|
case "AUTO_RESPONSE":
|
response = HandleAutoResponse(parts);
|
break;
|
|
case "RESPONSE_DELAY":
|
response = HandleResponseDelay(parts);
|
break;
|
|
case "LOG":
|
response = HandleLog();
|
break;
|
|
case "BUS_STATUS":
|
response = _bus?.GetStatusSummary() ?? "总线未初始化";
|
break;
|
|
default:
|
throw new ArgumentException($"[CAN] 不支持的查询类型: {operation}");
|
}
|
|
_latencyStopwatch.Stop();
|
_lastLatencyMs = _latencyStopwatch.Elapsed.TotalMilliseconds;
|
Log.Debug($"[CAN] 设备 {DeviceId} 查询响应: {response} (延迟 {_lastLatencyMs:F2}ms)");
|
return Task.FromResult(response);
|
}
|
catch (Exception ex)
|
{
|
_latencyStopwatch.Stop();
|
_lastLatencyMs = _latencyStopwatch.Elapsed.TotalMilliseconds;
|
_lastError = ex.Message;
|
_lastErrorTime = DateTime.Now;
|
Log.Error($"[CAN] 设备 {DeviceId} 查询失败: {ex.Message}", ex);
|
throw;
|
}
|
}
|
|
/// <summary>
|
/// 处理 RECV 查询 - 获取下一帧
|
/// </summary>
|
private string HandleRecv()
|
{
|
// 先从缓冲区取
|
if (_recvBuffer.Count > 0)
|
{
|
var frame = _recvBuffer.Dequeue();
|
return $"RECV:{frame.FrameIdHex}:{frame.DataHex}:{frame.Timestamp:HH:mm:ss.fff}";
|
}
|
|
// 从节点邮箱取
|
if (_node!.TryDequeueReceived(out var frame2))
|
{
|
return $"RECV:{frame2!.FrameIdHex}:{frame2.DataHex}:{frame2.Timestamp:HH:mm:ss.fff}";
|
}
|
|
return "RECV:EMPTY";
|
}
|
|
/// <summary>
|
/// 处理 RECV_ALL 查询 - 获取所有帧
|
/// </summary>
|
private string HandleRecvAll()
|
{
|
var frames = new List<string>();
|
|
// 先取缓冲区
|
while (_recvBuffer.Count > 0)
|
{
|
var f = _recvBuffer.Dequeue();
|
frames.Add($"{f.FrameIdHex}:{f.DataHex}");
|
}
|
|
// 再取节点邮箱
|
var mailbox = _node!.PeekReceiveMailbox();
|
_node.ClearReceiveMailbox();
|
foreach (var f in mailbox)
|
{
|
frames.Add($"{f.FrameIdHex}:{f.DataHex}:{f.Timestamp:HH:mm:ss.fff}");
|
}
|
|
return frames.Count > 0 ? $"RECV_ALL:{frames.Count}:{string.Join("|", frames)}" : "RECV_ALL:0";
|
}
|
|
/// <summary>
|
/// 处理 STATUS 查询
|
/// </summary>
|
private string HandleStatus()
|
{
|
var nodeStatus = _node?.GetStatusSummary() ?? "节点未初始化";
|
var busStatus = _bus?.GetStatusSummary() ?? "总线未初始化";
|
return $"STATUS:{nodeStatus}|{busStatus}";
|
}
|
|
/// <summary>
|
/// 处理 FILTER 设置
|
/// </summary>
|
private string HandleFilter(string[] parts)
|
{
|
if (parts.Length < 3)
|
{
|
throw new ArgumentException("[CAN] FILTER 命令格式错误,应为 FILTER:id:mask");
|
}
|
|
uint filterId = CanFrame.ParseFrameId(parts[1]);
|
uint mask = CanFrame.ParseFrameId(parts[2]);
|
|
_node!.AddFilter(new AcceptanceFilter
|
{
|
FilterId = filterId,
|
Mask = mask,
|
IsExtended = filterId > 0x7FF
|
});
|
|
return $"OK:滤波器已添加 ID=0x{filterId:X3}, Mask=0x{mask:X3}";
|
}
|
|
/// <summary>
|
/// 处理 AUTO_RESPONSE 命令
|
/// 格式: AUTO_RESPONSE:on|off
|
/// </summary>
|
private string HandleAutoResponse(string[] parts)
|
{
|
if (parts.Length < 2)
|
{
|
// 查询当前状态
|
bool current = _node!.AutoResponseEnabled;
|
return $"AUTO_RESPONSE:{(current ? "ON" : "OFF")}";
|
}
|
|
string mode = parts[1].Trim().ToUpper();
|
switch (mode)
|
{
|
case "ON":
|
case "1":
|
case "TRUE":
|
case "ENABLE":
|
_node!.AutoResponseEnabled = true;
|
Log.Info($"[CAN] 设备 {DeviceId} 启用自动响应模式");
|
return "OK:自动响应模式已启用";
|
|
case "OFF":
|
case "0":
|
case "FALSE":
|
case "DISABLE":
|
_node!.AutoResponseEnabled = false;
|
Log.Info($"[CAN] 设备 {DeviceId} 禁用自动响应模式");
|
return "OK:自动响应模式已禁用";
|
|
default:
|
throw new ArgumentException($"[CAN] AUTO_RESPONSE 参数错误: {mode},应为 on/off");
|
}
|
}
|
|
/// <summary>
|
/// 处理 RESPONSE_DELAY 命令
|
/// 格式: RESPONSE_DELAY:毫秒数
|
/// </summary>
|
private string HandleResponseDelay(string[] parts)
|
{
|
if (parts.Length < 2)
|
{
|
// 查询当前延迟
|
return $"RESPONSE_DELAY:{_node!.AutoResponseDelayMs}ms";
|
}
|
|
if (!int.TryParse(parts[1].Trim(), out int delayMs) || delayMs < 0)
|
{
|
throw new ArgumentException($"[CAN] RESPONSE_DELAY 参数错误: {parts[1]},应为非负整数(毫秒)");
|
}
|
|
_node!.AutoResponseDelayMs = delayMs;
|
Log.Info($"[CAN] 设备 {DeviceId} 设置响应延迟: {delayMs}ms");
|
return $"OK:响应延迟已设置为 {delayMs}ms";
|
}
|
|
/// <summary>
|
/// 处理 LOG 查询
|
/// </summary>
|
private string HandleLog()
|
{
|
var log = _bus?.GetBusLog(50) ?? new List<CanFrame>();
|
if (log.Count == 0) return "LOG:EMPTY";
|
|
var entries = log.Select(f =>
|
$"[{f.Timestamp:HH:mm:ss.fff}]{f.SourceNodeId}>{f.FrameIdHex}:{f.DataHex}");
|
return $"LOG:{log.Count}:{string.Join("|", entries)}";
|
}
|
|
/// <summary>
|
/// 帧接收事件(公开给外部订阅)
|
/// </summary>
|
public event EventHandler<CanFrame>? DriverFrameReceived;
|
|
/// <summary>
|
/// 帧接收事件处理
|
/// </summary>
|
private void OnFrameReceived(object? sender, CanFrame frame)
|
{
|
// 放入接收缓冲区(用于 RECV/RECV_ALL 查询)
|
while (_recvBuffer.Count >= 1000)
|
{
|
_recvBuffer.Dequeue();
|
}
|
_recvBuffer.Enqueue(frame);
|
|
// 触发公开事件(用于即时显示)
|
DriverFrameReceived?.Invoke(this, frame);
|
}
|
|
/// <inheritdoc/>
|
public DeviceDriverStatus GetStatus()
|
{
|
return new DeviceDriverStatus
|
{
|
IsConnected = IsConnected,
|
IsHealthy = IsConnected && string.IsNullOrEmpty(_lastError),
|
LastError = _lastError,
|
LastErrorTime = _lastErrorTime,
|
LatencyMs = _lastLatencyMs,
|
ReconnectCount = _reconnectCount
|
};
|
}
|
|
/// <inheritdoc/>
|
public void Dispose()
|
{
|
if (_disposed) return;
|
_disposed = true;
|
|
try
|
{
|
if (_node != null)
|
{
|
_node.FrameReceived -= OnFrameReceived;
|
_node.Deactivate();
|
_bus?.UnregisterNode(_node.NodeId);
|
}
|
}
|
catch (Exception ex)
|
{
|
Log.Error($"[CAN] 设备 {DeviceId} 释放资源异常: {ex.Message}", ex);
|
}
|
}
|
|
/// <summary>
|
/// 触发所有总线和节点(用于测试清理)
|
/// </summary>
|
public static void ResetAllBuses()
|
{
|
lock (_busLock)
|
{
|
foreach (var bus in _sharedBuses.Values)
|
{
|
bus.Dispose();
|
}
|
_sharedBuses.Clear();
|
Log.Info("[CAN] 所有虚拟总线已重置");
|
}
|
}
|
|
/// <summary>
|
/// 触发所有总线实例(供外部查询)
|
/// </summary>
|
public static IReadOnlyDictionary<string, CanBusSimulator> GetAllBuses()
|
{
|
lock (_busLock)
|
{
|
return new Dictionary<string, CanBusSimulator>(_sharedBuses);
|
}
|
}
|
|
/// <summary>
|
/// 触发连接状态变化事件
|
/// </summary>
|
private void OnConnectionChanged(bool isConnected, string reason)
|
{
|
ConnectionChanged?.Invoke(this, new DeviceConnectionEventArgs
|
{
|
DeviceId = DeviceId,
|
IsConnected = isConnected,
|
Reason = reason
|
});
|
|
if (!isConnected)
|
{
|
_reconnectCount++;
|
}
|
}
|
|
/// <summary>
|
/// 检查是否已释放
|
/// </summary>
|
private void ThrowIfDisposed()
|
{
|
if (_disposed) throw new ObjectDisposedException(nameof(CanDriverAdapter));
|
}
|
}
|
}
|