From 3b41459dcd2215132b8ff606f79eee35329a65aa Mon Sep 17 00:00:00 2001 From: hsc Date: Mon, 6 Jul 2026 13:29:34 +0800 Subject: [PATCH] =?UTF-8?q?CAN=E5=8A=9F=E8=83=BD=E7=A7=BB=E6=A4=8D?= =?UTF-8?q?=E5=85=BC=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ADP/ViewModels/ShellViewModel.cs | 38 +- ADP/Views/ShellView.xaml | 19 +- .../{UpdateInfoModule.cs => CANModule.cs} | 3 +- .../CollectCANSingalSettingViewModel.cs | 249 +++++++ CANModule/ViewModels/ZLGCANFDViewModel.cs | 649 ++++++++++++++++++ .../Views/CollectCANSignalSettingView.xaml | 120 ++++ .../Views/CollectCANSignalSettingView.xaml.cs | 25 + CANModule/Views/ZLGCANFDView.xaml | 286 ++++++++ CANModule/Views/ZLGCANFDView.xaml.cs | 35 + UIShare/GlobalVariable/DeviceManager.cs | 143 +++- UIShare/GlobalVariable/SystemConfig.cs | 2 + UIShare/UIViewModel/AutoDBCLoadItemVM.cs | 33 + UIShare/UIViewModel/CANSignalConfigVM.cs | 14 + UIShare/UIViewModel/CanMessageShowVM.cs | 37 +- UIShare/ViewModelBase/DialogViewModelBase.cs | 30 + ZLGUSBCANFD/USBCANFD.cs | 33 +- 16 files changed, 1658 insertions(+), 58 deletions(-) rename CANModule/{UpdateInfoModule.cs => CANModule.cs} (66%) create mode 100644 CANModule/ViewModels/CollectCANSingalSettingViewModel.cs create mode 100644 CANModule/ViewModels/ZLGCANFDViewModel.cs create mode 100644 CANModule/Views/CollectCANSignalSettingView.xaml create mode 100644 CANModule/Views/CollectCANSignalSettingView.xaml.cs create mode 100644 CANModule/Views/ZLGCANFDView.xaml create mode 100644 CANModule/Views/ZLGCANFDView.xaml.cs create mode 100644 UIShare/UIViewModel/AutoDBCLoadItemVM.cs create mode 100644 UIShare/UIViewModel/CANSignalConfigVM.cs diff --git a/ADP/ViewModels/ShellViewModel.cs b/ADP/ViewModels/ShellViewModel.cs index a4aaaf8..d415a2b 100644 --- a/ADP/ViewModels/ShellViewModel.cs +++ b/ADP/ViewModels/ShellViewModel.cs @@ -1,6 +1,7 @@  using Logger; using MaterialDesignThemes.Wpf; +using Microsoft.Win32; using Model.Models; using Notifications.Wpf.Core; using System.Collections.Concurrent; @@ -147,7 +148,9 @@ namespace ADP.ViewModels public ICommand NewCommand { get; set; } public ICommand SetDefaultCommand { get; set; } public ICommand ShowDialogManagerViewCommand { get; set; } + public ICommand SelectCANSignalMonitorCommand { get; set; } public ICommand SelectCANMessageCommand { get; set; } + public ICommand GetFileStringCommand { get; set; } #endregion public ShellViewModel(IContainerProvider containerProvider) @@ -177,7 +180,9 @@ namespace ADP.ViewModels SaveAsCommand = new DelegateCommand(SaveAs); SaveCommand = new DelegateCommand(Save); SetDefaultCommand = new DelegateCommand(SetDefault); + SelectCANSignalMonitorCommand = new DelegateCommand(SelectCANSignalMonitor); SelectCANMessageCommand = new DelegateCommand(SelectCANMessage); + GetFileStringCommand = new DelegateCommand(GetFileString); _globalInfo.ContextDic.Add("default", new ScopedContext()); @@ -200,7 +205,7 @@ namespace ADP.ViewModels _uiRefreshTimer.Start(); } - + private void RefreshAllContextProperties() { RaisePropertyChanged(nameof(RunState)); @@ -211,6 +216,37 @@ namespace ADP.ViewModels } #region 命令处理与事件 + private void GetFileString() + { + var openFileDialog = new OpenFileDialog + { + Title = "选择文件", + Filter = "所有文件 (*.*)|*.*", + }; + if (openFileDialog.ShowDialog() == true) + { + string filePath = openFileDialog.FileName; + Application.Current.Dispatcher.Invoke(() => + { + Clipboard.SetDataObject(filePath); + }); + } + } + private void SelectCANSignalMonitor() + { + var canfd = CurrentConfig?.CANFD; + if (canfd == null || _globalInfo.CurrentScope == "default") + { + LoggerHelper.Warn("当前作用域未配置 CAN 设备,无法打开报文选择窗口。"); + return; + } + + _dialogService.ShowDialog("CollectCANSignalSettingView", new DialogParameters + { + { "SystemConfig", CurrentConfig } + }, _ => { }); + } + private void SelectCANMessage() { var canfd = CurrentConfig?.CANFD; diff --git a/ADP/Views/ShellView.xaml b/ADP/Views/ShellView.xaml index 0084fad..52d0df6 100644 --- a/ADP/Views/ShellView.xaml +++ b/ADP/Views/ShellView.xaml @@ -158,13 +158,30 @@ Foreground="Black" /> - + + + + + + + + + + + + diff --git a/CANModule/UpdateInfoModule.cs b/CANModule/CANModule.cs similarity index 66% rename from CANModule/UpdateInfoModule.cs rename to CANModule/CANModule.cs index aa035ac..9944f14 100644 --- a/CANModule/UpdateInfoModule.cs +++ b/CANModule/CANModule.cs @@ -1,4 +1,3 @@ -using System.Reflection; using CANModule.Views; namespace CANModule @@ -12,6 +11,8 @@ namespace CANModule public void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterDialog("SelectCanMessageView"); + containerRegistry.RegisterDialog("CollectCANSignalSettingView"); + containerRegistry.RegisterForNavigation("ZLGCANFDView"); } } } diff --git a/CANModule/ViewModels/CollectCANSingalSettingViewModel.cs b/CANModule/ViewModels/CollectCANSingalSettingViewModel.cs new file mode 100644 index 0000000..d5eff53 --- /dev/null +++ b/CANModule/ViewModels/CollectCANSingalSettingViewModel.cs @@ -0,0 +1,249 @@ +using System; +using System.Collections.ObjectModel; +using System.Linq; +using System.Windows.Input; +using UIShare.GlobalVariable; +using UIShare.PubEvent; +using UIShare.UIViewModel; +using UIShare.ViewModelBase; +using ZLGUSBCANFD; + +namespace CANModule.ViewModels +{ + public class CollectCANSignalSettingViewModel : DialogViewModelBase + { + #region 字段 + private ZLGCANFD? _canfd; + private SystemConfig? _systemConfig; + #endregion + + #region 属性 + public ObservableCollection ConfigurationList + { + get => _systemConfig?.ConfigurationList ?? new ObservableCollection(); + } + + + + private ObservableCollection _messageList; + public ObservableCollection MessageList + { + get => _messageList; + set => SetProperty(ref _messageList, value); + } + + private ObservableCollection _signalList; + public ObservableCollection SignalList + { + get => _signalList; + set => SetProperty(ref _signalList, value); + } + + private string _title = "信号采集设置"; + public string Title + { + get => _title; + set => SetProperty(ref _title, value); + } + + private string _selectedMessage; + public string SelectedMessage + { + get => _selectedMessage; + set + { + if (SetProperty(ref _selectedMessage, value)) + { + if (!string.IsNullOrEmpty(value) && _canfd != null) + { + var db = _canfd.DBCParser.MsgDatabase; + if (MessageChannel >= 0 && MessageChannel < db.Count) + { + var targetMsg = db[MessageChannel] + .FirstOrDefault(x => $"[0x{x.msg_id:X}]{x.msg_name}" == value); + + if (targetMsg != null && targetMsg.signal_Name != null) + { + SignalList = new ObservableCollection(targetMsg.signal_Name); + } + else + { + SignalList = new ObservableCollection(); + } + } + } + else + { + SignalList = new ObservableCollection(); + } + } + } + } + + private CANSignalConfig _SelectedItem; + public CANSignalConfig SelectedItem + { + get => _SelectedItem; + set => SetProperty(ref _SelectedItem, value); + } + + private string _selectedSignal; + public string SelectedSignal + { + get => _selectedSignal; + set => SetProperty(ref _selectedSignal, value); + } + + private int _MessageChannel=-1; + public int MessageChannel + { + get => _MessageChannel; + set + { + if (_MessageChannel != value) + { + _MessageChannel = value; + + SelectedMessage = null; + SelectedSignal = null; + SignalList = new ObservableCollection(); + + if (_canfd != null) + { + var db = _canfd.DBCParser.MsgDatabase; + if (db != null && value >= 0 && value < db.Count) + { + MessageList = new ObservableCollection( + db[value].Select(s => $"[0x{s.msg_id:X}]{s.msg_name}") + ); + } + else + { + MessageList = new ObservableCollection(); + } + } + else + { + MessageList = new ObservableCollection(); + } + + RaisePropertyChanged(); + } + } + } + + private int _collectionInterval = 1000; + public int CollectionInterval + { + get => _collectionInterval; + set => SetProperty(ref _collectionInterval, value); + } + #endregion + + #region 命令 + public ICommand AddCommand { get; set; } + public ICommand CloseCommand { get; set; } + public ICommand SaveCommand { get; set; } + public ICommand DeleteCommand { get; set; } + #endregion + + public CollectCANSignalSettingViewModel(IContainerProvider containerProvider) : base(containerProvider) + { + CloseCommand = new DelegateCommand(Close); + AddCommand = new DelegateCommand(Add); + SaveCommand = new DelegateCommand(Save); + DeleteCommand = new DelegateCommand(Delete); + } + + #region Dialog 生命周期 + public override void OnDialogOpened(IDialogParameters parameters) + { + _systemConfig = parameters.GetValue("SystemConfig"); + _canfd = _systemConfig?.CANFD; + string title = _systemConfig.Title; + char lastChar = title[title.Length - 1]; + // 3. "TestCellX"转int + MessageChannel = int.Parse(lastChar.ToString())-1; + RaisePropertyChanged(nameof(ConfigurationList)); + } + #endregion + + #region 命令处理 + private void Delete() + { + if (SelectedItem != null) + { + _systemConfig?.ConfigurationList.Remove(SelectedItem); + } + } + + private void Save() + { + ConfigService.Save(_systemConfig); + } + + + private void Add() + { + if (string.IsNullOrEmpty(SelectedSignal) || string.IsNullOrEmpty(SelectedMessage) || CollectionInterval < 100) + { + return; + } + if (_systemConfig == null) return; + + string hexString = ExtractHex(SelectedMessage); + if (string.IsNullOrEmpty(hexString)) return; + + int decimalValue = ConvertHexToDecimal(hexString); + if (_systemConfig.ConfigurationList.Any(x => x.SignalName == SelectedSignal && x.Channel == MessageChannel)) + { + return; + } + + _systemConfig.ConfigurationList.Add(new CANSignalConfig + { + CollectionID = Guid.NewGuid(), + Channel = MessageChannel, + MessageName = ExtractMessageName(SelectedMessage), + SignalName = SelectedSignal, + MessageID = decimalValue, + CollectionInterval = CollectionInterval + }); + } + + private string ExtractHex(string input) + { + int startIndex = input.IndexOf("0x") + 2; + int endIndex = input.IndexOf("]", startIndex); + if (startIndex >= 2 && endIndex > startIndex) + { + return input.Substring(startIndex, endIndex - startIndex); + } + return string.Empty; + } + + private int ConvertHexToDecimal(string hex) + { + try + { + return Convert.ToInt32(hex, 16); + } + catch (FormatException) + { + return -1; + } + } + + private string ExtractMessageName(string input) + { + var parts = input.Split(']'); + return parts.Length > 1 ? parts[1] : string.Empty; + } + + private void Close() + { + RequestClose.Invoke(); + } + #endregion + } +} diff --git a/CANModule/ViewModels/ZLGCANFDViewModel.cs b/CANModule/ViewModels/ZLGCANFDViewModel.cs new file mode 100644 index 0000000..9688c8a --- /dev/null +++ b/CANModule/ViewModels/ZLGCANFDViewModel.cs @@ -0,0 +1,649 @@ +using Microsoft.Win32; +using System; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Input; +using UIShare.GlobalVariable; +using UIShare.UIViewModel; +using UIShare.ViewModelBase; +using ZLGUSBCANFD; + +namespace CANModule.ViewModels +{ + public class ZLGCANFDViewModel : NavigateViewModelBase + { + #region 字段 + private ZLGCANFD? _canfd; + private SystemConfig? _systemConfig; + private CancellationTokenSource? _refreshCts; + private readonly DeviceManager _deviceManager; + #endregion + + #region 属性 + private string _Title = "周立功CAN"; + public string Title + { + get => _Title; + set => SetProperty(ref _Title, value); + } + + private ObservableCollection _CanMessageList = new(); + public ObservableCollection CanMessageList + { + get => _CanMessageList; + set => SetProperty(ref _CanMessageList, value); + } + + private int _DBCChannel = 0; + public int DBCChannel + { + get => _DBCChannel; + set => SetProperty(ref _DBCChannel, value); + } + + private bool _IsDirectlySend = false; + public bool IsDirectlySend + { + get => _IsDirectlySend; + set => SetProperty(ref _IsDirectlySend, value); + } + + private ObservableCollection _Message; + public ObservableCollection Message + { + get => _Message; + set => SetProperty(ref _Message, value); + } + + private ObservableCollection _Signal; + public ObservableCollection Signal + { + get => _Signal; + set => SetProperty(ref _Signal, value); + } + + private byte _channel; + private string _selectedMessage; + public string SelectedMessage + { + get => _selectedMessage; + set + { + if (_selectedMessage != value) + { + if (!string.IsNullOrEmpty(value) && _canfd != null) + { + var db = _canfd.DBCParser.MsgDatabase; + if (_channel >= 0 && _channel < db.Count) + { + var targetMsg = db[(int)_channel] + .FirstOrDefault(x => $"[0x{x.msg_id:X}]{x.msg_name}" == value); + + if (targetMsg != null && targetMsg.signal_Name != null) + { + Signal = new ObservableCollection(targetMsg.signal_Name); + } + else + { + Signal = new ObservableCollection(); + } + } + } + else + { + Signal = new ObservableCollection(); + } + _selectedMessage = value; + RaisePropertyChanged(); + } + } + } + + private string _selectedSignal; + public string SelectedSignal + { + get => _selectedSignal; + set => SetProperty(ref _selectedSignal, value); + } + + private string _signalValue; + public string SignalValue + { + get => _signalValue; + set => SetProperty(ref _signalValue, value); + } + + private string _cycleSendInterval; + public string CycleSendInterval + { + get => _cycleSendInterval; + set => SetProperty(ref _cycleSendInterval, value); + } + + private int _messageSendChannel = 0; + public int MessageSendChannel + { + get => _messageSendChannel; + set => SetProperty(ref _messageSendChannel, value); + } + + public string BLFPath + { + get => _systemConfig?.DefaultBLFFilePath ?? ""; + set + { + if (_systemConfig != null && _systemConfig.DefaultBLFFilePath != value) + { + _systemConfig.DefaultBLFFilePath = value; + RaisePropertyChanged(); + } + } + } + + public string DBCPath + { + get => _systemConfig?.DefaultDBCFilePath ?? ""; + set + { + if (_systemConfig != null && _systemConfig.DefaultDBCFilePath != value) + { + _systemConfig.DefaultDBCFilePath = value; + RaisePropertyChanged(); + } + } + } + + private bool _IsSaved; + public bool IsSaved + { + get => _IsSaved; + set => SetProperty(ref _IsSaved, value); + } + + public ObservableCollection dbcAutoLoadList + { + get => _systemConfig?.dbcAutoLoadList ?? new ObservableCollection(); + } + + private AutoDBCLoadItem _SelectedDcAutoLoad; + public AutoDBCLoadItem SelectedDcAutoLoad + { + get => _SelectedDcAutoLoad; + set => SetProperty(ref _SelectedDcAutoLoad, value); + } + + // ===== 自定义报文发送属性 ===== + private APP_CHANNEL[] _appChannelNames = Enum.GetValues(); + public APP_CHANNEL[] APP_CHANNEL + { + get => _appChannelNames; + set => SetProperty(ref _appChannelNames, value); + } + + private APP_CHANNEL _aIdxChn; + public APP_CHANNEL AIdxChn + { + get => _aIdxChn; + set => SetProperty(ref _aIdxChn, value); + } + + private int _aID; + public int AID + { + get => _aID; + set => SetProperty(ref _aID, value); + } + + private bool _aIsTx; + public bool AIsTx + { + get => _aIsTx; + set => SetProperty(ref _aIsTx, value); + } + + private bool _aIsExt; + public bool AIsExt + { + get => _aIsExt; + set => SetProperty(ref _aIsExt, value); + } + + private bool _aIsRemote; + public bool AIsRemote + { + get => _aIsRemote; + set => SetProperty(ref _aIsRemote, value); + } + + private byte _aDlc; + public byte ADLC + { + get => _aDlc; + set => SetProperty(ref _aDlc, value); + } + + private byte[] _aDataArray; + public byte[] ADataArray + { + get => _aDataArray; + set => SetProperty(ref _aDataArray, value); + } + + private bool _aIsFD = true; + public bool AIsFD + { + get => _aIsFD; + set => SetProperty(ref _aIsFD, value); + } + + private bool _aIsBRS = false; + public bool AIsBRS + { + get => _aIsBRS; + set => SetProperty(ref _aIsBRS, value); + } + + private bool _AutoSetting = true; + public bool AutoSetting + { + get => _AutoSetting; + set => SetProperty(ref _AutoSetting, value); + } + + private string _CustomMessage = "[00,11,22,33,AA,BB,CC,DD]"; + public string CustomMessage + { + get => _CustomMessage; + set => SetProperty(ref _CustomMessage, value); + } + + private float _CustomSendCycleInterval = 0.0f; + public float CustomSendCycleInterval + { + get => _CustomSendCycleInterval; + set => SetProperty(ref _CustomSendCycleInterval, value); + } + + //private bool _UpdateToDB; + //public bool UpdateToDB + //{ + // get => _UpdateToDB; + // set => SetProperty(ref _UpdateToDB, value); + //} + + // ===== 连接状态 ===== + private bool _IsDeviceOpened; + public bool IsDeviceOpened + { + get => _IsDeviceOpened; + set => SetProperty(ref _IsDeviceOpened, value); + } + + private ObservableCollection _ChannelList; + public ObservableCollection ChannelList + { + get => _ChannelList; + set => SetProperty(ref _ChannelList, value); + } + + private int _SelectedChannel = -1; + public int SelectedChannel + { + get => _SelectedChannel; + set + { + if (_SelectedChannel != value) + { + _SelectedChannel = value; + if (value >= 0 && _canfd != null) + { + _canfd.初始化并启动通道((uint)value); + } + RaisePropertyChanged(); + } + } + } + #endregion + + #region 命令 + public ICommand SelectBLFPathCommand { get; set; } + public ICommand SaveBLFPathCommand { get; set; } + public ICommand StartTranscribeCommand { get; set; } + public ICommand EndTranscribeCommand { get; set; } + public ICommand ConnectCanCommand { get; set; } + public ICommand CloseCanCommand { get; set; } + public ICommand ClearCycleCommand { get; set; } + public ICommand UpLoadDBCCommand { get; set; } + public ICommand ChangeDBCCommand { get; set; } + public ICommand LoadDBCCommand { get; set; } + public ICommand LoadMessageCommand { get; set; } + public ICommand SetAndSendMessageCommand { get; set; } + public ICommand DeleteDcAutoLoadCommand { get; set; } + public ICommand SendCustomMessageCommand { get; set; } + public ICommand LoadCommand { get; set; } + #endregion + + public ZLGCANFDViewModel(IContainerProvider containerProvider) : base(containerProvider) + { + _systemConfig = containerProvider.Resolve(); + _deviceManager = containerProvider.Resolve(); + _canfd = _deviceManager.CANFD; + SelectBLFPathCommand = new DelegateCommand(SelectBLFPath); + SaveBLFPathCommand = new DelegateCommand(SaveBLFPath); + StartTranscribeCommand = new DelegateCommand(StartTranscribe); + EndTranscribeCommand = new DelegateCommand(EndTranscribe); + ConnectCanCommand = new DelegateCommand(ConnectCan); + CloseCanCommand = new DelegateCommand(CloseCan); + ClearCycleCommand = new DelegateCommand(ClearCycle); + UpLoadDBCCommand = new DelegateCommand(UpLoadDBC); + ChangeDBCCommand = new DelegateCommand(ChangeDBC); + LoadDBCCommand = new DelegateCommand(LoadDBC); + SetAndSendMessageCommand = new DelegateCommand(SetAndSendMessage); + LoadMessageCommand = new DelegateCommand(LoadMessage); + DeleteDcAutoLoadCommand = new DelegateCommand(DeleteDcAutoLoad); + SendCustomMessageCommand = new DelegateCommand(SendCustomMessage); + _canfd.OnDbcMessageDecoded += OnDbcMessageDecoded; + LoadCommand = new DelegateCommand(Load); + } + + #region Dialog 生命周期 + public override void OnNavigatedTo(NavigationContext navigationContext) + { + if (_canfd != null) + { + + } + + RaisePropertyChanged(nameof(BLFPath)); + RaisePropertyChanged(nameof(DBCPath)); + RaisePropertyChanged(nameof(dbcAutoLoadList)); + } + + public override void OnNavigatedFrom(NavigationContext navigationContext) + { + if (_canfd != null) + { + _canfd.OnDbcMessageDecoded -= OnDbcMessageDecoded; + } + _refreshCts?.Cancel(); + } + #endregion + + #region 命令处理 + private void Load() + { + int maxCh = _canfd?.DBCParser.MaxChannels ?? 4; + ChannelList = new ObservableCollection(Enumerable.Range(0, maxCh)); + } + + private void SendCustomMessage() + { + if (_canfd == null) return; + + try + { + ADataArray = 报文转换(CustomMessage); + if (ADataArray.Length > byte.MaxValue) throw new Exception("报文数量超过上限!"); + if (AutoSetting) + { + ADLC = (byte)ADataArray.Length; + } + } + catch (Exception ex) + { + ShowErrorMessageBox($"报文转换失败:{ex.Message}", () => { }); + return; + } + + _canfd.发送自定义报文( + (uint)AIdxChn, + AID.ToString(), + ADataArray, + ADLC, + AIsExt, + AIsFD, + AIsBRS); + } + + private void DeleteDcAutoLoad() + { + if (SelectedDcAutoLoad != null) + { + dbcAutoLoadList.Remove(SelectedDcAutoLoad); + } + } + + private void LoadMessage() + { + if (_canfd == null) return; + + if (MessageSendChannel >= 0) + { + var db = _canfd.DBCParser.MsgDatabase; + if (MessageSendChannel < db.Count) + { + Message = new ObservableCollection( + db[MessageSendChannel].Select(s => $"[0x{s.msg_id:X}]{s.msg_name}")); + } + } + } + + private void SetAndSendMessage() + { + if (_canfd == null) return; + + if (int.TryParse(CycleSendInterval, out int sendPeriod) && + double.TryParse(SignalValue, out double SValue) && + MessageSendChannel >= 0) + { + _channel = (byte)MessageSendChannel; + var message = SelectedMessage.Split(']')[1]; + + // 通过 DBC 查找帧 ID + var db = _canfd.DBCParser.MsgDatabase; + if (MessageSendChannel < db.Count) + { + var targetMsg = db[MessageSendChannel] + .FirstOrDefault(x => $"[0x{x.msg_id:X}]{x.msg_name}" == SelectedMessage); + + if (targetMsg != null) + { + _canfd.设置报文((uint)MessageSendChannel, targetMsg.msg_id.ToString(), SelectedSignal, SValue, sendPeriod); + } + } + } + } + + private void UpLoadDBC() + { + if (_canfd == null) return; + + // 释放当前通道的 DBC + if (DBCChannel >= 0) + { + _canfd.释放通道DBC((uint)DBCChannel); + } + } + + private void ChangeDBC() + { + var dialog = new OpenFileDialog + { + Title = "更换DBC文件", + Filter = "DBC 文件 (*.dbc)|*.dbc|所有文件 (*.*)|*.*", + DefaultExt = ".dbc", + }; + if (dialog.ShowDialog() == true) + { + DBCPath = System.IO.Path.GetFullPath(dialog.FileName); + } + } + + private void LoadDBC() + { + if (_canfd == null || DBCChannel < 0) return; + + bool re = _canfd.加载通道DBC文件((uint)DBCChannel, DBCPath); + + if (re && IsSaved) + { + var item = dbcAutoLoadList.FirstOrDefault(s => s.DBCChannel == DBCChannel); + if (item == null) + { + dbcAutoLoadList.Add(new AutoDBCLoadItem + { + DBCFilePath = DBCPath, + DBCChannel = DBCChannel + }); + } + else + { + dbcAutoLoadList.Remove(item); + dbcAutoLoadList.Add(new AutoDBCLoadItem + { + DBCFilePath = DBCPath, + DBCChannel = DBCChannel + }); + } + ConfigService.Save(_systemConfig); + } + } + private void ClearCycle() + { + _canfd?.停止所有循环发送(); + } + + private void CloseCan() + { + if (_canfd == null) return; + + _canfd.关闭CAN卡设备(); + IsDeviceOpened = false; + _refreshCts?.Cancel(); + } + + private void ConnectCan() + { + if (_canfd == null) return; + + bool re = _canfd.打开设备(); + IsDeviceOpened = re; + + if (re) + { + // 启动实时报文刷新 + _refreshCts?.Cancel(); + _refreshCts = new CancellationTokenSource(); + _ = Refresh(_refreshCts.Token); + } + } + + private void EndTranscribe() + { + // TODO: BLF 录制功能在 ZLGCANFD 中未实现,暂不支持 + ShowInfoMessageBox("BLF 录制功能尚未在此硬件驱动中实现。", () => { }); + } + + private void StartTranscribe() + { + // TODO: BLF 录制功能在 ZLGCANFD 中未实现,暂不支持 + ShowInfoMessageBox("BLF 录制功能尚未在此硬件驱动中实现。", () => { }); + } + + private void SaveBLFPath() + { + // 路径已直接绑定到 SystemConfig,无需额外操作 + } + + private void SelectBLFPath() + { + var dialog = new SaveFileDialog + { + Title = "保存 CAN报文回放 BLF 文件", + Filter = "BLF 文件 (*.blf)|*.blf|所有文件 (*.*)|*.*", + DefaultExt = ".blf", + FileName = "log.blf", + OverwritePrompt = true + }; + if (dialog.ShowDialog() == true) + { + BLFPath = System.IO.Path.GetFullPath(dialog.FileName); + } + } + + #endregion + + #region 辅助方法 + public byte[] 报文转换(string s) + { + s = s.Replace("[", string.Empty).Replace("]", string.Empty); + var strs = s.Split(',', StringSplitOptions.RemoveEmptyEntries); + byte[] bytes = new byte[strs.Length]; + for (int i = 0; i < strs.Length; i++) + { + bytes[i] = Convert.ToByte(strs[i], 16); + } + return bytes; + } + + private async Task Refresh(CancellationToken ct = default) + { + while (!ct.IsCancellationRequested) + { + // 通过 OnDbcMessageDecoded 事件已实时更新 CanMessageList + // 此处仅做周期性 UI 刷新触发 + await Task.Delay(50, ct); + } + } + + private void OnDbcMessageDecoded(uint channel, ZDBC.DBCMessage msg) + { + // DBC 解码后的报文回调 + Application.Current.Dispatcher.Invoke(() => + { + var msgName = System.Text.Encoding.Default.GetString(msg.strName).TrimEnd('\0'); + var find = CanMessageList.FirstOrDefault(s => s.报文ID == (int)msg.nID); + if (find != null) + { + find.通道 = (byte)channel; + find.报文ID = (int)msg.nID; + find.时间戳 = (ulong)DateTimeOffset.Now.ToUnixTimeMilliseconds(); ; + find.长度 = (byte)msg.nSize; + } + else + { + CanMessageList.Add(new CanMessageShowVM + { + 通道 = (byte)channel, + 报文ID = (int)msg.nID, + 时间戳 = (ulong)DateTimeOffset.Now.ToUnixTimeMilliseconds(), + 长度 = (byte)msg.nSize + }); + } + }); + } + + private string 十六进制字符串转换(System.Span s) + { + if (s == null || s.Length == 0) + return "[]"; + + var sb = new StringBuilder(); + sb.Append('['); + for (int i = 0; i < s.Length; i++) + { + sb.Append("0x"); + sb.Append(s[i].ToString("X2")); + if (i < s.Length - 1) + sb.Append(", "); + } + sb.Append(']'); + return sb.ToString(); + } + #endregion + } +} diff --git a/CANModule/Views/CollectCANSignalSettingView.xaml b/CANModule/Views/CollectCANSignalSettingView.xaml new file mode 100644 index 0000000..dfb3fa9 --- /dev/null +++ b/CANModule/Views/CollectCANSignalSettingView.xaml @@ -0,0 +1,120 @@ + + +