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 } }