using Aging.Infrastructure.Drivers;
using log4net;
using Plugin.DeviceDebug.Entity;
using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
namespace Plugin.DeviceDebug.Panels
{
///
/// CAN 帧显示模型
///
public class CanFrameDisplay
{
public DateTime Timestamp { get; set; }
public string FrameId { get; set; } = string.Empty;
public int Dlc { get; set; }
public string Data { get; set; } = string.Empty;
public string Direction { get; set; } = string.Empty; // TX / RX
public string Source { get; set; } = string.Empty;
}
///
/// CanDebugPanel.xaml 的交互逻辑
///
public partial class CanDebugPanel : UserControl
{
private static readonly ILog log = LogManager.GetLogger(typeof(CanDebugPanel));
///
/// 是否为维护模式(控制输入可用性)
///
public static readonly DependencyProperty IsMaintenanceModeProperty =
DependencyProperty.Register(nameof(IsMaintenanceMode), typeof(bool), typeof(CanDebugPanel),
new PropertyMetadata(false));
public bool IsMaintenanceMode
{
get => (bool)GetValue(IsMaintenanceModeProperty);
set => SetValue(IsMaintenanceModeProperty, value);
}
///
/// 命令执行事件
///
public event EventHandler? OnCommandExecute;
///
/// 接收帧列表
///
public ObservableCollection ReceivedFrames { get; } = new();
///
/// CAN 驱动适配器
///
private CanDriverAdapter? _driver;
///
/// 轮询定时器(用于检查接收邮箱)
///
private DispatcherTimer? _pollTimer;
///
/// 是否已连接
///
private bool _isConnected;
///
/// 设备ID(从 DeviceDebug 传入,面板自动拼接 deviceId)
///
public string DeviceId
{
get => _deviceId;
set
{
_deviceId = value;
if (!string.IsNullOrEmpty(value))
{
// 将设备ID前缀拼接到默认设备标识中,避免与模拟器面板重名
UpdateDeviceIdDisplay();
}
}
}
private string _deviceId = string.Empty;
///
/// 从设备 Endpoint 解析出的总线名称
///
public string InitialBusName { get; set; }
///
/// 从设备 Endpoint 解析出的节点编号
///
public int? InitialNodeIndex { get; set; }
///
/// 是否自动连接(从 DeviceDebug 打开时=true)
///
public bool AutoConnectOnLoad { get; set; }
public CanDebugPanel()
{
InitializeComponent();
receiveList.ItemsSource = ReceivedFrames;
InitializePollTimer();
Loaded += CanDebugPanel_Loaded;
}
private void CanDebugPanel_Loaded(object sender, RoutedEventArgs e)
{
Loaded -= CanDebugPanel_Loaded;
// 从设备信息初始化总线/节点输入
if (!string.IsNullOrEmpty(InitialBusName))
{
busNameInput.Text = InitialBusName;
}
if (InitialNodeIndex.HasValue)
{
nodeIndexInput.Text = InitialNodeIndex.Value.ToString();
}
// 自动连接
if (AutoConnectOnLoad)
{
Dispatcher.BeginInvoke(new Action(() =>
{
try { DoConnect(); }
catch (Exception ex) { log.Warn($"自动连接失败: {ex.Message}"); }
}), DispatcherPriority.Background);
}
}
private void UpdateDeviceIdDisplay()
{
if (!string.IsNullOrEmpty(_deviceId) && busNameInput != null)
{
// 保留默认总线名,仅记录 deviceId 以供日志使用
log.Info($"[CanDebugPanel] 关联设备: {_deviceId}");
}
}
///
/// 初始化轮询定时器
///
private void InitializePollTimer()
{
_pollTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(100) // 100ms 轮询一次
};
_pollTimer.Tick += PollTimer_Tick;
}
///
/// 轮询定时器回调 - 仅更新统计信息(帧显示由事件驱动)
///
private void PollTimer_Tick(object? sender, EventArgs e)
{
if (!_isConnected || _driver == null) return;
try
{
// 更新统计
UpdateStats();
}
catch (Exception ex)
{
log.Error($"轮询统计更新失败: {ex.Message}");
}
}
///
/// 连接按钮点击
///
private void ConnectBtn_Click(object sender, RoutedEventArgs e)
{
try
{
DoConnect();
}
catch (Exception ex)
{
log.Error($"CAN 连接失败: {ex.Message}", ex);
MessageBox.Show($"连接失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
UpdateConnectionStatus(false);
}
}
///
/// 实际连接逻辑(从 UI 或自动连接调用)
///
private void DoConnect()
{
if (_isConnected) return;
string busName = busNameInput.Text.Trim();
if (!int.TryParse(nodeIndexInput.Text.Trim(), out int nodeIndex))
{
throw new ArgumentException("节点编号格式错误");
}
// 优先使用 DeviceDebug 传入的设备 ID,否则自动生成一个
string deviceId = string.IsNullOrEmpty(_deviceId)
? $"CAN_Test_{nodeIndex}_{DateTime.Now:HHmmss}"
: _deviceId;
_driver = new CanDriverAdapter(deviceId, busName, nodeIndex);
// 订阅驱动的帧接收事件(即时显示接收到的帧)
_driver.DriverFrameReceived += OnDriverFrameReceived;
// 连接
_driver.ConnectAsync(CancellationToken.None).GetAwaiter().GetResult();
_isConnected = true;
// 模拟器模式下,连接后自动启用维护模式(允许发送)
IsMaintenanceMode = true;
if (maintenanceToggleBtn != null)
{
maintenanceToggleBtn.IsChecked = true;
maintenanceToggleBtn.IsEnabled = true;
}
// 更新 UI
UpdateConnectionStatus(true);
_pollTimer?.Start();
log.Info($"CAN 驱动已连接: Bus={busName}, Node={nodeIndex}, DeviceId={deviceId}");
}
///
/// 驱动帧接收事件处理 - 即时显示接收到的帧
///
private void OnDriverFrameReceived(object? sender, CanSimulator.CanFrame frame)
{
try
{
// 判断方向:如果帧的来源是当前节点,则为自动响应(显示为 RX)
string direction = "RX";
string source = frame.SourceNodeId;
AddReceivedFrame(
frame.FrameIdHex,
frame.DataHex,
direction,
source
);
log.Debug($"[即时显示] 接收帧: {frame}");
}
catch (Exception ex)
{
log.Error($"即时显示帧失败: {ex.Message}", ex);
}
}
///
/// 断开按钮点击
///
private void DisconnectBtn_Click(object sender, RoutedEventArgs e)
{
try
{
_pollTimer?.Stop();
if (_driver != null)
{
_driver.DriverFrameReceived -= OnDriverFrameReceived;
_driver.DisconnectAsync().GetAwaiter().GetResult();
_driver.Dispose();
}
_driver = null;
_isConnected = false;
IsMaintenanceMode = false;
UpdateConnectionStatus(false);
log.Info("CAN 驱动已断开");
}
catch (Exception ex)
{
log.Error($"CAN 断开失败: {ex.Message}", ex);
}
}
///
/// 混杂模式按钮点击
///
private void PromiscuousBtn_Click(object sender, RoutedEventArgs e)
{
if (!_isConnected || _driver == null) return;
try
{
_driver.QueryAsync("PROMISCUOUS", CancellationToken.None).GetAwaiter().GetResult();
log.Info("已设置混杂模式(接收所有帧)");
MessageBox.Show("已设置混杂模式,将接收所有 CAN 帧", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
log.Error($"设置混杂模式失败: {ex.Message}");
}
}
///
/// 维护模式切换按钮点击
///
private void MaintenanceToggleBtn_Click(object sender, RoutedEventArgs e)
{
bool isOn = maintenanceToggleBtn.IsChecked == true;
IsMaintenanceMode = isOn;
if (isOn)
{
maintenanceToggleBtn.Content = "维护模式: 开";
maintenanceToggleBtn.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4CAF50"));
log.Info("维护模式已开启(可发送 CAN 帧)");
}
else
{
maintenanceToggleBtn.Content = "维护模式: 关";
maintenanceToggleBtn.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#9E9E9E"));
log.Info("维护模式已关闭(只读监控)");
}
}
///
/// 自动响应模式切换按钮点击
///
private void AutoResponseToggleBtn_Click(object sender, RoutedEventArgs e)
{
if (!_isConnected || _driver == null) return;
try
{
bool isOn = autoResponseToggleBtn.IsChecked == true;
// 通过驱动发送命令启用/禁用自动响应
string cmd = isOn ? "AUTO_RESPONSE:ON" : "AUTO_RESPONSE:OFF";
_driver.QueryAsync(cmd, CancellationToken.None).GetAwaiter().GetResult();
if (isOn)
{
autoResponseToggleBtn.Content = "自动响应: 开";
autoResponseToggleBtn.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF9800"));
log.Info("自动响应模式已开启(收到帧将自动回复)");
}
else
{
autoResponseToggleBtn.Content = "自动响应: 关";
autoResponseToggleBtn.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#9E9E9E"));
log.Info("自动响应模式已关闭");
}
}
catch (Exception ex)
{
log.Error($"切换自动响应模式失败: {ex.Message}");
MessageBox.Show($"切换失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
///
/// 更新连接状态显示
///
private void UpdateConnectionStatus(bool connected)
{
_isConnected = connected;
if (connected)
{
statusIndicator.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4CAF50"));
statusText.Text = "已连接";
connectBtn.IsEnabled = false;
disconnectBtn.IsEnabled = true;
promiscuousBtn.IsEnabled = true;
maintenanceToggleBtn.IsEnabled = true;
autoResponseToggleBtn.IsEnabled = true;
busNameInput.IsEnabled = false;
nodeIndexInput.IsEnabled = false;
}
else
{
statusIndicator.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#9E9E9E"));
statusText.Text = "未连接";
connectBtn.IsEnabled = true;
disconnectBtn.IsEnabled = false;
promiscuousBtn.IsEnabled = false;
maintenanceToggleBtn.IsEnabled = false;
autoResponseToggleBtn.IsEnabled = false;
busNameInput.IsEnabled = true;
nodeIndexInput.IsEnabled = true;
}
}
///
/// 更新统计信息
///
private void UpdateStats()
{
if (_driver == null) return;
try
{
var status = _driver.GetStatus();
var response = _driver.QueryAsync("STATUS", CancellationToken.None).GetAwaiter().GetResult();
// 解析状态: "STATUS:Node=..., Active=True, Rx=0, Tx=0, Err=0, Mailbox=0|Bus=..."
statsText.Text = $"TX: {GetStatValue(response, "Tx")} RX: {GetStatValue(response, "Rx")} Err: {GetStatValue(response, "Err")}";
}
catch
{
// 忽略统计更新失败
}
}
///
/// 从状态字符串中提取数值值
///
private string GetStatValue(string statusStr, string key)
{
try
{
var parts = statusStr.Split(',');
foreach (var part in parts)
{
var kv = part.Trim().Split('=');
if (kv.Length == 2 && kv[0].Trim() == key)
{
return kv[1].Trim();
}
}
}
catch { }
return "0";
}
///
/// 添加接收帧
///
public void AddReceivedFrame(string frameId, string data, string direction = "RX", string source = "")
{
try
{
var frame = new CanFrameDisplay
{
Timestamp = DateTime.Now,
FrameId = frameId,
Dlc = data.Split(new[] { ' ', '-' }, StringSplitOptions.RemoveEmptyEntries).Length,
Data = data,
Direction = direction,
Source = source
};
Application.Current.Dispatcher.Invoke(() =>
{
ReceivedFrames.Insert(0, frame);
// 限制显示数量
if (ReceivedFrames.Count > 500)
{
var toRemove = ReceivedFrames.Skip(500).ToList();
foreach (var item in toRemove)
{
ReceivedFrames.Remove(item);
}
}
});
}
catch (Exception ex)
{
log.Error($"添加接收帧失败: {ex.Message}", ex);
}
}
///
/// 发送按钮点击
///
private void SendBtn_Click(object sender, RoutedEventArgs e)
{
try
{
if (!IsMaintenanceMode)
{
MessageBox.Show("当前为只读模式,无法发送数据", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
if (!_isConnected || _driver == null)
{
MessageBox.Show("请先连接 CAN 总线", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
string frameIdStr = frameIdInput.Text.Trim();
string dataStr = dataInput.Text.Trim();
// 验证帧 ID
uint frameId = CanSimulator.CanFrame.ParseFrameId(frameIdStr);
// 验证数据
byte[] dataBytes = CanSimulator.CanFrame.ParseData(dataStr);
if (dataBytes.Length > 8)
{
MessageBox.Show("数据长度不能超过 8 字节", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
// 通过驱动发送
string command = $"SEND:{frameIdStr}:{dataStr}";
var response = _driver.SendCommandAsync(command, CancellationToken.None).GetAwaiter().GetResult();
// 添加到显示列表(TX)
AddReceivedFrame($"0x{frameId:X3}", BitConverter.ToString(dataBytes).Replace("-", " "), "TX", "LOCAL");
log.Info($"发送 CAN 帧: ID=0x{frameId:X3}, Data={BitConverter.ToString(dataBytes)}, Response={response}");
}
catch (Exception ex)
{
log.Error($"发送 CAN 帧失败: {ex.Message}", ex);
MessageBox.Show($"发送失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
///
/// 清空按钮点击
///
private void ClearBtn_Click(object sender, RoutedEventArgs e)
{
try
{
ReceivedFrames.Clear();
log.Info("清空 CAN 接收记录");
}
catch (Exception ex)
{
log.Error($"清空接收记录失败: {ex.Message}", ex);
}
}
}
}