using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Aging.Infrastructure.Contracts;
using CanSimulator;
using log4net;
namespace Aging.Infrastructure.Drivers
{
///
/// 全系统唯一的 CAN 设备驱动。实现 IDeviceDriver,命令语义(SEND/QUERY)与原适配器一致。
/// Local 与 Broker 的差异下沉到构造时选择的 后端:
/// Local → (TCP 直连 CanSimulatorServer)
/// Broker → (命名管道委托 DeviceBroker 宿主)
/// 接收帧由后端 FramePushed 事件驱动,进入本地缓冲并触发 ,
/// 使调试面板与自动化程序对两种执行模式完全无感知。
///
public sealed class CanDeviceDriver : IDeviceDriver
{
private static readonly ILog Log = LogManager.GetLogger(typeof(CanDeviceDriver));
// 信息型查询:响应去掉传输层的 OK: 前缀,还原为遗留 "VERB:payload" 形态(面板按此解析)
private static readonly Regex InfoVerb =
new(@"^OK:([A-Z][A-Z_]*):", RegexOptions.Compiled);
private readonly ICanTransport _transport;
private readonly string _busName;
private readonly int _nodeIndex;
private readonly Queue _recvBuffer = new();
private readonly object _bufferLock = new();
/// 宿主侧待捎带推送行(EV:RECV 原文),供 BrokerServer 随响应追加给客户端。
private readonly Queue _pendingPushLines = new();
private const int MaxPendingPushLines = 1000;
private readonly Stopwatch _latency = new();
private bool _disposed;
private string _lastError = string.Empty;
private DateTime? _lastErrorTime;
private long _txCount;
private int _reconnectCount;
public string DeviceId { get; }
public bool IsConnected => !_disposed && _transport.IsConnected;
public event EventHandler? ConnectionChanged;
/// 接收帧即时事件(Local/Broker 统一由后端推送驱动)
public event EventHandler? DriverFrameReceived;
/// Local 设备:从端点解析 bus:node,TCP 直连模拟器(host/port 取配置或覆盖值)。
public CanDeviceDriver(string deviceId, string endpoint,
string hostOverride = null, int? portOverride = null)
{
DeviceId = deviceId ?? throw new ArgumentNullException(nameof(deviceId));
(_busName, _nodeIndex) = ParseEndpoint(endpoint);
_transport = new NodeChannelTransport(hostOverride, portOverride, deviceId);
WireTransport();
}
/// Broker 设备:注入代理传输后端(由 DriverFactory 依 ExecutionMode 构造)。
internal CanDeviceDriver(string deviceId, string endpoint, ICanTransport transport)
{
DeviceId = deviceId ?? throw new ArgumentNullException(nameof(deviceId));
(_busName, _nodeIndex) = ParseEndpoint(endpoint);
_transport = transport ?? throw new ArgumentNullException(nameof(transport));
WireTransport();
}
/// Broker 设备代理工厂:客户端/Bootstrapper 据此构造委托宿主的统一驱动。
public static CanDeviceDriver CreateBrokerProxy(string deviceId, string endpoint, string pipeName, string configHash)
=> new CanDeviceDriver(deviceId, endpoint, new BrokerRpcTransport(deviceId, pipeName, configHash));
private void WireTransport()
{
_transport.FramePushed += OnFramePushed;
_transport.TransportBroken += () => OnConnection(false, "通道断开");
}
private static (string, int) ParseEndpoint(string endpoint)
{
if (string.IsNullOrWhiteSpace(endpoint))
throw new ArgumentException("CAN 端点不能为空", nameof(endpoint));
var parts = endpoint.Split(':');
if (parts.Length < 2 || !int.TryParse(parts[1], out int node) || node < 0)
throw new ArgumentException($"CAN 端点格式应为 总线名称:节点编号,实际: {endpoint}");
return (parts[0], node);
}
public async Task ConnectAsync(CancellationToken ct)
{
ThrowIfDisposed();
_latency.Restart();
try
{
await _transport.ConnectAsync(_busName, _nodeIndex, ct).ConfigureAwait(false);
_lastError = string.Empty;
OnConnection(true, "连接成功");
}
catch (Exception ex)
{
_lastError = ex.Message;
_lastErrorTime = DateTime.Now;
Log.Error($"[CAN] 设备 {DeviceId} 连接失败: {ex.Message}", ex);
throw;
}
finally
{
if (_latency.IsRunning) _latency.Stop();
}
}
public async Task DisconnectAsync()
{
ThrowIfDisposed();
try { await _transport.DisconnectAsync().ConfigureAwait(false); }
finally { OnConnection(false, "主动断开"); }
}
public async Task SendCommandAsync(string command, CancellationToken ct)
{
ThrowIfDisposed();
await EnsureConnectedAsync(ct).ConfigureAwait(false);
_latency.Restart();
try
{
// 命令串原样透传(SEND:frameId:data);响应去 OK: 传输前缀交由 QUERY 语义,
// 发送响应保留遗留 "OK:TX:…" 形态(上层与测试按 OK:TX 断言)
string resp = await _transport.TransceiveAsync(command, ct).ConfigureAwait(false);
Interlocked.Increment(ref _txCount);
return resp;
}
catch (Exception ex)
{
RecordError(ex);
throw;
}
finally
{
if (_latency.IsRunning) _latency.Stop();
}
}
public async Task QueryAsync(string query, CancellationToken ct)
{
ThrowIfDisposed();
await EnsureConnectedAsync(ct).ConfigureAwait(false);
_latency.Restart();
try
{
string op = (query ?? "").Split(':')[0].ToUpperInvariant();
// 接收类查询由本地缓冲应答(推送已实时入队),无需往返模拟器
if (op == "RECV") return HandleRecv();
if (op == "RECV_ALL") return HandleRecvAll();
string resp = await _transport.TransceiveAsync("QUERY:" + query, ct).ConfigureAwait(false);
return NormalizeResponse(resp);
}
catch (Exception ex)
{
RecordError(ex);
throw;
}
finally
{
if (_latency.IsRunning) _latency.Stop();
}
}
public DeviceDriverStatus GetStatus() => new DeviceDriverStatus
{
IsConnected = IsConnected,
IsHealthy = IsConnected && string.IsNullOrEmpty(_lastError),
LastError = _lastError,
LastErrorTime = _lastErrorTime,
LatencyMs = _latency.IsRunning ? _latency.Elapsed.TotalMilliseconds : 0,
ReconnectCount = _reconnectCount
};
// ==================== 推送与缓冲 ====================
/// 后端 EV:RECV 行 → 解析为帧 → 入本地缓冲 + 触发即时事件。
private void OnFramePushed(string line)
{
if (_disposed) return;
try
{
var frame = ParsePush(line);
if (frame == null) return;
lock (_bufferLock)
{
while (_recvBuffer.Count >= 1000) _recvBuffer.Dequeue();
_recvBuffer.Enqueue(frame);
// 累积原始推送行供宿主捎带给远端客户端(满则丢最老)
while (_pendingPushLines.Count >= MaxPendingPushLines) _pendingPushLines.Dequeue();
_pendingPushLines.Enqueue(line);
}
DriverFrameReceived?.Invoke(this, frame);
}
catch (Exception ex)
{
Log.Warn($"[CAN] {DeviceId} 推送帧解析失败: {ex.Message} [{line}]");
}
}
private static CanFrame ParsePush(string line)
{
// EV:RECV:{frameIdHex}:{dataHex}:{ts}:{srcNodeIndex}
if (!line.StartsWith("EV:RECV:", StringComparison.Ordinal)) return null;
var body = line.Substring("EV:RECV:".Length);
var parts = body.Split(':');
if (parts.Length < 2) return null;
uint id = CanFrame.ParseFrameId(parts[0]);
byte[] data = parts.Length >= 2 ? CanFrame.ParseData(parts[1]) : Array.Empty();
return new CanFrame
{
FrameId = id,
Format = id > 0x7FF ? CanFrameFormat.Extended : CanFrameFormat.Standard,
Type = CanFrameType.Data,
Data = data,
SourceNodeId = parts.Length >= 4 ? parts[3] : "",
Timestamp = DateTime.Now
};
}
private string HandleRecv()
{
lock (_bufferLock)
{
if (_recvBuffer.Count == 0) return "RECV:EMPTY";
var f = _recvBuffer.Dequeue();
return $"RECV:{f.FrameIdHex}:{f.DataHex}:{f.Timestamp:HH:mm:ss.fff}";
}
}
private string HandleRecvAll()
{
List batch = new();
lock (_bufferLock)
{
while (_recvBuffer.Count > 0) batch.Add(_recvBuffer.Dequeue());
}
if (batch.Count == 0) return "RECV_ALL:0";
var items = batch.Select(f => $"{f.FrameIdHex}:{f.DataHex}");
return $"RECV_ALL:{batch.Count}:{string.Join("|", items)}";
}
///
/// 信息型查询(VERB:…)去掉传输层 OK: 前缀还原遗留格式;
/// 动作确认(中文提示等)保留 OK:。
///
private static string NormalizeResponse(string resp)
{
if (string.IsNullOrEmpty(resp)) return resp;
var m = InfoVerb.Match(resp);
if (m.Success)
return resp.Substring(3); // 去 "OK:"
return resp;
}
// ==================== 杂项 ====================
///
/// 宿主专用:取出并清空自上次以来累积的推送行,供 BrokerServer 拼接到响应 readback 尾部。
///
public List DrainPendingPushLines()
{
lock (_bufferLock)
{
var list = _pendingPushLines.ToList();
_pendingPushLines.Clear();
return list;
}
}
private void EnsureConnected()
{
if (!IsConnected) throw new InvalidOperationException($"[CAN] 设备 {DeviceId} 未连接");
}
/// 按需连接:首次 I/O 时若未连接则自动建立通道(契合“按需连接”设计)。
private async Task EnsureConnectedAsync(CancellationToken ct)
{
if (IsConnected) return;
await ConnectAsync(ct).ConfigureAwait(false);
}
private void RecordError(Exception ex)
{
_lastError = ex.Message;
_lastErrorTime = DateTime.Now;
Log.Error($"[CAN] 设备 {DeviceId} 操作失败: {ex.Message}", ex);
}
private void OnConnection(bool connected, string reason)
{
if (!connected) _reconnectCount++;
ConnectionChanged?.Invoke(this, new DeviceConnectionEventArgs
{
DeviceId = DeviceId,
IsConnected = connected,
Reason = reason
});
}
public void Dispose()
{
if (_disposed) return;
_disposed = true;
try
{
_transport.FramePushed -= OnFramePushed;
_transport.Dispose();
}
catch (Exception ex)
{
Log.Error($"[CAN] 设备 {DeviceId} 释放异常: {ex.Message}", ex);
}
}
private void ThrowIfDisposed()
{
if (_disposed) throw new ObjectDisposedException(nameof(CanDeviceDriver));
}
}
}