Files
SDC_ACQ_DAC/PC_Test/SDCAcqDacTester/MainWindow.xaml.cs
T

195 lines
8.1 KiB
C#

using System.Collections.ObjectModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using SDCAcqDacTester.Can;
namespace SDCAcqDacTester;
public partial class MainWindow : Window
{
private const uint HeartbeatId = 0x6A0;
private const uint AdcId = 0x6A1;
private const uint PwmId = 0x6A2;
private const uint DacCommandId = 0x6A3;
private const uint DacAckId = 0x6A4;
private const uint AssiCommandId = 0x6A5;
private const uint GpioId = 0x6A6;
private readonly ObservableCollection<LogRow> _rows = [];
private readonly DispatcherTimer _holdTimer;
private ControlCanTransport? _can;
private bool _holdDac1;
private bool _holdDac2;
public MainWindow()
{
InitializeComponent();
LogGrid.ItemsSource = _rows;
_holdTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(50) };
_holdTimer.Tick += HoldTimer_Tick;
}
private void ConnectButton_Click(object sender, RoutedEventArgs e)
{
try
{
if (!uint.TryParse(DeviceIndexBox.Text, out uint index)) throw new InvalidOperationException("设备索引必须为非负整数。");
_can = new ControlCanTransport();
_can.FrameReceived += Can_FrameReceived;
_can.ReceiveError += Can_ReceiveError;
_can.Open(index, (uint)ChannelBox.SelectedIndex);
ConnectionText.Text = $"已连接:USBCAN-II / CAN{ChannelBox.SelectedIndex + 1}";
ConnectionText.Foreground = System.Windows.Media.Brushes.LightGreen;
ConnectButton.IsEnabled = false;
DisconnectButton.IsEnabled = true;
AddLog("SYS", "-", [], "已连接,500 kbit/s 正常模式。");
}
catch (Exception ex) { ShowError(ex.Message); }
}
private void DisconnectButton_Click(object sender, RoutedEventArgs e) => Disconnect();
private void SendDac1Once_Click(object sender, RoutedEventArgs e) => SendDac(1, ReadVolts(Dac1Box));
private void SendDac2Once_Click(object sender, RoutedEventArgs e) => SendDac(2, ReadVolts(Dac2Box));
private void HoldDac1Button_Click(object sender, RoutedEventArgs e)
{
_holdDac1 = !_holdDac1;
HoldDac1Button.Content = _holdDac1 ? "停止保持" : "开始保持";
UpdateHoldTimer();
}
private void HoldDac2Button_Click(object sender, RoutedEventArgs e)
{
_holdDac2 = !_holdDac2;
HoldDac2Button.Content = _holdDac2 ? "停止保持" : "开始保持";
UpdateHoldTimer();
}
private void HoldTimer_Tick(object? sender, EventArgs e)
{
try
{
if (_holdDac1) SendDac(1, ReadVolts(Dac1Box));
if (_holdDac2) SendDac(2, ReadVolts(Dac2Box));
}
catch (Exception ex)
{
_holdDac1 = _holdDac2 = false;
HoldDac1Button.Content = HoldDac2Button.Content = "开始保持";
UpdateHoldTimer();
ShowError(ex.Message);
}
}
private void Assi_Click(object sender, RoutedEventArgs e)
{
if (sender is not Button { Tag: string value } || !byte.TryParse(value, out byte state)) return;
Send(AssiCommandId, [state], $"ASSI 状态={state}");
}
private void SendRaw_Click(object sender, RoutedEventArgs e)
{
try
{
uint id = uint.Parse(RawIdBox.Text.Trim().Replace("0x", "", StringComparison.OrdinalIgnoreCase), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
byte[] data = string.IsNullOrWhiteSpace(RawDataBox.Text) ? [] : RawDataBox.Text.Split([' ', ',', ';'], StringSplitOptions.RemoveEmptyEntries)
.Select(x => byte.Parse(x.Replace("0x", "", StringComparison.OrdinalIgnoreCase), NumberStyles.HexNumber, CultureInfo.InvariantCulture)).ToArray();
Send(id, data, "手工帧");
}
catch (Exception ex) { ShowError($"手工帧格式错误:{ex.Message}"); }
}
private void SendDac(byte dac, ushort millivolts)
{
Send(DacCommandId, [dac, (byte)(millivolts & 0xFF), (byte)(millivolts >> 8)], $"DAC{dac}={millivolts} mV");
}
private void Send(uint id, byte[] data, string description)
{
try
{
if (_can is not { IsOpen: true }) throw new InvalidOperationException("请先连接 CAN。 ");
_can.SendStandard(id, data);
AddLog("TX", $"0x{id:X3}", data, description);
}
catch (Exception ex) { ShowError(ex.Message); }
}
private static ushort ReadVolts(TextBox box)
{
if (!decimal.TryParse(box.Text, NumberStyles.Number, CultureInfo.InvariantCulture, out decimal volts) &&
!decimal.TryParse(box.Text, NumberStyles.Number, CultureInfo.CurrentCulture, out volts))
throw new InvalidOperationException("DAC 电压必须是 0 - 5 V 的数值。");
if (volts is < 0 or > 5) throw new InvalidOperationException("DAC 电压范围为 0 - 5 V。");
return (ushort)Math.Round(volts * 1000m, MidpointRounding.AwayFromZero);
}
private void Can_FrameReceived(object? sender, CanFrame frame) => Dispatcher.Invoke(() =>
{
string description = Decode(frame);
AddLog("RX", frame.IsExtended ? $"0x{frame.Id:X8}" : $"0x{frame.Id:X3}", frame.Data, description, frame.Timestamp);
});
private void Can_ReceiveError(object? sender, string error) => Dispatcher.Invoke(() => ShowError($"接收异常:{error}"));
private string Decode(CanFrame frame)
{
var d = frame.Data;
switch (frame.Id)
{
case HeartbeatId when d.Length >= 8:
string heartbeat = $"板号=0x{d[0]:X2} 协议={d[1]} 序号={U16(d, 2)} 运行={U32(d, 4)} ms";
HeartbeatValue.Text = heartbeat; return heartbeat;
case AdcId when d.Length >= 2:
string adc = $"{U16(d, 0)} mV ({U16(d, 0) / 1000.0:F3} V)";
AdcValue.Text = adc; return adc;
case PwmId when d.Length >= 4:
string pwm = $"左前 PA0={U16(d, 0) / 1000.0:F3} m/s / 右前 PA6={U16(d, 2) / 1000.0:F3} m/s";
PwmValue.Text = pwm; return pwm;
case DacAckId when d.Length >= 4:
string ack = $"DAC{d[1]} {(d[0] == 1 ? "OK" : "ERR")} = {U16(d, 2)} mV";
DacAckValue.Text = ack; return ack;
case GpioId when d.Length >= 8:
string gpio = $"状态=0x{U16(d, 0):X4} 变化=0x{U16(d, 2):X4} 时间={U32(d, 4)} ms";
GpioValue.Text = gpio; return gpio;
default: return "未定义帧";
}
}
private static ushort U16(byte[] data, int offset) => (ushort)(data[offset] | (data[offset + 1] << 8));
private static uint U32(byte[] data, int offset) => (uint)(data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24));
private void AddLog(string direction, string id, byte[] data, string description, DateTime? timestamp = null)
{
_rows.Insert(0, new LogRow((timestamp ?? DateTime.Now).ToString("HH:mm:ss.fff"), direction, id, data.Length.ToString(), BitConverter.ToString(data).Replace('-', ' '), description));
while (_rows.Count > 500) _rows.RemoveAt(_rows.Count - 1);
}
private void UpdateHoldTimer()
{
if (_holdDac1 || _holdDac2) _holdTimer.Start(); else _holdTimer.Stop();
}
private void Disconnect()
{
_holdDac1 = _holdDac2 = false;
_holdTimer.Stop();
_can?.Dispose(); _can = null;
ConnectionText.Text = "未连接"; ConnectionText.Foreground = System.Windows.Media.Brushes.Orange;
ConnectButton.IsEnabled = true; DisconnectButton.IsEnabled = false;
}
private void ShowError(string message)
{
AddLog("ERR", "-", [], message);
MessageBox.Show(message, "SDC-ACQ_DAC 测试工具", MessageBoxButton.OK, MessageBoxImage.Warning);
}
private void Window_Closing(object? sender, System.ComponentModel.CancelEventArgs e) => Disconnect();
}
public sealed record LogRow(string Time, string Direction, string Id, string Dlc, string Data, string Description);