582 lines
19 KiB
C#
582 lines
19 KiB
C#
|
|
using Microsoft.Win32;
|
|
using Model;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Collections.Specialized;
|
|
using System.ComponentModel;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Channels;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using System.Windows.Data;
|
|
using System.Windows.Input;
|
|
using System.Windows.Threading;
|
|
using TSMasterCAN;
|
|
using UIShare.GlobalVariable;
|
|
using UIShare.UIViewModel;
|
|
|
|
namespace CANModule.ViewModels
|
|
{
|
|
public class CANViewModel : BindableBase, IDialogAware
|
|
{
|
|
|
|
#region 属性
|
|
private string _Title = "同星CAN";
|
|
|
|
public string Title
|
|
{
|
|
get { return _Title; }
|
|
set { SetProperty(ref _Title, value); }
|
|
}
|
|
private ObservableCollection<CanMessageShowVM> _CanMessageList = new();
|
|
|
|
public ObservableCollection<CanMessageShowVM> CanMessageList
|
|
{
|
|
get { return _CanMessageList; }
|
|
set { SetProperty(ref _CanMessageList, value); }
|
|
}
|
|
private string _DBCChannel = "0";
|
|
|
|
public string DBCChannel
|
|
{
|
|
get { return _DBCChannel; }
|
|
set { SetProperty(ref _DBCChannel, value); }
|
|
}
|
|
private bool _IsDirectlySend = false;
|
|
|
|
public bool IsDirectlySend
|
|
{
|
|
get { return _IsDirectlySend; }
|
|
set { SetProperty(ref _IsDirectlySend, value); }
|
|
}
|
|
private ObservableCollection<string> _Message;
|
|
public ObservableCollection<string> Message
|
|
{
|
|
get { return _Message; }
|
|
set { SetProperty(ref _Message, value); }
|
|
}
|
|
private ObservableCollection<string> _Signal;
|
|
public ObservableCollection<string> Signal
|
|
{
|
|
get { return _Signal; }
|
|
set { SetProperty(ref _Signal, value); }
|
|
}
|
|
byte channel;
|
|
private string _selectedMessage;
|
|
public string SelectedMessage
|
|
{
|
|
get { return _selectedMessage; }
|
|
set
|
|
{
|
|
if (_selectedMessage != value)
|
|
{
|
|
Signal = new(DBCParse.MsgDatabase[channel].Where(x => $"[0x{x.msg_id:X}]{x.msg_name}" == value).First().signal_Name);
|
|
_selectedMessage = value;
|
|
RaisePropertyChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
private string _selectedSignal;
|
|
public string SelectedSignal
|
|
{
|
|
get { return _selectedSignal; }
|
|
set { SetProperty(ref _selectedSignal, value); }
|
|
}
|
|
|
|
private string _signalValue;
|
|
public string SignalValue
|
|
{
|
|
get { return _signalValue; }
|
|
set { SetProperty(ref _signalValue, value); }
|
|
}
|
|
|
|
private string _cycleSendInterval;
|
|
public string CycleSendInterval
|
|
{
|
|
get { return _cycleSendInterval; }
|
|
set { SetProperty(ref _cycleSendInterval, value); }
|
|
}
|
|
|
|
private string _messageSendChannel = "0";
|
|
public string MessageSendChannel
|
|
{
|
|
get { return _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 { return _IsSaved; }
|
|
set { SetProperty(ref _IsSaved, value); }
|
|
}
|
|
public ObservableCollection<AutoDBCLoadItem> DBCAutoLoadList
|
|
{
|
|
get => _systemConfig?.DBCAutoLoadList ?? new ObservableCollection<AutoDBCLoadItem>();
|
|
}
|
|
|
|
private AutoDBCLoadItem _SelectedDcAutoLoad;
|
|
public AutoDBCLoadItem SelectedDcAutoLoad
|
|
{
|
|
get { return _SelectedDcAutoLoad; }
|
|
set { SetProperty(ref _SelectedDcAutoLoad, value); }
|
|
}
|
|
private TSMaster.APP_CHANNEL[] _appChannelNames = new[] { TSMaster.APP_CHANNEL.CHN1, TSMaster.APP_CHANNEL.CHN2, TSMaster.APP_CHANNEL.CHN3, TSMaster.APP_CHANNEL.CHN4 };
|
|
public TSMaster.APP_CHANNEL[] APP_CHANNEL
|
|
{
|
|
get { return _appChannelNames; }
|
|
set { SetProperty(ref _appChannelNames, value); }
|
|
}
|
|
|
|
private TSMaster.APP_CHANNEL _aIdxChn;
|
|
public TSMaster.APP_CHANNEL AIdxChn
|
|
{
|
|
get { return _aIdxChn; }
|
|
set { SetProperty(ref _aIdxChn, value); }
|
|
}
|
|
|
|
private int _aID;
|
|
public int AID
|
|
{
|
|
get { return _aID; }
|
|
set { SetProperty(ref _aID, value); }
|
|
}
|
|
|
|
private bool _aIsTx;
|
|
public bool AIsTx
|
|
{
|
|
get { return _aIsTx; }
|
|
set { SetProperty(ref _aIsTx, value); }
|
|
}
|
|
|
|
private bool _aIsExt;
|
|
public bool AIsExt
|
|
{
|
|
get { return _aIsExt; }
|
|
set { SetProperty(ref _aIsExt, value); }
|
|
}
|
|
|
|
private bool _aIsRemote;
|
|
public bool AIsRemote
|
|
{
|
|
get { return _aIsRemote; }
|
|
set { SetProperty(ref _aIsRemote, value); }
|
|
}
|
|
|
|
private byte _aDlc;
|
|
public byte ADLC
|
|
{
|
|
get { return _aDlc; }
|
|
set { SetProperty(ref _aDlc, value); }
|
|
}
|
|
|
|
private byte[] _aDataArray;
|
|
public byte[] ADataArray
|
|
{
|
|
get { return _aDataArray; }
|
|
set { SetProperty(ref _aDataArray, value); }
|
|
}
|
|
|
|
private bool _aIsFD = true;
|
|
public bool AIsFD
|
|
{
|
|
get { return _aIsFD; }
|
|
set { SetProperty(ref _aIsFD, value); }
|
|
}
|
|
|
|
private bool _aIsBRS = false;
|
|
public bool AIsBRS
|
|
{
|
|
get { return _aIsBRS; }
|
|
set { SetProperty(ref _aIsBRS, value); }
|
|
}
|
|
|
|
private bool _AutoSetting = true;
|
|
public bool AutoSetting
|
|
{
|
|
get { return _AutoSetting; }
|
|
set { SetProperty(ref _AutoSetting, value); }
|
|
}
|
|
|
|
private string _CustomMessage = "[00,11,22,33,AA,BB,CC,DD]";
|
|
public string CustomMessage
|
|
{
|
|
get { return _CustomMessage; }
|
|
set { SetProperty(ref _CustomMessage, value); }
|
|
}
|
|
|
|
private float _CustomSendCycleInterval = 0.0f;
|
|
public float CustomSendCycleInterval
|
|
{
|
|
get { return _CustomSendCycleInterval; }
|
|
set { SetProperty(ref _CustomSendCycleInterval, value); }
|
|
}
|
|
private bool _UpdateToDB;
|
|
public bool UpdateToDB
|
|
{
|
|
get { return _UpdateToDB; }
|
|
set { SetProperty(ref _UpdateToDB, value); }
|
|
}
|
|
#endregion
|
|
#region 命令
|
|
public ICommand SelectBLFPathCommand { get; set; }
|
|
public ICommand SaveBLFPathCommand { get; set; }
|
|
public ICommand StartTranscirbeCommand { get; set; }
|
|
public ICommand EndTranscirbeCommand { get; set; }
|
|
public ICommand CloseCommand { 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 CheckDBCCommand { get; set; }
|
|
public ICommand InitTSMasterCommand { get; set; }
|
|
public ICommand ReleaseTSMasterCommand { get; set; }
|
|
public ICommand LoadMessageCommand { get; set; }
|
|
public ICommand SetAndSendMessageCommand { get; set; }
|
|
public ICommand DeleteDcAutoLoadCommand { get; set; }
|
|
public ICommand SendCustomMessageCommand { get; set; }
|
|
|
|
#endregion
|
|
private IEventAggregator _eventAggregator { get; set; }
|
|
private CAN? _canfd;
|
|
private SystemConfig? _systemConfig;
|
|
private readonly DeviceManager _deviceManager;
|
|
private IDialogService _dialogService { get; set; }
|
|
CancellationTokenSource cts = new CancellationTokenSource();
|
|
public CANViewModel(IContainerProvider containerProvider)
|
|
{
|
|
_eventAggregator = containerProvider.Resolve<IEventAggregator>();
|
|
_systemConfig = containerProvider.Resolve<SystemConfig>();
|
|
_deviceManager = containerProvider.Resolve<DeviceManager>();
|
|
_canfd = _deviceManager.CANFD;
|
|
_dialogService = containerProvider.Resolve<IDialogService>();
|
|
SelectBLFPathCommand = new DelegateCommand(SelectBLFPath);
|
|
SaveBLFPathCommand = new DelegateCommand(SaveBLFPath);
|
|
StartTranscirbeCommand = new DelegateCommand(StartTranscirbe);
|
|
EndTranscirbeCommand = new DelegateCommand(EndTranscirbe);
|
|
CloseCommand = new DelegateCommand(Close);
|
|
ConnectCanCommand = new DelegateCommand(ConnectCan);
|
|
CloseCanCommand = new DelegateCommand(CloseCan);
|
|
ClearCycleCommand = new DelegateCommand(ClearCycle);
|
|
UpLoadDBCCommand = new DelegateCommand(UpLoadDBC);
|
|
ChangeDBCCommand = new DelegateCommand(ChangeDBC);
|
|
LoadDBCCommand = new DelegateCommand(LoadDBC);
|
|
CheckDBCCommand = new DelegateCommand(CheckDBC);
|
|
InitTSMasterCommand = new DelegateCommand(InitTSMaster);
|
|
ReleaseTSMasterCommand = new DelegateCommand(ReleaseTSMaster);
|
|
SetAndSendMessageCommand = new DelegateCommand(SetAndSendMessage);
|
|
LoadMessageCommand = new DelegateCommand(LoadMessage);
|
|
DeleteDcAutoLoadCommand = new DelegateCommand(DeleteDcAutoLoad);
|
|
SendCustomMessageCommand = new DelegateCommand(SendCustomMessage);
|
|
}
|
|
|
|
#region 命令
|
|
private void SendCustomMessage()
|
|
{
|
|
try
|
|
{
|
|
ADataArray = 报文转换(CustomMessage);
|
|
if (ADataArray.Length > byte.MaxValue) throw new Exception("报文数量超过上限!");
|
|
if (AutoSetting)
|
|
{
|
|
ADLC = (byte)ADataArray.Length;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_dialogService.ShowDialog("MessageDialog", new DialogParameters
|
|
{
|
|
{ "message", $"报文转换失败:{ex.Message}" }
|
|
});
|
|
return;
|
|
}
|
|
var re = CAN.SendMsg(AIdxChn, AID, AIsTx, AIsExt,
|
|
AIsRemote, ADLC, ADataArray, AIsFD, AIsBRS, UpdateToDB, CustomSendCycleInterval);
|
|
}
|
|
private void DeleteDcAutoLoad()
|
|
{
|
|
DBCAutoLoadList.Remove(SelectedDcAutoLoad);
|
|
ConfigService.Save(_systemConfig);
|
|
}
|
|
private void LoadMessage()
|
|
{
|
|
if (int.TryParse(MessageSendChannel, out int channel))
|
|
{
|
|
Message = new(DBCParse.MsgDatabase[channel].Select(s => $"[0x{s.msg_id:X}]{s.msg_name}"));
|
|
}
|
|
else
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
private void SetAndSendMessage()
|
|
{
|
|
if (int.TryParse(CycleSendInterval, out int sendPeriod) && double.TryParse(SignalValue, out double SValue) && byte.TryParse(MessageSendChannel, out byte c))
|
|
{
|
|
channel = c;
|
|
var message = SelectedMessage.Split(']')[1];
|
|
var re = CAN.SetSignalValue(channel, message, SelectedSignal, SValue, IsDirectlySend, sendPeriod);
|
|
}
|
|
else
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
private void ReleaseTSMaster()
|
|
{
|
|
CAN.Release();
|
|
}
|
|
|
|
private void InitTSMaster()
|
|
{
|
|
var re = CAN.Init("ACP");
|
|
}
|
|
private void UpLoadDBC()
|
|
{
|
|
var re = CAN.UnLoadDBC();
|
|
}
|
|
|
|
private void ChangeDBC()
|
|
{
|
|
var dialog = new OpenFileDialog
|
|
{
|
|
Title = "更换DBC文件",
|
|
Filter = "DBC 文件 (*.dbc)|*.dbc|所有文件 (*.*)|*.*",
|
|
DefaultExt = ".dbc",
|
|
};
|
|
if (dialog.ShowDialog() == true)
|
|
{
|
|
DBCPath = Path.GetFullPath(dialog.FileName);
|
|
ConfigService.Save(_systemConfig);
|
|
}
|
|
}
|
|
|
|
private void LoadDBC()
|
|
{
|
|
if (int.TryParse(DBCChannel, out var channel))
|
|
{
|
|
var re = CAN.LoadDBC(DBCPath, [channel], out _);
|
|
if (re == 0 && IsSaved)
|
|
{
|
|
var item = DBCAutoLoadList.Where(s => s.DBCChannel == channel).FirstOrDefault();
|
|
if (item == null)
|
|
{
|
|
DBCAutoLoadList.Add(new AutoDBCLoadItem
|
|
{
|
|
DBCFilePath = DBCPath,
|
|
DBCChannel = channel
|
|
});
|
|
}
|
|
else
|
|
{
|
|
DBCAutoLoadList.Remove(item);
|
|
DBCAutoLoadList.Add(new AutoDBCLoadItem
|
|
{
|
|
DBCFilePath = DBCPath,
|
|
DBCChannel = channel
|
|
});
|
|
}
|
|
ConfigService.Save(_systemConfig);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
private void CheckDBC()
|
|
{
|
|
if (int.TryParse(DBCChannel, out var channel))
|
|
{
|
|
_dialogService.ShowDialog("DBCInfo", new DialogParameters
|
|
{
|
|
{ "can_Network", DBCParse.can_Network },
|
|
{ "MsgDatabase", DBCParse.MsgDatabase[channel] }
|
|
});
|
|
}
|
|
|
|
}
|
|
private void ClearCycle()
|
|
{
|
|
var re = CAN.DeleteCyclicMsgs();
|
|
}
|
|
|
|
private void CloseCan()
|
|
{
|
|
var re = CAN.DisConnect();
|
|
}
|
|
|
|
private void ConnectCan()
|
|
{
|
|
var re = CAN.Connect();
|
|
}
|
|
private void EndTranscirbe()
|
|
{
|
|
var re = CAN.StopLogging();
|
|
}
|
|
|
|
private void StartTranscirbe()
|
|
{
|
|
var re = CAN.StartLogging(BLFPath);
|
|
}
|
|
|
|
private void SaveBLFPath()
|
|
{
|
|
_systemConfig.DefaultBLFFilePath = BLFPath;
|
|
ConfigService.Save(_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 = Path.GetFullPath(dialog.FileName);
|
|
}
|
|
}
|
|
private void Close()
|
|
{
|
|
RequestClose.Invoke();
|
|
}
|
|
#endregion
|
|
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 == false)
|
|
{
|
|
var temp = CAN.RealTimeMessages.Select(s => s.Value).ToArray();
|
|
foreach (var item in temp)
|
|
{
|
|
var find = CanMessageList.FirstOrDefault(s => s.报文ID == item.FIdentifier);
|
|
if (find != null)
|
|
{
|
|
if (find.通道 != item.FIdxChn) find.通道 = item.FIdxChn;
|
|
if (find.报文ID != item.FIdentifier) find.报文ID = item.FIdentifier;
|
|
if (find.时间戳 != item.FTimeUS) find.时间戳 = item.FTimeUS;
|
|
if (find.FD != item.FIsFD) find.FD = item.FIsFD;
|
|
if (find.IsTx != item.FIsTx) find.IsTx = item.FIsTx;
|
|
|
|
var 长度改变 = find.长度 == item.FDLC;
|
|
var 数据改变 = find.Bytes.SequenceEqual(item.FData);
|
|
if (长度改变 || 数据改变)
|
|
{
|
|
find.长度 = item.FDLC;
|
|
Array.Copy(item.FData, 0, find.Bytes, 0, 64);
|
|
find.报文 = 十六进制字符串转换(item.FData.AsSpan(0, find.长度));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var 报文temp = new CanMessageShowVM()
|
|
{
|
|
通道 = item.FIdxChn,
|
|
报文ID = item.FIdentifier,
|
|
时间戳 = item.FTimeUS,
|
|
FD = item.FIsFD,
|
|
IsTx = item.FIsTx,
|
|
长度 = item.FDLC,
|
|
报文 = 十六进制字符串转换(item.FData.AsSpan(0, item.FDLC)),
|
|
};
|
|
Array.Copy(item.FData, 0, 报文temp.Bytes, 0, 64);
|
|
Application.Current.Dispatcher.Invoke(() =>
|
|
{
|
|
CanMessageList.Add(报文temp);
|
|
});
|
|
}
|
|
|
|
}
|
|
await Task.Delay(50, ct);
|
|
}
|
|
}
|
|
string 十六进制字符串转换(Span<byte> 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();
|
|
}
|
|
#region dialog规范
|
|
public DialogCloseListener RequestClose { get; set; }
|
|
|
|
public bool CanCloseDialog()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public void OnDialogClosed()
|
|
{
|
|
cts.Cancel();
|
|
}
|
|
|
|
public void OnDialogOpened(IDialogParameters parameters)
|
|
{
|
|
Task.Run(() => Refresh(cts.Token), cts.Token);
|
|
}
|
|
|
|
|
|
#endregion
|
|
}
|
|
}
|