周立功换同星
This commit is contained in:
12
ACP.sln
12
ACP.sln
@@ -35,8 +35,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonitorModule", "MonitorMod
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeviceEditModule", "DeviceEditModule\DeviceEditModule.csproj", "{170AD4C1-189D-4FBE-B10D-2A4304527834}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeviceEditModule", "DeviceEditModule\DeviceEditModule.csproj", "{170AD4C1-189D-4FBE-B10D-2A4304527834}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZLGUSBCANFD", "ZLGUSBCANFD\ZLGUSBCANFD.csproj", "{542FE380-2344-4343-9AC6-F0C587C13B88}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CANModule", "CANModule\CANModule.csproj", "{AF2533DD-599D-49D7-942B-B0C26534D15F}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CANModule", "CANModule\CANModule.csproj", "{AF2533DD-599D-49D7-942B-B0C26534D15F}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "加密狗", "加密狗\加密狗.csproj", "{A35CA316-7B5B-4183-8636-4ECA532B1489}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "加密狗", "加密狗\加密狗.csproj", "{A35CA316-7B5B-4183-8636-4ECA532B1489}"
|
||||||
@@ -47,6 +45,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExportModule", "ExportModul
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ACP", "ACP\ACP.csproj", "{CC810066-06A3-4635-96C7-1D30D78C2130}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ACP", "ACP\ACP.csproj", "{CC810066-06A3-4635-96C7-1D30D78C2130}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TSMasterCAN", "TSMasterCAN\TSMasterCAN.csproj", "{B45DC94C-2CBB-4119-8787-B52A9E7EC58B}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -113,10 +113,6 @@ Global
|
|||||||
{170AD4C1-189D-4FBE-B10D-2A4304527834}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{170AD4C1-189D-4FBE-B10D-2A4304527834}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{170AD4C1-189D-4FBE-B10D-2A4304527834}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{170AD4C1-189D-4FBE-B10D-2A4304527834}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{170AD4C1-189D-4FBE-B10D-2A4304527834}.Release|Any CPU.Build.0 = Release|Any CPU
|
{170AD4C1-189D-4FBE-B10D-2A4304527834}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{542FE380-2344-4343-9AC6-F0C587C13B88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{542FE380-2344-4343-9AC6-F0C587C13B88}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{542FE380-2344-4343-9AC6-F0C587C13B88}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{542FE380-2344-4343-9AC6-F0C587C13B88}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{AF2533DD-599D-49D7-942B-B0C26534D15F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{AF2533DD-599D-49D7-942B-B0C26534D15F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{AF2533DD-599D-49D7-942B-B0C26534D15F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{AF2533DD-599D-49D7-942B-B0C26534D15F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{AF2533DD-599D-49D7-942B-B0C26534D15F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{AF2533DD-599D-49D7-942B-B0C26534D15F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
@@ -137,6 +133,10 @@ Global
|
|||||||
{CC810066-06A3-4635-96C7-1D30D78C2130}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{CC810066-06A3-4635-96C7-1D30D78C2130}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{CC810066-06A3-4635-96C7-1D30D78C2130}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{CC810066-06A3-4635-96C7-1D30D78C2130}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{CC810066-06A3-4635-96C7-1D30D78C2130}.Release|Any CPU.Build.0 = Release|Any CPU
|
{CC810066-06A3-4635-96C7-1D30D78C2130}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B45DC94C-2CBB-4119-8787-B52A9E7EC58B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B45DC94C-2CBB-4119-8787-B52A9E7EC58B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B45DC94C-2CBB-4119-8787-B52A9E7EC58B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B45DC94C-2CBB-4119-8787-B52A9E7EC58B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
@@ -31,9 +31,9 @@
|
|||||||
<ProjectReference Include="..\Service\Service.csproj" />
|
<ProjectReference Include="..\Service\Service.csproj" />
|
||||||
<ProjectReference Include="..\SettingModule\SettingModule.csproj" />
|
<ProjectReference Include="..\SettingModule\SettingModule.csproj" />
|
||||||
<ProjectReference Include="..\TestingModule\TestingModule.csproj" />
|
<ProjectReference Include="..\TestingModule\TestingModule.csproj" />
|
||||||
|
<ProjectReference Include="..\TSMasterCAN\TSMasterCAN.csproj" />
|
||||||
<ProjectReference Include="..\UIShare\UIShare.csproj" />
|
<ProjectReference Include="..\UIShare\UIShare.csproj" />
|
||||||
<ProjectReference Include="..\UpdateInfoMoudle\UpdateInfoMoudle.csproj" />
|
<ProjectReference Include="..\UpdateInfoMoudle\UpdateInfoMoudle.csproj" />
|
||||||
<ProjectReference Include="..\ZLGUSBCANFD\ZLGUSBCANFD.csproj" />
|
|
||||||
<ProjectReference Include="..\加密狗\加密狗.csproj" />
|
<ProjectReference Include="..\加密狗\加密狗.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace CANModule
|
|||||||
{
|
{
|
||||||
containerRegistry.RegisterDialog<SelectCanMessageView>("SelectCanMessageView");
|
containerRegistry.RegisterDialog<SelectCanMessageView>("SelectCanMessageView");
|
||||||
containerRegistry.RegisterDialog<CollectCANSignalSettingView>("CollectCANSignalSettingView");
|
containerRegistry.RegisterDialog<CollectCANSignalSettingView>("CollectCANSignalSettingView");
|
||||||
containerRegistry.RegisterForNavigation<ZLGCANFDView>("ZLGCANFDView");
|
containerRegistry.RegisterForNavigation<CANView>("CANView");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0-windows</TargetFramework>
|
<TargetFramework>net8.0-windows</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\UIShare\UIShare.csproj" />
|
<ProjectReference Include="..\TSMasterCAN\TSMasterCAN.csproj" />
|
||||||
</ItemGroup>
|
<ProjectReference Include="..\UIShare\UIShare.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Interop.TSMasterAPI">
|
||||||
|
<HintPath>..\TSMasterCAN\Interop.TSMasterAPI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
581
CANModule/ViewModels/CANViewModel.cs
Normal file
581
CANModule/ViewModels/CANViewModel.cs
Normal file
@@ -0,0 +1,581 @@
|
|||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,30 +1,41 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Configuration;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
using Model;
|
||||||
|
using TSMasterCAN;
|
||||||
using UIShare.GlobalVariable;
|
using UIShare.GlobalVariable;
|
||||||
using UIShare.PubEvent;
|
using UIShare.PubEvent;
|
||||||
using UIShare.UIViewModel;
|
using UIShare.UIViewModel;
|
||||||
using UIShare.ViewModelBase;
|
|
||||||
using ZLGUSBCANFD;
|
|
||||||
|
|
||||||
namespace CANModule.ViewModels
|
namespace CANModule.ViewModels
|
||||||
{
|
{
|
||||||
public class CollectCANSignalSettingViewModel : DialogViewModelBase
|
public class CollectCANSignalSettingViewModel : BindableBase, IDialogAware
|
||||||
{
|
{
|
||||||
#region 字段
|
|
||||||
private ZLGCANFD? _canfd;
|
|
||||||
private SystemConfig? _systemConfig;
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 属性
|
#region 属性
|
||||||
public ObservableCollection<CANSignalConfig> ConfigurationList
|
public ObservableCollection<CANSignalConfig> ConfigurationList
|
||||||
{
|
{
|
||||||
get => _systemConfig?.ConfigurationList ?? new ObservableCollection<CANSignalConfig>();
|
get => _systemConfig.ConfigurationList;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if(_systemConfig.ConfigurationList != value)
|
||||||
|
{
|
||||||
|
_systemConfig.ConfigurationList = value;
|
||||||
|
RaisePropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ObservableCollection<int> _ChannelList;
|
||||||
|
public ObservableCollection<int> ChannelList
|
||||||
|
{
|
||||||
|
get => _ChannelList;
|
||||||
|
set => SetProperty(ref _ChannelList, value);
|
||||||
|
}
|
||||||
private ObservableCollection<string> _messageList;
|
private ObservableCollection<string> _messageList;
|
||||||
public ObservableCollection<string> MessageList
|
public ObservableCollection<string> MessageList
|
||||||
{
|
{
|
||||||
@@ -39,42 +50,23 @@ namespace CANModule.ViewModels
|
|||||||
set => SetProperty(ref _signalList, value);
|
set => SetProperty(ref _signalList, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string _title = "信号采集设置";
|
private string _title= "信号采集设置";
|
||||||
public string Title
|
public string Title
|
||||||
{
|
{
|
||||||
get => _title;
|
get => _title;
|
||||||
set => SetProperty(ref _title, value);
|
set => SetProperty(ref _title, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string _selectedMessage;
|
private string _selectedMessage;
|
||||||
public string SelectedMessage
|
public string SelectedMessage
|
||||||
{
|
{
|
||||||
get => _selectedMessage;
|
get => _selectedMessage;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (SetProperty(ref _selectedMessage, value))
|
if (SetProperty(ref _selectedMessage,value))
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(value) && _canfd != null)
|
if (value != null && value != "")
|
||||||
{
|
{
|
||||||
var db = _canfd.DBCParser.MsgDatabase;
|
SignalList = new(DBCParse.MsgDatabase[MessageChannel].Where(x => $"[0x{x.msg_id:X}]{x.msg_name}" == value).First().signal_Name);
|
||||||
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<string>(targetMsg.signal_Name);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SignalList = new ObservableCollection<string>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SignalList = new ObservableCollection<string>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,7 +78,6 @@ namespace CANModule.ViewModels
|
|||||||
get => _SelectedItem;
|
get => _SelectedItem;
|
||||||
set => SetProperty(ref _SelectedItem, value);
|
set => SetProperty(ref _SelectedItem, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string _selectedSignal;
|
private string _selectedSignal;
|
||||||
public string SelectedSignal
|
public string SelectedSignal
|
||||||
{
|
{
|
||||||
@@ -94,7 +85,7 @@ namespace CANModule.ViewModels
|
|||||||
set => SetProperty(ref _selectedSignal, value);
|
set => SetProperty(ref _selectedSignal, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int _MessageChannel=-1;
|
private int _MessageChannel;
|
||||||
public int MessageChannel
|
public int MessageChannel
|
||||||
{
|
{
|
||||||
get => _MessageChannel;
|
get => _MessageChannel;
|
||||||
@@ -103,127 +94,91 @@ namespace CANModule.ViewModels
|
|||||||
if (_MessageChannel != value)
|
if (_MessageChannel != value)
|
||||||
{
|
{
|
||||||
_MessageChannel = value;
|
_MessageChannel = value;
|
||||||
|
MessageList = new(DBCParse.MsgDatabase[value].Select(s => $"[0x{s.msg_id:X}]{s.msg_name}"));
|
||||||
|
SignalList = new();
|
||||||
SelectedMessage = null;
|
SelectedMessage = null;
|
||||||
SelectedSignal = null;
|
SelectedSignal = null;
|
||||||
SignalList = new ObservableCollection<string>();
|
|
||||||
|
|
||||||
if (_canfd != null)
|
|
||||||
{
|
|
||||||
var db = _canfd.DBCParser.MsgDatabase;
|
|
||||||
if (db != null && value >= 0 && value < db.Count)
|
|
||||||
{
|
|
||||||
MessageList = new ObservableCollection<string>(
|
|
||||||
db[value].Select(s => $"[0x{s.msg_id:X}]{s.msg_name}")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MessageList = new ObservableCollection<string>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MessageList = new ObservableCollection<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
RaisePropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int _collectionInterval = 1000;
|
private int _collectionInterval=1000;
|
||||||
public int CollectionInterval
|
public int CollectionInterval
|
||||||
{
|
{
|
||||||
get => _collectionInterval;
|
get => _collectionInterval;
|
||||||
set => SetProperty(ref _collectionInterval, value);
|
set => SetProperty(ref _collectionInterval, value);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion Properties
|
||||||
|
|
||||||
#region 命令
|
|
||||||
public ICommand AddCommand { get; set; }
|
public ICommand AddCommand { get; set; }
|
||||||
public ICommand CloseCommand { get; set; }
|
public ICommand CloseCommand { get; set; }
|
||||||
public ICommand SaveCommand { get; set; }
|
public ICommand LoadCommand { get; set; }
|
||||||
public ICommand DeleteCommand { get; set; }
|
public ICommand SaveCommand { get; set; }
|
||||||
#endregion
|
public ICommand DeleteCommand { get; set; }
|
||||||
|
public DialogCloseListener RequestClose { get; set; }
|
||||||
public CollectCANSignalSettingViewModel(IContainerProvider containerProvider) : base(containerProvider)
|
public IEventAggregator _eventAggregator { get; set; }
|
||||||
|
private SystemConfig _systemConfig;
|
||||||
|
public CollectCANSignalSettingViewModel(IContainerProvider containerProvider)
|
||||||
{
|
{
|
||||||
|
_eventAggregator=containerProvider.Resolve<IEventAggregator>();
|
||||||
|
_systemConfig= containerProvider.Resolve<SystemConfig>();
|
||||||
CloseCommand = new DelegateCommand(Close);
|
CloseCommand = new DelegateCommand(Close);
|
||||||
AddCommand = new DelegateCommand(Add);
|
AddCommand = new DelegateCommand(Add);
|
||||||
|
LoadCommand = new DelegateCommand(Load);
|
||||||
SaveCommand = new DelegateCommand(Save);
|
SaveCommand = new DelegateCommand(Save);
|
||||||
DeleteCommand = new DelegateCommand(Delete);
|
DeleteCommand = new DelegateCommand(Delete);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Dialog 生命周期
|
|
||||||
public override void OnDialogOpened(IDialogParameters parameters)
|
|
||||||
{
|
|
||||||
_systemConfig = parameters.GetValue<SystemConfig>("SystemConfig");
|
|
||||||
_canfd = _systemConfig?.CANFD;
|
|
||||||
string title = _systemConfig.Title;
|
|
||||||
char lastChar = title[title.Length - 1];
|
|
||||||
// 3. "TestCellX"转int
|
|
||||||
MessageChannel = int.Parse(lastChar.ToString())-1;
|
|
||||||
if (MessageChannel >= 4) MessageChannel -= 4;
|
|
||||||
RaisePropertyChanged(nameof(ConfigurationList));
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 命令处理
|
|
||||||
private void Delete()
|
private void Delete()
|
||||||
{
|
{
|
||||||
if (SelectedItem != null)
|
ConfigurationList.Remove(SelectedItem);
|
||||||
{
|
|
||||||
_systemConfig?.ConfigurationList.Remove(SelectedItem);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Save()
|
private void Save()
|
||||||
{
|
{
|
||||||
|
_eventAggregator.GetEvent<CollectedCANMessageChangedEvent>().Publish();
|
||||||
ConfigService.Save(_systemConfig);
|
ConfigService.Save(_systemConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Load()
|
||||||
|
{
|
||||||
|
ChannelList = new ObservableCollection<int>(Enumerable.Range(0, 12));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void Add()
|
private void Add()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(SelectedSignal) || string.IsNullOrEmpty(SelectedMessage) || CollectionInterval < 100)
|
if (string.IsNullOrEmpty(SelectedSignal) || string.IsNullOrEmpty(SelectedMessage)||CollectionInterval<100)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_systemConfig == null) return;
|
|
||||||
|
|
||||||
string hexString = ExtractHex(SelectedMessage);
|
string hexString = ExtractHex(SelectedMessage);
|
||||||
if (string.IsNullOrEmpty(hexString)) return;
|
if (string.IsNullOrEmpty(hexString))
|
||||||
|
|
||||||
int decimalValue = ConvertHexToDecimal(hexString);
|
|
||||||
if (_systemConfig.ConfigurationList.Any(x => x.SignalName == SelectedSignal && x.Channel == MessageChannel))
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
int decimalValue = ConvertHexToDecimal(hexString);
|
||||||
_systemConfig.ConfigurationList.Add(new CANSignalConfig
|
if (ConfigurationList.Where(x => x.SignalName == SelectedSignal&&x.Channel==MessageChannel).FirstOrDefault() != null) return;
|
||||||
|
ConfigurationList.Add(new CANSignalConfig
|
||||||
{
|
{
|
||||||
CollectionID = Guid.NewGuid(),
|
CollectionID = Guid.NewGuid(),
|
||||||
Channel = MessageChannel,
|
Channel = MessageChannel,
|
||||||
MethodName = CANSignalBroadcaster.BuildMethodName((uint)decimalValue, SelectedSignal),
|
|
||||||
MessageName = ExtractMessageName(SelectedMessage),
|
MessageName = ExtractMessageName(SelectedMessage),
|
||||||
SignalName = SelectedSignal,
|
SignalName = SelectedSignal,
|
||||||
MessageID = decimalValue,
|
MessageID = decimalValue,
|
||||||
CollectionInterval = CollectionInterval
|
CollectionInterval = CollectionInterval
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ExtractHex(string input)
|
private string ExtractHex(string input)
|
||||||
{
|
{
|
||||||
int startIndex = input.IndexOf("0x") + 2;
|
int startIndex = input.IndexOf("0x") + 2;
|
||||||
int endIndex = input.IndexOf("]", startIndex);
|
int endIndex = input.IndexOf("]", startIndex);
|
||||||
if (startIndex >= 2 && endIndex > startIndex)
|
if (startIndex >= 2 && endIndex > startIndex)
|
||||||
{
|
{
|
||||||
return input.Substring(startIndex, endIndex - startIndex);
|
return input.Substring(startIndex, endIndex - startIndex);
|
||||||
}
|
}
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int ConvertHexToDecimal(string hex)
|
private int ConvertHexToDecimal(string hex)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -232,20 +187,37 @@ namespace CANModule.ViewModels
|
|||||||
}
|
}
|
||||||
catch (FormatException)
|
catch (FormatException)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ExtractMessageName(string input)
|
private string ExtractMessageName(string input)
|
||||||
{
|
{
|
||||||
var parts = input.Split(']');
|
var parts = input.Split(']');
|
||||||
return parts.Length > 1 ? parts[1] : string.Empty;
|
return parts.Length > 1 ? parts[1] : string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Close()
|
private void Close()
|
||||||
{
|
{
|
||||||
RequestClose.Invoke();
|
RequestClose.Invoke();
|
||||||
}
|
}
|
||||||
#endregion
|
|
||||||
|
public bool CanCloseDialog()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDialogClosed()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDialogOpened(IDialogParameters parameters)
|
||||||
|
{
|
||||||
|
if (DBCParse.MsgDatabase.Count == 0 || DBCParse.MsgDatabase[0].Count==0)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
MessageList = new(DBCParse.MsgDatabase[0].Select(s => $"[0x{s.msg_id:X}]{s.msg_name}"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,26 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Channels;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using UIShare.ViewModelBase;
|
using TSMasterCAN;
|
||||||
using ZLGUSBCANFD;
|
using UIShare.UIViewModel;
|
||||||
|
|
||||||
namespace CANModule.ViewModels
|
namespace CANModule.ViewModels
|
||||||
{
|
{
|
||||||
public class SelectCanMessageViewModel : DialogViewModelBase
|
public class SelectCanMessageViewModel : BindableBase, IDialogAware
|
||||||
{
|
{
|
||||||
#region 字段
|
|
||||||
private ZLGCANFD? _canfd;
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 属性
|
#region 属性
|
||||||
public DialogCloseListener RequestClose { get; set; }
|
|
||||||
|
|
||||||
private ObservableCollection<int> _ChannelList;
|
private ObservableCollection<int> _ChannelList;
|
||||||
public ObservableCollection<int> ChannelList
|
public ObservableCollection<int> ChannelList
|
||||||
{
|
{
|
||||||
get => _ChannelList;
|
get => _ChannelList;
|
||||||
set => SetProperty(ref _ChannelList, value);
|
set => SetProperty(ref _ChannelList, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ObservableCollection<string> _messageList;
|
private ObservableCollection<string> _messageList;
|
||||||
public ObservableCollection<string> MessageList
|
public ObservableCollection<string> MessageList
|
||||||
{
|
{
|
||||||
@@ -44,7 +41,6 @@ namespace CANModule.ViewModels
|
|||||||
get => _title;
|
get => _title;
|
||||||
set => SetProperty(ref _title, value);
|
set => SetProperty(ref _title, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string _selectedMessage;
|
private string _selectedMessage;
|
||||||
public string SelectedMessage
|
public string SelectedMessage
|
||||||
{
|
{
|
||||||
@@ -53,27 +49,9 @@ namespace CANModule.ViewModels
|
|||||||
{
|
{
|
||||||
if (SetProperty(ref _selectedMessage, value))
|
if (SetProperty(ref _selectedMessage, value))
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(value) && _canfd != null)
|
if (value != null && value != "")
|
||||||
{
|
{
|
||||||
var db = _canfd.DBCParser.MsgDatabase;
|
SignalList = new(DBCParse.MsgDatabase[MessageChannel].Where(x => $"[0x{x.msg_id:X}]{x.msg_name}" == value).First().signal_Name);
|
||||||
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<string>(targetMsg.signal_Name);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SignalList = new ObservableCollection<string>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SignalList = new ObservableCollection<string>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,8 +63,7 @@ namespace CANModule.ViewModels
|
|||||||
get => _selectedSignal;
|
get => _selectedSignal;
|
||||||
set => SetProperty(ref _selectedSignal, value);
|
set => SetProperty(ref _selectedSignal, value);
|
||||||
}
|
}
|
||||||
|
private int _MessageChannel;
|
||||||
private int _MessageChannel = -1;
|
|
||||||
public int MessageChannel
|
public int MessageChannel
|
||||||
{
|
{
|
||||||
get => _MessageChannel;
|
get => _MessageChannel;
|
||||||
@@ -94,94 +71,84 @@ namespace CANModule.ViewModels
|
|||||||
{
|
{
|
||||||
if (_MessageChannel != value)
|
if (_MessageChannel != value)
|
||||||
{
|
{
|
||||||
_MessageChannel = value;
|
_MessageChannel=value;
|
||||||
|
MessageList = new(DBCParse.MsgDatabase[value].Select(s => $"[0x{s.msg_id:X}]{s.msg_name}"));
|
||||||
// 1. 先安全清空旧的选择项和信号列表
|
SignalList = new();
|
||||||
SelectedMessage = null;
|
SelectedMessage = null;
|
||||||
SelectedSignal = null;
|
SelectedSignal = null;
|
||||||
SignalList = new ObservableCollection<string>();
|
|
||||||
|
|
||||||
// 2. 刷新当前通道的报文列表
|
|
||||||
if (_canfd != null)
|
|
||||||
{
|
|
||||||
var db = _canfd.DBCParser.MsgDatabase;
|
|
||||||
if (db != null && value >= 0 && value < db.Count)
|
|
||||||
{
|
|
||||||
MessageList = new ObservableCollection<string>(
|
|
||||||
db[value].Select(s => $"[0x{s.msg_id:X}]{s.msg_name}")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MessageList = new ObservableCollection<string>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MessageList = new ObservableCollection<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
RaisePropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 命令
|
|
||||||
public ICommand CloseCommand { get; set; }
|
public ICommand CloseCommand { get; set; }
|
||||||
public ICommand GetSelectedMessageCommand { get; set; }
|
public ICommand GetSelectedMessageCommand { get; set; }
|
||||||
public ICommand GetSelectedSignalCommand { get; set; }
|
public ICommand GetSelectedSignalCommand { get; set; }
|
||||||
public ICommand LoadCommand { get; set; }
|
public ICommand LoadCommand { get; set; }
|
||||||
#endregion
|
public DialogCloseListener RequestClose { get; set; }
|
||||||
|
public SelectCanMessageViewModel()
|
||||||
public SelectCanMessageViewModel(IContainerProvider containerProvider) : base(containerProvider)
|
|
||||||
{
|
{
|
||||||
CloseCommand = new DelegateCommand(Close);
|
CloseCommand = new DelegateCommand(Close);
|
||||||
GetSelectedMessageCommand = new DelegateCommand(GetSelectedMessage);
|
GetSelectedMessageCommand = new DelegateCommand(GetSelectedMessage);
|
||||||
GetSelectedSignalCommand = new DelegateCommand(GetSelectedSignal);
|
GetSelectedSignalCommand = new DelegateCommand(GetSelectedSignal);
|
||||||
LoadCommand = new DelegateCommand(Load);
|
LoadCommand = new DelegateCommand(Load);
|
||||||
}
|
|
||||||
|
|
||||||
#region Dialog 生命周期
|
|
||||||
public override void OnDialogOpened(IDialogParameters parameters)
|
|
||||||
{
|
|
||||||
_canfd = parameters.GetValue<ZLGCANFD>("CANFD");
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 命令处理
|
|
||||||
private void Close()
|
|
||||||
{
|
|
||||||
RequestClose.Invoke();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Load()
|
private void Load()
|
||||||
{
|
{
|
||||||
int maxCh = _canfd?.DBCParser.MaxChannels ?? 4;
|
ChannelList = new ObservableCollection<int>(Enumerable.Range(0, 12));
|
||||||
ChannelList = new ObservableCollection<int>(Enumerable.Range(0, maxCh));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetSelectedSignal()
|
private void GetSelectedSignal()
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(SelectedSignal))
|
if (SelectedSignal!=null)
|
||||||
{
|
{
|
||||||
Application.Current.Dispatcher.Invoke(() =>
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
{
|
{
|
||||||
Clipboard.SetDataObject(SelectedSignal);
|
Clipboard.SetDataObject(SelectedSignal);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetSelectedMessage()
|
private void GetSelectedMessage()
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(SelectedMessage) && SelectedMessage.Contains(']'))
|
if (SelectedSignal != null)
|
||||||
{
|
{
|
||||||
Application.Current.Dispatcher.Invoke(() =>
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
{
|
{
|
||||||
Clipboard.SetDataObject(SelectedMessage);
|
Clipboard.SetDataObject(SelectedMessage.Split("]")[1]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Close()
|
||||||
|
{
|
||||||
|
RequestClose.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanCloseDialog()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDialogClosed()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDialogOpened(IDialogParameters parameters)
|
||||||
|
{
|
||||||
|
MessageList = new(DBCParse.MsgDatabase[0].Select(s => $"[0x{s.msg_id:X}]{s.msg_name}"));
|
||||||
}
|
}
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,649 +0,0 @@
|
|||||||
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<CanMessageShowVM> _CanMessageList = new();
|
|
||||||
public ObservableCollection<CanMessageShowVM> 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<string> _Message;
|
|
||||||
public ObservableCollection<string> Message
|
|
||||||
{
|
|
||||||
get => _Message;
|
|
||||||
set => SetProperty(ref _Message, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObservableCollection<string> _Signal;
|
|
||||||
public ObservableCollection<string> 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<string>(targetMsg.signal_Name);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Signal = new ObservableCollection<string>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Signal = new ObservableCollection<string>();
|
|
||||||
}
|
|
||||||
_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<AutoDBCLoadItem> DBCAutoLoadList
|
|
||||||
{
|
|
||||||
get => _systemConfig?.DBCAutoLoadList ?? new ObservableCollection<AutoDBCLoadItem>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private AutoDBCLoadItem _SelectedDcAutoLoad;
|
|
||||||
public AutoDBCLoadItem SelectedDcAutoLoad
|
|
||||||
{
|
|
||||||
get => _SelectedDcAutoLoad;
|
|
||||||
set => SetProperty(ref _SelectedDcAutoLoad, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== 自定义报文发送属性 =====
|
|
||||||
private APP_CHANNEL[] _appChannelNames = Enum.GetValues<APP_CHANNEL>();
|
|
||||||
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<int> _ChannelList;
|
|
||||||
public ObservableCollection<int> 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<SystemConfig>();
|
|
||||||
_deviceManager = containerProvider.Resolve<DeviceManager>();
|
|
||||||
_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<int>(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<string>(
|
|
||||||
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, IsDirectlySend);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.BeginInvoke(() =>
|
|
||||||
{
|
|
||||||
var msgName = System.Text.Encoding.Default.GetString(msg.strName).TrimEnd('\0');
|
|
||||||
var find = CanMessageList.FirstOrDefault(s => s.报文ID == (int)msg.nID&& s.通道==channel);
|
|
||||||
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<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();
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
<UserControl x:Class="CANModule.Views.ZLGCANFDView"
|
<UserControl x:Class="CANModule.Views.CANView"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:local="clr-namespace:CANModule.Views"
|
xmlns:oxy="http://oxyplot.org/wpf"
|
||||||
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
|
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
|
||||||
|
xmlns:local="clr-namespace:CANModule.Views"
|
||||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
xmlns:prism="http://prismlibrary.com/"
|
xmlns:prism="http://prismlibrary.com/"
|
||||||
@@ -17,16 +18,11 @@
|
|||||||
<Style BasedOn="{StaticResource DialogUserManageStyle}"
|
<Style BasedOn="{StaticResource DialogUserManageStyle}"
|
||||||
TargetType="Window" />
|
TargetType="Window" />
|
||||||
</prism:Dialog.WindowStyle>
|
</prism:Dialog.WindowStyle>
|
||||||
<i:Interaction.Triggers>
|
|
||||||
<i:EventTrigger EventName="Loaded">
|
|
||||||
<i:InvokeCommandAction Command="{Binding LoadCommand}" />
|
|
||||||
</i:EventTrigger>
|
|
||||||
</i:Interaction.Triggers>
|
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<cons:HexConverter x:Key="HexConverter" />
|
<cons:HexConverter x:Key="HexConverter" />
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
<GroupBox Padding="0,0,0,0"
|
<GroupBox Padding="0,0,0,0"
|
||||||
>
|
MouseLeftButtonDown="MouseLeftButtonDown">
|
||||||
<GroupBox.Header>
|
<GroupBox.Header>
|
||||||
<Grid Margin="0">
|
<Grid Margin="0">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
@@ -38,59 +34,46 @@
|
|||||||
Foreground="White"
|
Foreground="White"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Margin="5,0,10,0" />
|
Margin="5,0,10,0" />
|
||||||
|
<Button Content="关闭"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="150 0 0 0"
|
||||||
|
Width="60"
|
||||||
|
Command="{Binding CloseCommand}"
|
||||||
|
Height="25" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</GroupBox.Header>
|
</GroupBox.Header>
|
||||||
<Grid>
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="829*" />
|
||||||
|
<ColumnDefinition Width="69*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition />
|
<RowDefinition />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<!-- CAN卡连接 -->
|
<!-- TreeView 用于显示不同选项 -->
|
||||||
<StackPanel Orientation="Horizontal"
|
<TreeView Grid.Row="1"
|
||||||
Margin="5"
|
Grid.ColumnSpan="2">
|
||||||
Grid.Row="0">
|
<!-- 调试选项 -->
|
||||||
<Button Content="打开设备"
|
<TreeViewItem Header="调试选项">
|
||||||
Command="{Binding ConnectCanCommand}"
|
<Button Content="初始化TSMaster"
|
||||||
Margin="5" />
|
Command="{Binding InitTSMasterCommand}"
|
||||||
<Button Content="关闭设备"
|
CommandParameter="初始化TSMaster" />
|
||||||
Command="{Binding CloseCanCommand}"
|
<Button Content="释放TSMaster"
|
||||||
Margin="5" />
|
Command="{Binding ReleaseTSMasterCommand}"
|
||||||
<Button Content="清空循环发送报文"
|
CommandParameter="释放TSMaster" />
|
||||||
Command="{Binding ClearCycleCommand}"
|
|
||||||
Margin="5" />
|
</TreeViewItem>
|
||||||
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Orientation="Horizontal"
|
|
||||||
Margin="5"
|
|
||||||
Grid.Row="1">
|
|
||||||
<Label Content="选择通道初始化(选择前先打开设备)"
|
|
||||||
VerticalAlignment="Center" />
|
|
||||||
<ComboBox SelectedItem="{Binding SelectedChannel}"
|
|
||||||
ItemsSource="{Binding ChannelList}"
|
|
||||||
materialDesign:HintAssist.Hint=""
|
|
||||||
MinWidth="80"
|
|
||||||
Margin="5"
|
|
||||||
VerticalAlignment="Center" />
|
|
||||||
</StackPanel>
|
|
||||||
<TreeView Grid.Row="2">
|
|
||||||
<!-- 监视选项 -->
|
<!-- 监视选项 -->
|
||||||
<TreeViewItem Header="监视">
|
<TreeViewItem Header="监视">
|
||||||
<DataGrid x:Name="表格"
|
<DataGrid x:Name="表格" Width="800"
|
||||||
Width="550"
|
Height="450"
|
||||||
Height="350"
|
|
||||||
VirtualizingPanel.IsVirtualizing="True"
|
VirtualizingPanel.IsVirtualizing="True"
|
||||||
IsReadOnly="True"
|
IsReadOnly="True"
|
||||||
AutoGenerateColumns="False"
|
|
||||||
ItemsSource="{Binding CanMessageList, Mode=OneWay}">
|
ItemsSource="{Binding CanMessageList, Mode=OneWay}">
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTextColumn Header="通道" Binding="{Binding 通道}" Width="90" />
|
|
||||||
<DataGridTextColumn Header="报文ID" Binding="{Binding 报文ID, Converter={StaticResource HexConverter}}" Width="150" />
|
|
||||||
<DataGridTextColumn Header="长度" Binding="{Binding 长度}" Width="90" />
|
|
||||||
<DataGridTextColumn Header="时间戳" Binding="{Binding 时间戳}" Width="Auto" />
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
</TreeViewItem>
|
</TreeViewItem>
|
||||||
|
|
||||||
@@ -98,11 +81,13 @@
|
|||||||
<TreeViewItem Header="DBC">
|
<TreeViewItem Header="DBC">
|
||||||
<Label Content="请注意,要在断开CAN卡的情况下才能操作DBC!"
|
<Label Content="请注意,要在断开CAN卡的情况下才能操作DBC!"
|
||||||
Foreground="Red" />
|
Foreground="Red" />
|
||||||
<Button Content="卸载当前通道DBC"
|
<Button Content="卸载所有DBC"
|
||||||
Command="{Binding UpLoadDBCCommand}" />
|
Command="{Binding UpLoadDBCCommand}"
|
||||||
|
CommandParameter="卸载DBC" />
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<Button Content="更换DBC"
|
<Button Content="更换DBC"
|
||||||
Command="{Binding ChangeDBCCommand}" />
|
Command="{Binding ChangeDBCCommand}"
|
||||||
|
CommandParameter="更换DBC" />
|
||||||
<Label Content="DBC文件路径"
|
<Label Content="DBC文件路径"
|
||||||
VerticalContentAlignment="Center" />
|
VerticalContentAlignment="Center" />
|
||||||
<TextBox Text="{Binding DBCPath,Mode=TwoWay}"
|
<TextBox Text="{Binding DBCPath,Mode=TwoWay}"
|
||||||
@@ -112,7 +97,8 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<Button Content="加载DBC"
|
<Button Content="加载DBC"
|
||||||
Command="{Binding LoadDBCCommand}" />
|
Command="{Binding LoadDBCCommand}"
|
||||||
|
CommandParameter="加载DBC" />
|
||||||
<Label Content="通道(从0开始)"
|
<Label Content="通道(从0开始)"
|
||||||
VerticalContentAlignment="Center" />
|
VerticalContentAlignment="Center" />
|
||||||
<TextBox Text="{Binding DBCChannel,Mode=TwoWay}"
|
<TextBox Text="{Binding DBCChannel,Mode=TwoWay}"
|
||||||
@@ -145,28 +131,27 @@
|
|||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</DataGrid.ContextMenu>
|
</DataGrid.ContextMenu>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
|
<Button Content="查看DBC"
|
||||||
|
Command="{Binding CheckDBCCommand}"
|
||||||
|
CommandParameter="查看DBC" />
|
||||||
|
</TreeViewItem>
|
||||||
|
|
||||||
|
<!-- CAN卡连接选项 -->
|
||||||
|
<TreeViewItem Header="CAN卡连接">
|
||||||
|
<Button Content="连接"
|
||||||
|
Command="{Binding ConnectCanCommand}"
|
||||||
|
CommandParameter="连接" />
|
||||||
|
<Button Content="断开"
|
||||||
|
Command="{Binding CloseCanCommand}"
|
||||||
|
CommandParameter="断开" />
|
||||||
|
<Button Content="清空循环发送报文"
|
||||||
|
Command="{Binding ClearCycleCommand}"
|
||||||
|
CommandParameter="清空循环发送报文" />
|
||||||
|
|
||||||
</TreeViewItem>
|
</TreeViewItem>
|
||||||
|
|
||||||
<!-- DBC报文发送 -->
|
<!-- DBC报文发送 -->
|
||||||
<TreeViewItem Header="DBC报文发送">
|
<TreeViewItem Header="DBC报文发送">
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<Label Content="通道(从0开始)"
|
|
||||||
VerticalContentAlignment="Center" />
|
|
||||||
<ComboBox SelectedValue="{Binding MessageSendChannel}"
|
|
||||||
SelectedValuePath="Content"
|
|
||||||
materialDesign:HintAssist.Hint=""
|
|
||||||
MinWidth="60"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="0,5,5,5">
|
|
||||||
<ComboBoxItem Content="0"/>
|
|
||||||
<ComboBoxItem Content="1"/>
|
|
||||||
<ComboBoxItem Content="2"/>
|
|
||||||
<ComboBoxItem Content="3"/>
|
|
||||||
</ComboBox>
|
|
||||||
<Button Content="加载报文"
|
|
||||||
Command="{Binding LoadMessageCommand}"
|
|
||||||
Margin="5" />
|
|
||||||
</StackPanel>
|
|
||||||
<DockPanel>
|
<DockPanel>
|
||||||
<Label Content="报文" />
|
<Label Content="报文" />
|
||||||
<ComboBox MinWidth="100"
|
<ComboBox MinWidth="100"
|
||||||
@@ -194,21 +179,33 @@
|
|||||||
MinWidth="100" />
|
MinWidth="100" />
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Label Content="通道(从0开始)"
|
||||||
|
VerticalContentAlignment="Center" />
|
||||||
|
<TextBox Text="{Binding MessageSendChannel}"
|
||||||
|
materialDesign:HintAssist.Hint=""
|
||||||
|
MinWidth="100"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Margin="0,10,10,10" />
|
||||||
|
<Button Content="加载报文"
|
||||||
|
Command="{Binding LoadMessageCommand}"
|
||||||
|
CommandParameter="加载报文"
|
||||||
|
Margin="10" />
|
||||||
<Button Content="设置信号并发送"
|
<Button Content="设置信号并发送"
|
||||||
Command="{Binding SetAndSendMessageCommand}" />
|
Command="{Binding SetAndSendMessageCommand}"
|
||||||
|
CommandParameter="设置" />
|
||||||
<CheckBox Content="直接发送"
|
<CheckBox Content="直接发送"
|
||||||
IsChecked="{Binding IsDirectlySend}" />
|
IsChecked="{Binding IsDirectlySend}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</TreeViewItem>
|
</TreeViewItem>
|
||||||
|
|
||||||
<!-- 自定义报文发送 -->
|
|
||||||
<TreeViewItem Header="自定义报文发送">
|
<TreeViewItem Header="自定义报文发送">
|
||||||
<DockPanel>
|
<DockPanel>
|
||||||
<Label Content="通道" />
|
<Label Content="通道" />
|
||||||
<ComboBox MinWidth="100"
|
<ComboBox MinWidth="100"
|
||||||
materialDesign:HintAssist.Hint=""
|
materialDesign:HintAssist.Hint=""
|
||||||
SelectedItem="{Binding AIdxChn}"
|
SelectedItem="{Binding AIdxChn}"
|
||||||
ItemsSource="{Binding APP_CHANNEL}" />
|
ItemsSource="{Binding APP_CHANNEL}"
|
||||||
|
/>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
<DockPanel>
|
<DockPanel>
|
||||||
<Label Content="ID" />
|
<Label Content="ID" />
|
||||||
@@ -250,10 +247,11 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<Button Content="发送"
|
<Button Content="发送"
|
||||||
Command="{Binding SendCustomMessageCommand}" />
|
Command="{Binding SendCustomMessageCommand}"
|
||||||
<!--<CheckBox Margin="10"
|
/>
|
||||||
|
<CheckBox Margin="10"
|
||||||
Content="更新到数据库"
|
Content="更新到数据库"
|
||||||
IsChecked="{Binding UpdateToDB}" />-->
|
IsChecked="{Binding UpdateToDB}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</TreeViewItem>
|
</TreeViewItem>
|
||||||
|
|
||||||
@@ -268,16 +266,20 @@
|
|||||||
VerticalContentAlignment="Center" />
|
VerticalContentAlignment="Center" />
|
||||||
<Button Content="选择路径"
|
<Button Content="选择路径"
|
||||||
Margin="10 0"
|
Margin="10 0"
|
||||||
Command="{Binding SelectBLFPathCommand}" />
|
Command="{Binding SelectBLFPathCommand}"
|
||||||
|
CommandParameter="选择路径" />
|
||||||
<Button Content="保存路径"
|
<Button Content="保存路径"
|
||||||
Command="{Binding SaveBLFPathCommand}" />
|
Command="{Binding SaveBLFPathCommand}"
|
||||||
|
CommandParameter="保存路径" />
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<Button Content="开始录制"
|
<Button Content="开始录制"
|
||||||
Command="{Binding StartTranscribeCommand}" />
|
Command="{Binding StartTranscirbeCommand}"
|
||||||
|
CommandParameter="开始录制" />
|
||||||
<Button Content="结束录制"
|
<Button Content="结束录制"
|
||||||
Margin="10 0"
|
Margin="10 0"
|
||||||
Command="{Binding EndTranscribeCommand}" />
|
Command="{Binding EndTranscirbeCommand}"
|
||||||
|
CommandParameter="结束录制" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</TreeViewItem>
|
</TreeViewItem>
|
||||||
</TreeView>
|
</TreeView>
|
||||||
@@ -16,14 +16,15 @@ using System.Windows.Shapes;
|
|||||||
namespace CANModule.Views
|
namespace CANModule.Views
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ZLGCANFDView.xaml 的交互逻辑
|
/// CANView.xaml 的交互逻辑
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ZLGCANFDView : UserControl
|
public partial class CANView : UserControl
|
||||||
{
|
{
|
||||||
public ZLGCANFDView()
|
public CANView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
private void MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.LeftButton == MouseButtonState.Pressed)
|
if (e.LeftButton == MouseButtonState.Pressed)
|
||||||
@@ -31,5 +32,7 @@ namespace CANModule.Views
|
|||||||
Window.GetWindow(this)?.DragMove();
|
Window.GetWindow(this)?.DragMove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,23 +1,27 @@
|
|||||||
<UserControl x:Class="CANModule.Views.CollectCANSignalSettingView"
|
<UserControl x:Class="CANModule.Views.CollectCANSignalSettingView"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:local="clr-namespace:CANModule.Views"
|
xmlns:oxy="http://oxyplot.org/wpf"
|
||||||
xmlns:oxy="http://oxyplot.org/wpf"
|
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
|
||||||
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
|
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
mc:Ignorable="d"
|
||||||
mc:Ignorable="d"
|
xmlns:prism="http://prismlibrary.com/"
|
||||||
xmlns:prism="http://prismlibrary.com/"
|
xmlns:cons="clr-namespace:UIShare.Converters;assembly=UIShare"
|
||||||
xmlns:cons="clr-namespace:UIShare.Converters;assembly=UIShare"
|
Background="White"
|
||||||
Background="White"
|
prism:ViewModelLocator.AutoWireViewModel="True"
|
||||||
prism:ViewModelLocator.AutoWireViewModel="True"
|
Height="400"
|
||||||
Height="400"
|
Width="1000">
|
||||||
Width="1000">
|
|
||||||
<prism:Dialog.WindowStyle>
|
<prism:Dialog.WindowStyle>
|
||||||
<Style BasedOn="{StaticResource DialogUserManageStyle}"
|
<Style BasedOn="{StaticResource DialogUserManageStyle}"
|
||||||
TargetType="Window" />
|
TargetType="Window" />
|
||||||
</prism:Dialog.WindowStyle>
|
</prism:Dialog.WindowStyle>
|
||||||
|
<i:Interaction.Triggers>
|
||||||
|
<i:EventTrigger EventName="Loaded">
|
||||||
|
<i:InvokeCommandAction Command="{Binding LoadCommand}" />
|
||||||
|
</i:EventTrigger>
|
||||||
|
</i:Interaction.Triggers>
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<cons:HexConverter x:Key="HexConverter" />
|
<cons:HexConverter x:Key="HexConverter" />
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
@@ -79,11 +83,12 @@
|
|||||||
Margin="10">
|
Margin="10">
|
||||||
<Label Content="通道(从0开始)"
|
<Label Content="通道(从0开始)"
|
||||||
VerticalAlignment="Center" />
|
VerticalAlignment="Center" />
|
||||||
<TextBox Name="SignalComboBox"
|
<ComboBox Name="SignalComboBox"
|
||||||
Text="{Binding MessageChannel}"
|
SelectedItem="{Binding MessageChannel,Mode=TwoWay}"
|
||||||
MaxWidth="200"
|
MaxWidth="200"
|
||||||
MinWidth="120"
|
MinWidth="120"
|
||||||
Margin="10"
|
Margin="10"
|
||||||
|
ItemsSource="{Binding ChannelList}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
materialDesign:HintAssist.Hint="信号通道" />
|
materialDesign:HintAssist.Hint="信号通道" />
|
||||||
<Label Content="报文"
|
<Label Content="报文"
|
||||||
@@ -1,11 +1,22 @@
|
|||||||
using System.Windows;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Documents;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
using System.Windows.Navigation;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
|
||||||
namespace CANModule.Views
|
namespace CANModule.Views
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// CollectCANSignalSettingView.xaml 的交互逻辑
|
/// CollectCANSignalSetting.xaml 的交互逻辑
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class CollectCANSignalSettingView : UserControl
|
public partial class CollectCANSignalSettingView : UserControl
|
||||||
{
|
{
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
<UserControl x:Class="CANModule.Views.SelectCanMessageView"
|
<UserControl x:Class="CANModule.Views.SelectCanMessageView"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:local="clr-namespace:CANModule.Views"
|
|
||||||
xmlns:oxy="http://oxyplot.org/wpf"
|
xmlns:oxy="http://oxyplot.org/wpf"
|
||||||
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
|
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
|
||||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
@@ -44,26 +43,26 @@
|
|||||||
Height="25" />
|
Height="25" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</GroupBox.Header>
|
</GroupBox.Header>
|
||||||
<Grid>
|
<Grid>
|
||||||
<StackPanel Grid.Row="1" Margin="10">
|
<StackPanel Grid.Row="1" Margin="10">
|
||||||
<Label Content="通道" VerticalAlignment="Center"/>
|
<Label Content="通道" VerticalAlignment="Center"/>
|
||||||
<ComboBox ItemsSource="{Binding ChannelList}"
|
<ComboBox ItemsSource="{Binding ChannelList}"
|
||||||
SelectedItem="{Binding MessageChannel,Mode=TwoWay}"
|
SelectedItem="{Binding MessageChannel,Mode=TwoWay}"
|
||||||
MinWidth="100"
|
MinWidth="100"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
materialDesign:HintAssist.Hint="" />
|
materialDesign:HintAssist.Hint="" />
|
||||||
<Label Content="报文" VerticalAlignment="Center"/>
|
<Label Content="报文" VerticalAlignment="Center"/>
|
||||||
<ComboBox materialDesign:HintAssist.Hint=""
|
<ComboBox materialDesign:HintAssist.Hint=""
|
||||||
MinWidth="100"
|
MinWidth="100"
|
||||||
SelectedItem="{Binding SelectedMessage,Mode=TwoWay}"
|
SelectedItem="{Binding SelectedMessage,Mode=TwoWay}"
|
||||||
ItemsSource="{Binding MessageList}" />
|
ItemsSource="{Binding MessageList}" />
|
||||||
<Label Content="信号" VerticalAlignment="Center"/>
|
<Label Content="信号" VerticalAlignment="Center"/>
|
||||||
<ComboBox materialDesign:HintAssist.Hint=""
|
<ComboBox materialDesign:HintAssist.Hint=""
|
||||||
SelectedItem="{Binding SelectedSignal,Mode=TwoWay}"
|
SelectedItem="{Binding SelectedSignal,Mode=TwoWay}"
|
||||||
ItemsSource="{Binding SignalList}" MinWidth="100" />
|
ItemsSource="{Binding SignalList}" MinWidth="100" />
|
||||||
<Button Content="选择报文名" Margin="10" Command="{Binding GetSelectedMessageCommand}"/>
|
<Button Content="选择报文名" Margin="10" Command="{Binding GetSelectedMessageCommand}"/>
|
||||||
<Button Content="选择信号名" Margin="10" Command="{Binding GetSelectedSignalCommand}"/>
|
<Button Content="选择信号名" Margin="10" Command="{Binding GetSelectedSignalCommand}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ using System.Collections.ObjectModel;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
using TSMasterCAN;
|
||||||
using UIShare.GlobalVariable;
|
using UIShare.GlobalVariable;
|
||||||
using UIShare.PubEvent;
|
using UIShare.PubEvent;
|
||||||
using UIShare.UIViewModel;
|
using UIShare.UIViewModel;
|
||||||
using UIShare.ViewModelBase;
|
using UIShare.ViewModelBase;
|
||||||
using ZLGUSBCANFD;
|
|
||||||
|
|
||||||
namespace MonitorModule.ViewModels.Dialogs
|
namespace MonitorModule.ViewModels.Dialogs
|
||||||
{
|
{
|
||||||
@@ -197,10 +197,10 @@ namespace MonitorModule.ViewModels.Dialogs
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private IEnumerable<(string DisplayName, string Fingerprint, string MethodName)> DiscoverCanSignals()
|
private IEnumerable<(string DisplayName, string Fingerprint, string MethodName)> DiscoverCanSignals()
|
||||||
{
|
{
|
||||||
if (_deviceManager?.CANFD?.DBCParser?.MsgDatabase == null) yield break;
|
if (_deviceManager?.CANFD == null) yield break;
|
||||||
if (_systemConfig?.ConfigurationList == null) yield break;
|
if (_systemConfig?.ConfigurationList == null) yield break;
|
||||||
|
|
||||||
var msgDb = _deviceManager.CANFD.DBCParser.MsgDatabase;
|
var msgDb = DBCParse.MsgDatabase;
|
||||||
string canDeviceFingerprint = _deviceManager.GetCanDeviceFingerprint();
|
string canDeviceFingerprint = _deviceManager.GetCanDeviceFingerprint();
|
||||||
foreach (var cfg in _systemConfig.ConfigurationList)
|
foreach (var cfg in _systemConfig.ConfigurationList)
|
||||||
{
|
{
|
||||||
@@ -331,10 +331,10 @@ namespace MonitorModule.ViewModels.Dialogs
|
|||||||
private void OnDbcLoaded(DBCLoadedArgs args)
|
private void OnDbcLoaded(DBCLoadedArgs args)
|
||||||
{
|
{
|
||||||
if (args.Scope != _systemConfig?.Title) return;
|
if (args.Scope != _systemConfig?.Title) return;
|
||||||
if (_deviceManager?.CANFD?.DBCParser?.MsgDatabase == null) return;
|
if (_deviceManager?.CANFD == null) return;
|
||||||
|
|
||||||
int channel = (int)args.Channel;
|
int channel = (int)args.Channel;
|
||||||
var msgDb = _deviceManager.CANFD.DBCParser.MsgDatabase;
|
var msgDb = DBCParse.MsgDatabase;
|
||||||
if (channel < 0 || channel >= msgDb.Count) return;
|
if (channel < 0 || channel >= msgDb.Count) return;
|
||||||
|
|
||||||
string canDeviceFingerprint = _deviceManager.GetCanDeviceFingerprint();
|
string canDeviceFingerprint = _deviceManager.GetCanDeviceFingerprint();
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
|
using TSMasterCAN;
|
||||||
using UIShare.GlobalVariable;
|
using UIShare.GlobalVariable;
|
||||||
using UIShare.PubEvent;
|
using UIShare.PubEvent;
|
||||||
using UIShare.UIViewModel;
|
using UIShare.UIViewModel;
|
||||||
using UIShare.ViewModelBase;
|
using UIShare.ViewModelBase;
|
||||||
using ZLGUSBCANFD;
|
|
||||||
|
|
||||||
|
|
||||||
namespace MonitorModule.ViewModels
|
namespace MonitorModule.ViewModels
|
||||||
@@ -535,9 +535,9 @@ namespace MonitorModule.ViewModels
|
|||||||
private void RefreshConfiguredCanSignals()
|
private void RefreshConfiguredCanSignals()
|
||||||
{
|
{
|
||||||
if (_systemConfig?.ConfigurationList == null || _systemConfig.ConfigurationList.Count == 0) return;
|
if (_systemConfig?.ConfigurationList == null || _systemConfig.ConfigurationList.Count == 0) return;
|
||||||
if (_deviceManager?.CANFD?.DBCParser?.MsgDatabase == null) return;
|
if (_deviceManager?.CANFD == null) return;
|
||||||
|
|
||||||
var msgDb = _deviceManager.CANFD.DBCParser.MsgDatabase;
|
var msgDb = DBCParse.MsgDatabase;
|
||||||
foreach (var channel in _systemConfig.ConfigurationList.Select(c => c.Channel).Distinct())
|
foreach (var channel in _systemConfig.ConfigurationList.Select(c => c.Channel).Distinct())
|
||||||
{
|
{
|
||||||
if (channel < 0 || channel >= msgDb.Count) continue;
|
if (channel < 0 || channel >= msgDb.Count) continue;
|
||||||
@@ -633,10 +633,9 @@ namespace MonitorModule.ViewModels
|
|||||||
private void OnDbcLoaded(DBCLoadedArgs args)
|
private void OnDbcLoaded(DBCLoadedArgs args)
|
||||||
{
|
{
|
||||||
if (args.Scope != TestStatus) return;
|
if (args.Scope != TestStatus) return;
|
||||||
if (_deviceManager?.CANFD?.DBCParser?.MsgDatabase == null) return;
|
if (_deviceManager?.CANFD == null) return;
|
||||||
|
|
||||||
int channel = (int)args.Channel;
|
int channel = (int)args.Channel;
|
||||||
var msgDb = _deviceManager.CANFD.DBCParser.MsgDatabase;
|
var msgDb = DBCParse.MsgDatabase;
|
||||||
if (channel < 0 || channel >= msgDb.Count) return;
|
if (channel < 0 || channel >= msgDb.Count) return;
|
||||||
|
|
||||||
RefreshCanSignalsForChannel(args.Channel, msgDb[channel]);
|
RefreshCanSignalsForChannel(args.Channel, msgDb[channel]);
|
||||||
|
|||||||
679
TSMasterCAN/CAN.cs
Normal file
679
TSMasterCAN/CAN.cs
Normal file
@@ -0,0 +1,679 @@
|
|||||||
|
using Common.Attributes;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using TSMaster;
|
||||||
|
namespace TSMasterCAN
|
||||||
|
{
|
||||||
|
[ACPCommand]
|
||||||
|
public class CAN : IDisposable
|
||||||
|
{
|
||||||
|
#region 静态成员
|
||||||
|
|
||||||
|
public static event Action? ConnectEvent;
|
||||||
|
public static event Action? DisConnectEvent;
|
||||||
|
public static bool ConnectFlag { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实时接收到的 CAN 报文缓存(按报文 ID 索引,保留最新帧)。
|
||||||
|
/// 供 CANViewModel.Refresh 等界面轮询读取。
|
||||||
|
/// </summary>
|
||||||
|
public static ConcurrentDictionary<int, TLIBCANFD> RealTimeMessages { get; } = new();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 实例字段与属性
|
||||||
|
|
||||||
|
/// <summary>设备类型号(43 = USBCANFD-400U 等,TSMaster 中作为配置参考保留)</summary>
|
||||||
|
public uint DeviceType { get; }
|
||||||
|
/// <summary>设备索引</summary>
|
||||||
|
public uint DeviceIndex { get; }
|
||||||
|
/// <summary>最大通道数</summary>
|
||||||
|
public int MaxChannels { get; }
|
||||||
|
/// <summary>仲裁域波特率</summary>
|
||||||
|
public string ABitBaud { get; }
|
||||||
|
/// <summary>数据域波特率</summary>
|
||||||
|
public string DBitBaud { get; }
|
||||||
|
/// <summary>是否开启终端电阻</summary>
|
||||||
|
public bool EnableTerminalResistance { get; }
|
||||||
|
|
||||||
|
/// <summary>DBC 解析器(提供 MsgDatabase、MaxChannels 等访问)</summary>
|
||||||
|
public DBCParse DBCParser => DBCParse.Instance;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DBC 报文解码事件:当接收到 CAN 帧并通过 DBC 解码后触发。
|
||||||
|
/// 参数:(通道号, 解码后的报文信息)
|
||||||
|
/// </summary>
|
||||||
|
public event Action<uint, DBCMessage>? OnDbcMessageDecoded;
|
||||||
|
|
||||||
|
private bool _disposed;
|
||||||
|
private bool _isOpened;
|
||||||
|
private TCANFDQueueEvent_Win32? _instanceListener;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 构造函数
|
||||||
|
|
||||||
|
/// <summary>默认构造函数(兼容 SystemConfig.CANFD = new() 等场景)</summary>
|
||||||
|
public CAN() : this(43, 0, 4, "500000", "2000000", true) { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 带配置参数的构造函数(兼容原 ZLG 设备管理流程)。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="deviceType">设备类型号</param>
|
||||||
|
/// <param name="deviceIndex">设备索引</param>
|
||||||
|
/// <param name="maxChannels">最大通道数</param>
|
||||||
|
/// <param name="aBitBaud">仲裁域波特率</param>
|
||||||
|
/// <param name="dBitBaud">数据域波特率</param>
|
||||||
|
/// <param name="enableTerminalResistance">是否开启终端电阻</param>
|
||||||
|
public CAN(uint deviceType, uint deviceIndex, int maxChannels, string aBitBaud, string dBitBaud, bool enableTerminalResistance)
|
||||||
|
{
|
||||||
|
DeviceType = deviceType;
|
||||||
|
DeviceIndex = deviceIndex;
|
||||||
|
MaxChannels = maxChannels;
|
||||||
|
ABitBaud = aBitBaud;
|
||||||
|
DBitBaud = dBitBaud;
|
||||||
|
EnableTerminalResistance = enableTerminalResistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 实例方法 — 设备管理(替代原 ZLG 实例方法)
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 打开设备:初始化 TSMaster 库 → 设置通道数 → 配置波特率 → 连接 → 启动 RBS → 注册监听。
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>true 表示成功</returns>
|
||||||
|
public bool 打开设备()
|
||||||
|
{
|
||||||
|
if (_isOpened) return true;
|
||||||
|
if (ConnectFlag) { _isOpened = true; return true; }
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 1. 初始化 TSMaster 库
|
||||||
|
var re = Init("ACP");
|
||||||
|
if (re != 0)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"TSMaster 初始化失败,错误代码:{re}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 设置通道数
|
||||||
|
TsMasterApi.tsapp_set_can_channel_count(MaxChannels);
|
||||||
|
|
||||||
|
// 3. 配置各通道波特率
|
||||||
|
float arbKbps = float.Parse(ABitBaud) / 1000f;
|
||||||
|
float dataKbps = float.Parse(DBitBaud) / 1000f;
|
||||||
|
for (int ch = 0; ch < MaxChannels; ch++)
|
||||||
|
{
|
||||||
|
TsMasterApi.tsapp_configure_baudrate_canfd(
|
||||||
|
ch, arbKbps, dataKbps,
|
||||||
|
TLIBCANFDControllerType.lfdtISOCAN,
|
||||||
|
TLIBCANFDControllerMode.lfdmNormal,
|
||||||
|
EnableTerminalResistance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 连接硬件
|
||||||
|
re = Connect();
|
||||||
|
if (re != 0)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"CAN 连接失败,错误代码:{re}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 注册接收监听(用于 DBC 信号解码广播)
|
||||||
|
_instanceListener = new TCANFDQueueEvent_Win32(OnCanFdReceived);
|
||||||
|
RegisterListener(_instanceListener);
|
||||||
|
|
||||||
|
_isOpened = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"打开设备异常:{ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 关闭 CAN 卡设备:注销监听 → 断开连接。
|
||||||
|
/// </summary>
|
||||||
|
public void 关闭CAN卡设备()
|
||||||
|
{
|
||||||
|
if (!_isOpened && !ConnectFlag) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_instanceListener != null)
|
||||||
|
{
|
||||||
|
UnRegisterListener(_instanceListener);
|
||||||
|
_instanceListener = null;
|
||||||
|
}
|
||||||
|
DisConnect();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"关闭CAN卡设备异常:{ex.Message}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isOpened = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化并启动指定通道(配置波特率,TSMaster 在 Connect 时已自动启动所有通道)。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="channel">通道号</param>
|
||||||
|
public void 初始化并启动通道(uint channel)
|
||||||
|
{
|
||||||
|
if (channel >= MaxChannels) return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
float arbKbps = float.Parse(ABitBaud) / 1000f;
|
||||||
|
float dataKbps = float.Parse(DBitBaud) / 1000f;
|
||||||
|
TsMasterApi.tsapp_configure_baudrate_canfd(
|
||||||
|
(int)channel, arbKbps, dataKbps,
|
||||||
|
TLIBCANFDControllerType.lfdtISOCAN,
|
||||||
|
TLIBCANFDControllerMode.lfdmNormal,
|
||||||
|
EnableTerminalResistance);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"初始化通道 {channel} 异常:{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 加载通道 DBC 文件。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="channel">通道号</param>
|
||||||
|
/// <param name="filePath">DBC 文件路径</param>
|
||||||
|
/// <returns>true 表示加载成功</returns>
|
||||||
|
public bool 加载通道DBC文件(uint channel, string filePath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var re = LoadDBC(filePath, [(int)channel], out _);
|
||||||
|
return re == 0;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"加载通道 {channel} DBC 文件异常:{ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// CAN FD 接收回调:接收原始帧 → 查找 DBC → 解码信号 → 触发 OnDbcMessageDecoded 事件。
|
||||||
|
/// </summary>
|
||||||
|
private void OnCanFdReceived(ref int AObj, ref TLIBCANFD AData)
|
||||||
|
{
|
||||||
|
if (_disposed) return;
|
||||||
|
|
||||||
|
// 始终缓存最新报文,供界面轮询刷新
|
||||||
|
int identifier = AData.FIdentifier;
|
||||||
|
var canfdCopy = AData;
|
||||||
|
RealTimeMessages[identifier] = canfdCopy;
|
||||||
|
|
||||||
|
if (OnDbcMessageDecoded == null) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
uint channel = AData.FIdxChn;
|
||||||
|
if (channel >= DBCParse.MsgDatabase.Count) return;
|
||||||
|
|
||||||
|
// 在 DBC 数据库中查找匹配的报文
|
||||||
|
var msgList = DBCParse.MsgDatabase[(int)channel];
|
||||||
|
var match = msgList.FirstOrDefault(m => m.msg_id == identifier);
|
||||||
|
if (match == null || match.signal_Name == null || match.signal_Name.Length == 0) return;
|
||||||
|
|
||||||
|
// 解码各信号值
|
||||||
|
var signals = new DBCSignal[match.signal_Name.Length];
|
||||||
|
for (int i = 0; i < match.signal_Name.Length; i++)
|
||||||
|
{
|
||||||
|
double value = 0;
|
||||||
|
TsMasterApi.tsdb_get_signal_value_canfd(ref canfdCopy, match.msg_name, match.signal_Name[i], ref value);
|
||||||
|
signals[i] = new DBCSignal
|
||||||
|
{
|
||||||
|
strName = Encoding.Default.GetBytes(match.signal_Name[i]),
|
||||||
|
nRawvalue = value,
|
||||||
|
nFactor = 1,
|
||||||
|
nOffset = 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded = new DBCMessage
|
||||||
|
{
|
||||||
|
nID = (uint)identifier,
|
||||||
|
nSignalCount = signals.Length,
|
||||||
|
vSignals = signals
|
||||||
|
};
|
||||||
|
|
||||||
|
OnDbcMessageDecoded?.Invoke(channel, decoded);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"DBC 信号解码异常:{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed) return;
|
||||||
|
_disposed = true;
|
||||||
|
关闭CAN卡设备();
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化TSMasterCAN
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ProjectName"></param>
|
||||||
|
/// <param name="filePath"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Browsable(false)]
|
||||||
|
public static int Init(string ProjectName, string? filePath = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(filePath))
|
||||||
|
{
|
||||||
|
return TsMasterApi.initialize_lib_tsmaster(ProjectName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return TsMasterApi.initialize_lib_tsmaster_with_project(ProjectName, Path.GetFullPath(filePath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 释放TSMasterCAN
|
||||||
|
/// </summary>
|
||||||
|
[Browsable(false)]
|
||||||
|
public static void Release()
|
||||||
|
{
|
||||||
|
TsMasterApi.finalize_lib_tsmaster();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int obj = 0;
|
||||||
|
public static TCANFDQueueEvent_Win32 listener;
|
||||||
|
[Browsable(false)]
|
||||||
|
public static int RegisterListener(TCANFDQueueEvent_Win32 listenEvent)
|
||||||
|
{
|
||||||
|
listener = listenEvent;
|
||||||
|
var re = TsMasterApi.tsapp_register_event_canfd(ref obj, listener);
|
||||||
|
Debug.Assert(re == 0);
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Browsable(false)]
|
||||||
|
public static int UnRegisterListener(TCANFDQueueEvent_Win32 listenEvent = null)
|
||||||
|
{
|
||||||
|
if (listenEvent is not null)
|
||||||
|
{
|
||||||
|
listener = listenEvent;
|
||||||
|
}
|
||||||
|
var re = TsMasterApi.tsapp_unregister_event_canfd(ref obj, listener);
|
||||||
|
Debug.Assert(re == 0);
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 连接
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int Connect()
|
||||||
|
{
|
||||||
|
|
||||||
|
var re = TsMasterApi.tsapp_connect();
|
||||||
|
if (re == 0)
|
||||||
|
{
|
||||||
|
re = TsMasterApi.tscom_can_rbs_start();
|
||||||
|
if (re == 0)
|
||||||
|
{
|
||||||
|
ConnectFlag = true;
|
||||||
|
Task.Run(() => ConnectEvent?.Invoke());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"CAN_RBS启动失败,错误代码:{re}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"CAN连接失败,错误代码:{re}");
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 断开
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int DisConnect()
|
||||||
|
{
|
||||||
|
var re = TsMasterApi.tsapp_disconnect();
|
||||||
|
//Debug.Assert(re == 0);
|
||||||
|
if (re == 0)
|
||||||
|
{
|
||||||
|
ConnectFlag = false;
|
||||||
|
Task.Run(() => DisConnectEvent?.Invoke());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"断开CAN连接失败,错误代码:{re}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 加载DBC
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filePath"></param>
|
||||||
|
/// <param name="channel"></param>
|
||||||
|
/// <param name="databaseID"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Browsable(false)]
|
||||||
|
public static int LoadDBC(string filePath, int[] channel, out uint databaseID)
|
||||||
|
{
|
||||||
|
databaseID = 0;
|
||||||
|
|
||||||
|
var re = TsMasterApi.tsdb_load_can_db(Path.GetFullPath(filePath), string.Join(",", channel), ref databaseID);
|
||||||
|
foreach (var item in channel)
|
||||||
|
{
|
||||||
|
DBCParse.parse(databaseID, item);
|
||||||
|
DBCParse.rbs_parse(databaseID, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 卸载DBC
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="databaseID"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Browsable(false)]
|
||||||
|
public static int UnLoadDBC(uint? databaseID = null)
|
||||||
|
{
|
||||||
|
if (databaseID == null)
|
||||||
|
{
|
||||||
|
return TsMasterApi.tsdb_unload_can_dbs();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return TsMasterApi.tsdb_unload_can_db(databaseID.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 开始记录日志
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filePath"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int StartLogging(string filePath)
|
||||||
|
{
|
||||||
|
return TsMasterApi.tsapp_start_logging(Path.GetFullPath(filePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 结束记录日志
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int StopLogging()
|
||||||
|
{
|
||||||
|
return TsMasterApi.tsapp_stop_logging();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 弹出通道映射窗口
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isWait"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Browsable(false)]
|
||||||
|
public static int ShowChannelMappingWindow(bool isWait = false)
|
||||||
|
{
|
||||||
|
return TsMasterApi.tsapp_show_tsmaster_window("Hardware", isWait);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取信号值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="channel"></param>
|
||||||
|
/// <param name="AMsgName"></param>
|
||||||
|
/// <param name="ASgnName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static double GetSignalValue(byte channel, string AMsgName, string ASgnName)
|
||||||
|
{
|
||||||
|
double value = double.NaN;
|
||||||
|
var find = DBCParse.MsgDatabase[channel].First(s => s.msg_name == AMsgName);
|
||||||
|
var re = TsMasterApi.tsdb_get_signal_value_canfd(ref find.ACANFD, AMsgName, ASgnName, ref value);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取信号值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="channel"></param>
|
||||||
|
/// <param name="AMsgName"></param>
|
||||||
|
/// <param name="ASgnName"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Browsable(false)]
|
||||||
|
public static int GetSignalValue(byte channel, string AMsgName, string ASgnName, ref double value)
|
||||||
|
{
|
||||||
|
var find = DBCParse.MsgDatabase[channel].First(s => s.msg_name == AMsgName);
|
||||||
|
return TsMasterApi.tsdb_get_signal_value_canfd(ref find.ACANFD, AMsgName, ASgnName, ref value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取信号值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ACANFD"></param>
|
||||||
|
/// <param name="AMsgName"></param>
|
||||||
|
/// <param name="ASgnName"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
|
||||||
|
[Browsable(false)]
|
||||||
|
public static int GetSignalValue(ref TLIBCANFD ACANFD, string AMsgName, string ASgnName, ref double value)
|
||||||
|
{
|
||||||
|
return TsMasterApi.tsdb_get_signal_value_canfd(ref ACANFD, AMsgName, ASgnName, ref value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置信号值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="channel"></param>
|
||||||
|
/// <param name="AMsgName"></param>
|
||||||
|
/// <param name="ASgnName"></param>
|
||||||
|
/// <param name="AValue"></param>
|
||||||
|
/// <param name="isSend"></param>
|
||||||
|
/// <param name="sendPeriod"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="Exception"></exception>
|
||||||
|
public static int SetSignalValue(byte channel, string AMsgName, string ASgnName, double AValue, bool isSend = false, float sendPeriod = 0)
|
||||||
|
{
|
||||||
|
var find = DBCParse.MsgDatabase[channel].First(s => s.msg_name == AMsgName);
|
||||||
|
find.ACANFD.FIdxChn = channel;
|
||||||
|
var re = TsMasterApi.tsdb_set_signal_value_canfd(ref find.ACANFD, AMsgName, ASgnName, AValue);
|
||||||
|
Debug.Assert(re == 0);
|
||||||
|
if (re != 0) throw new Exception($"设置报文失败!返回代码{re}");
|
||||||
|
if (re != 0) return re;
|
||||||
|
if (isSend)
|
||||||
|
{
|
||||||
|
if (sendPeriod == 0)
|
||||||
|
{
|
||||||
|
return TsMasterApi.tsapp_transmit_canfd_async(ref find.ACANFD);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return TsMasterApi.tsapp_add_cyclic_msg_canfd(ref find.ACANFD, sendPeriod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置信号值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="channel"></param>
|
||||||
|
/// <param name="AMsgName"></param>
|
||||||
|
/// <param name="sendPeriod"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int SetSignalValue(APP_CHANNEL channel, string AMsgName, float sendPeriod = 0)
|
||||||
|
{
|
||||||
|
var find = DBCParse.MsgDatabase[(byte)channel].First(s => s.msg_name == AMsgName);
|
||||||
|
find.ACANFD.FIdxChn = (byte)channel;
|
||||||
|
if (sendPeriod == 0)
|
||||||
|
{
|
||||||
|
return TsMasterApi.tsapp_transmit_canfd_async(ref find.ACANFD);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return TsMasterApi.tsapp_add_cyclic_msg_canfd(ref find.ACANFD, sendPeriod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送自定义报文
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Browsable(false)]
|
||||||
|
public static int SetMsg(TLIBCANFD msg)
|
||||||
|
{
|
||||||
|
return TsMasterApi.tsapp_transmit_canfd_async(ref msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置报文
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="channel">通道</param>
|
||||||
|
/// <param name="ID">DBC数据库ID</param>
|
||||||
|
/// <param name="bytes">报文数组</param>
|
||||||
|
public static void SetMsg(APP_CHANNEL channel, int ID, byte[] bytes)
|
||||||
|
{
|
||||||
|
var find = DBCParse.MsgDatabase[(byte)channel].FirstOrDefault(s => s.ACANFD.FIdentifier == ID);
|
||||||
|
if (find != null)
|
||||||
|
{
|
||||||
|
Array.Copy(bytes, 0, find.ACANFD.FData, 0, Math.Min(bytes.Length, find.ACANFD.FData.Length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送自定义报文
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="AIdxChn"></param>
|
||||||
|
/// <param name="AID"></param>
|
||||||
|
/// <param name="AIsTx"></param>
|
||||||
|
/// <param name="AIsExt"></param>
|
||||||
|
/// <param name="AIsRemote"></param>
|
||||||
|
/// <param name="ADLC"></param>
|
||||||
|
/// <param name="ADataArray"></param>
|
||||||
|
/// <param name="AIsFD"></param>
|
||||||
|
/// <param name="AIsBRS"></param>
|
||||||
|
/// <param name="isUpdateDBCDatabase"></param>
|
||||||
|
/// <param name="sendPeriod"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int SendMsg(APP_CHANNEL AIdxChn, int AID, bool AIsTx, bool AIsExt,
|
||||||
|
bool AIsRemote, byte ADLC, byte[] ADataArray, bool AIsFD = true, bool AIsBRS = false,
|
||||||
|
bool isUpdateDBCDatabase = false, float sendPeriod = 0)
|
||||||
|
{
|
||||||
|
var send = new TLIBCANFD
|
||||||
|
{
|
||||||
|
FIdxChn = (byte)AIdxChn,
|
||||||
|
FProperties = 0,
|
||||||
|
FIdentifier = AID,
|
||||||
|
FDLC = ADLC,
|
||||||
|
FTimeUS = 0uL,
|
||||||
|
FData = new byte[64],
|
||||||
|
FFDProperties = 0,
|
||||||
|
FIsTx = AIsTx,
|
||||||
|
FIsError = false,
|
||||||
|
FIsExt = AIsExt,
|
||||||
|
FIsRemote = AIsRemote,
|
||||||
|
FIsFD = AIsFD,
|
||||||
|
FIsBRS = AIsBRS
|
||||||
|
};
|
||||||
|
int length = Math.Min(ADataArray.Length, 64);
|
||||||
|
Array.Copy(ADataArray, 0, send.FData, 0, length);
|
||||||
|
int re;
|
||||||
|
if (sendPeriod == 0)
|
||||||
|
{
|
||||||
|
re = TsMasterApi.tsapp_transmit_canfd_async(ref send);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
re = TsMasterApi.tsapp_add_cyclic_msg_canfd(ref send, sendPeriod);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUpdateDBCDatabase)
|
||||||
|
{
|
||||||
|
var find = DBCParse.MsgDatabase[(byte)AIdxChn].FirstOrDefault(s => s.ACANFD.FIdentifier == send.FIdentifier);
|
||||||
|
if (find != null)
|
||||||
|
{
|
||||||
|
Array.Copy(send.FData, 0, find.ACANFD.FData, 0, Math.Min(ADataArray.Length, find.ACANFD.FData.Length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加循环发送报文
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="channel"></param>
|
||||||
|
/// <param name="AMsgName"></param>
|
||||||
|
/// <param name="sendPeriod"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int AddCyclicMsg(byte channel, string AMsgName, float sendPeriod)
|
||||||
|
{
|
||||||
|
var find = DBCParse.MsgDatabase[channel].First(s => s.msg_name == AMsgName);
|
||||||
|
find.ACANFD.FIdxChn = channel;
|
||||||
|
return TsMasterApi.tsapp_add_cyclic_msg_canfd(ref find.ACANFD, sendPeriod);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清除循环发送的报文
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int DeleteCyclicMsgs()
|
||||||
|
{
|
||||||
|
return TsMasterApi.tsapp_delete_cyclic_msgs();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取错误提示
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="errorCode">错误代码</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetErrorDescription(int errorCode)
|
||||||
|
{
|
||||||
|
IntPtr ADesc = IntPtr.Zero;
|
||||||
|
TsMasterApi.tsapp_get_error_description(errorCode, ref ADesc);
|
||||||
|
|
||||||
|
if (ADesc == IntPtr.Zero) return $"未知错误代码: {errorCode}";
|
||||||
|
|
||||||
|
// 假设返回的是 ANSI 字符串(如 C 的 char*)
|
||||||
|
string? description = Marshal.PtrToStringAnsi(ADesc);
|
||||||
|
|
||||||
|
return description ?? $"未知错误代码: {errorCode}";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
305
TSMasterCAN/DBCParse.cs
Normal file
305
TSMasterCAN/DBCParse.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using TSMaster;
|
||||||
|
|
||||||
|
namespace TSMasterCAN
|
||||||
|
{
|
||||||
|
public struct can_network
|
||||||
|
{
|
||||||
|
[JsonInclude]
|
||||||
|
public string network_name;
|
||||||
|
[JsonInclude]
|
||||||
|
public can_node[] can_nodes;
|
||||||
|
|
||||||
|
}
|
||||||
|
public struct can_node
|
||||||
|
{
|
||||||
|
[JsonInclude]
|
||||||
|
public string node_name;
|
||||||
|
[JsonInclude]
|
||||||
|
public can_message[] tx_can_Messages;
|
||||||
|
[JsonInclude]
|
||||||
|
public can_message[] rx_can_Messages;
|
||||||
|
}
|
||||||
|
public struct can_message
|
||||||
|
{
|
||||||
|
[JsonInclude]
|
||||||
|
public string message_name;
|
||||||
|
[JsonInclude]
|
||||||
|
public string[] signals;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class _Msg_
|
||||||
|
{
|
||||||
|
public int msg_type { get; set; }
|
||||||
|
public int msg_id { get; set; }
|
||||||
|
public int msg_cyclic { get; set; }
|
||||||
|
public byte msg_dlc { get; set; }
|
||||||
|
public string msg_name { get; set; }
|
||||||
|
public string[] signal_Name { get; set; }
|
||||||
|
public int msg_brs { get; set; }
|
||||||
|
public TLIBCANFD ACANFD;
|
||||||
|
}
|
||||||
|
public enum Msg_Type
|
||||||
|
{
|
||||||
|
CAN = 0,
|
||||||
|
CANFD = 1,
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// DBC 文件信息类型枚举,用于指定 tsdb_get_can_db_info 查询的内容类型。
|
||||||
|
/// </summary>
|
||||||
|
public enum DBCINFO
|
||||||
|
{
|
||||||
|
/// <summary>网络名称</summary>
|
||||||
|
NETWORK_Name = 0,
|
||||||
|
|
||||||
|
/// <summary>CAN 报文总数</summary>
|
||||||
|
CAN_MSG_COUNT = 11,
|
||||||
|
/// <summary>CAN FD 报文总数</summary>
|
||||||
|
CANFD_MSG_COUNT = 12,
|
||||||
|
/// <summary>节点总数</summary>
|
||||||
|
NODE_COUNT = 14,
|
||||||
|
|
||||||
|
/// <summary>信号名称</summary>
|
||||||
|
SIGNAL_NAME = 33,
|
||||||
|
|
||||||
|
/// <summary>CAN 报文类型</summary>
|
||||||
|
CAN_MSG_TYPE = 40,
|
||||||
|
/// <summary>CAN 报文 DLC(数据长度码)</summary>
|
||||||
|
CAN_MSG_DLC = 41,
|
||||||
|
/// <summary>CAN 报文 ID</summary>
|
||||||
|
CAN_MSG_ID = 42,
|
||||||
|
/// <summary>CAN 报文周期</summary>
|
||||||
|
CAN_MSG_CYCLIC = 43,
|
||||||
|
/// <summary>CAN 报文名称</summary>
|
||||||
|
CAN_MSG_NAME = 44,
|
||||||
|
/// <summary>CAN 报文信号数量</summary>
|
||||||
|
CAN_MSG_SIGNAL_COUNT = 47,
|
||||||
|
/// <summary>CAN 报文信号名称索引</summary>
|
||||||
|
CAN_MSG_SIGNAL_NAME_INDEX = 48,
|
||||||
|
|
||||||
|
/// <summary>CAN FD 报文类型</summary>
|
||||||
|
CANFD_MSG_TYPE = 60,
|
||||||
|
/// <summary>CAN FD 报文 DLC(数据长度码)</summary>
|
||||||
|
CANFD_MSG_DLC = 61,
|
||||||
|
/// <summary>CAN FD 报文 ID</summary>
|
||||||
|
CANFD_MSG_ID = 62,
|
||||||
|
/// <summary>CAN FD 报文周期</summary>
|
||||||
|
CANFD_MSG_CYCLIC = 63,
|
||||||
|
/// <summary>CAN FD 报文名称</summary>
|
||||||
|
CANFD_MSG_NAME = 64,
|
||||||
|
/// <summary>CAN FD 报文信号数量</summary>
|
||||||
|
CANFD_MSG_SIGNAL_COUNT = 67,
|
||||||
|
/// <summary>CAN FD 报文信号名称索引</summary>
|
||||||
|
CANFD_MSG_SIGNAL_NAME_INDEX = 68,
|
||||||
|
|
||||||
|
/// <summary>节点名称</summary>
|
||||||
|
NODE_Name = 101,
|
||||||
|
/// <summary>节点发送 CAN 报文数量</summary>
|
||||||
|
NODE_TX_CAN_COUNT = 103,
|
||||||
|
/// <summary>节点发送 CAN 报文索引</summary>
|
||||||
|
NODE_TX_CAN_MESSAGE_INDEX = 104,
|
||||||
|
/// <summary>节点接收 CAN 报文数量</summary>
|
||||||
|
NODE_RX_CAN_COUNT = 105,
|
||||||
|
/// <summary>节点接收 CAN 报文索引</summary>
|
||||||
|
NODE_RX_CAN_MESSAGE_INDEX = 106,
|
||||||
|
/// <summary>节点发送 CAN FD 报文数量</summary>
|
||||||
|
NODE_TX_CANFD_COUNT = 107,
|
||||||
|
/// <summary>节点发送 CAN FD 报文索引</summary>
|
||||||
|
NODE_TX_CANFD_MESSAGE_INDEX = 108,
|
||||||
|
/// <summary>节点接收 CAN FD 报文数量</summary>
|
||||||
|
NODE_RX_CANFD_COUNT = 109,
|
||||||
|
/// <summary>节点接收 CAN FD 报文索引</summary>
|
||||||
|
NODE_RX_CANFD_MESSAGE_INDEX = 110,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DBC 解码后的信号信息(替代原 ZLG ZDBC.DBCSignal)。
|
||||||
|
/// </summary>
|
||||||
|
public struct DBCSignal
|
||||||
|
{
|
||||||
|
/// <summary>信号名称(原始字节,兼容 Encoding.Default 解码)</summary>
|
||||||
|
public byte[] strName;
|
||||||
|
/// <summary>原始值(TSMaster tsdb_get_signal_value_canfd 返回的物理值)</summary>
|
||||||
|
public double nRawvalue;
|
||||||
|
/// <summary>因子(TSMaster 返回物理值,因子固定为 1)</summary>
|
||||||
|
public double nFactor;
|
||||||
|
/// <summary>偏移(TSMaster 返回物理值,偏移固定为 0)</summary>
|
||||||
|
public double nOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DBC 解码后的报文信息(替代原 ZLG ZDBC.DBCMessage)。
|
||||||
|
/// 在 CAN 接收回调中对原始 TLIBCANFD 帧解码后构建此对象并触发 OnDbcMessageDecoded 事件。
|
||||||
|
/// </summary>
|
||||||
|
public class DBCMessage
|
||||||
|
{
|
||||||
|
/// <summary>报文 ID</summary>
|
||||||
|
public uint nID;
|
||||||
|
/// <summary>信号数量</summary>
|
||||||
|
public int nSignalCount;
|
||||||
|
/// <summary>信号数组</summary>
|
||||||
|
public DBCSignal[] vSignals;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DBCParse
|
||||||
|
{
|
||||||
|
private static DBCParse? _instance;
|
||||||
|
/// <summary>单例实例,供 CAN.DBCParser 属性使用(MsgDatabase 等成员为 static,实例仅用于属性访问兼容)。</summary>
|
||||||
|
public static DBCParse Instance => _instance ??= new DBCParse();
|
||||||
|
|
||||||
|
/// <summary>最大通道数(等于 MsgDatabase 的维度)</summary>
|
||||||
|
public int MaxChannels => MsgDatabase.Count;
|
||||||
|
|
||||||
|
public static List<List<_Msg_>> MsgDatabase { get; set; } =
|
||||||
|
[.. Enumerable.Range(0, 12).Select(_ => new List<_Msg_>())];
|
||||||
|
public static can_network can_Network;
|
||||||
|
public static string tsdb_get_can_db_info(uint ADatabaseId, int AType, int AIndex, int ASubIndex)
|
||||||
|
{
|
||||||
|
nint tmpInt = new nint();
|
||||||
|
int ret = TsMasterApi.tsdb_get_can_db_info(ADatabaseId, AType, AIndex, ASubIndex, ref tmpInt);
|
||||||
|
if (ret == 0)
|
||||||
|
{
|
||||||
|
return Marshal.PtrToStringAnsi(tmpInt) ?? "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void parse(uint Aid, int channel)
|
||||||
|
{
|
||||||
|
int CANcount = int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CAN_MSG_COUNT, 0, 0));
|
||||||
|
for (int i = 0; i < CANcount; i++)
|
||||||
|
{
|
||||||
|
_Msg_ temp = new _Msg_();
|
||||||
|
temp.msg_type = int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CAN_MSG_TYPE, i, 0));
|
||||||
|
temp.msg_dlc = byte.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CAN_MSG_DLC, i, 0));
|
||||||
|
temp.msg_id = int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CAN_MSG_ID, i, 0));
|
||||||
|
temp.msg_name = tsdb_get_can_db_info(Aid, (int)DBCINFO.CAN_MSG_NAME, i, 0);
|
||||||
|
temp.msg_cyclic = int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CAN_MSG_CYCLIC, i, 0));
|
||||||
|
temp.signal_Name = new string[int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CAN_MSG_SIGNAL_COUNT, i, 0))];
|
||||||
|
for (int signalcount = 0; signalcount < temp.signal_Name.Length; signalcount++)
|
||||||
|
{
|
||||||
|
temp.signal_Name[signalcount] = tsdb_get_can_db_info(Aid, (int)DBCINFO.SIGNAL_NAME, int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CAN_MSG_SIGNAL_NAME_INDEX, i, signalcount)), 0);
|
||||||
|
}
|
||||||
|
temp.msg_brs = 0;
|
||||||
|
temp.ACANFD = new TLIBCANFD(APP_CHANNEL.CHN1,
|
||||||
|
temp.msg_id,
|
||||||
|
true,
|
||||||
|
temp.msg_type == 1 || temp.msg_type == 4 ? true : false,
|
||||||
|
false, temp.msg_dlc,
|
||||||
|
temp.msg_type < 2 ? false : true,
|
||||||
|
false);
|
||||||
|
MsgDatabase[channel].Add(temp);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
CANcount = int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CANFD_MSG_COUNT, 0, 0));
|
||||||
|
for (int i = 0; i < CANcount; i++)
|
||||||
|
{
|
||||||
|
_Msg_ temp = new _Msg_();
|
||||||
|
temp.msg_type = int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CANFD_MSG_TYPE, i, 0));
|
||||||
|
temp.msg_dlc = byte.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CANFD_MSG_DLC, i, 0));
|
||||||
|
temp.msg_id = int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CANFD_MSG_ID, i, 0));
|
||||||
|
temp.msg_name = tsdb_get_can_db_info(Aid, (int)DBCINFO.CANFD_MSG_NAME, i, 0);
|
||||||
|
temp.msg_cyclic = int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CANFD_MSG_CYCLIC, i, 0));
|
||||||
|
temp.signal_Name = new string[int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CANFD_MSG_SIGNAL_COUNT, i, 0))];
|
||||||
|
for (int signalcount = 0; signalcount < temp.signal_Name.Length; signalcount++)
|
||||||
|
{
|
||||||
|
temp.signal_Name[signalcount] = tsdb_get_can_db_info(Aid, (int)DBCINFO.SIGNAL_NAME, int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CANFD_MSG_SIGNAL_NAME_INDEX, i, signalcount)), 0);
|
||||||
|
}
|
||||||
|
temp.msg_brs = int.Parse(tsdb_get_can_db_info(Aid, 69, i, 0));
|
||||||
|
temp.ACANFD = new TLIBCANFD(APP_CHANNEL.CHN1, temp.msg_id, true, temp.msg_type == 1 || temp.msg_type == 4 ? true : false, false, temp.msg_dlc, temp.msg_type < 2 ? false : true, temp.msg_brs == 1 ? true : false);
|
||||||
|
MsgDatabase[channel].Add(temp);
|
||||||
|
}
|
||||||
|
MsgDatabase[channel].Sort((a, b) => a.msg_id.CompareTo(b.msg_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void rbs_parse(uint AID, int channel)
|
||||||
|
{
|
||||||
|
can_Network = new can_network();
|
||||||
|
can_Network.network_name = tsdb_get_can_db_info(AID, (int)DBCINFO.NETWORK_Name, 0, 0);
|
||||||
|
int Node_count = int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.NODE_COUNT, 0, 0));
|
||||||
|
can_Network.can_nodes = new can_node[Node_count];
|
||||||
|
for (int i = 0; i < Node_count; i++)
|
||||||
|
{
|
||||||
|
can_Network.can_nodes[i].node_name = tsdb_get_can_db_info(AID, (int)DBCINFO.NODE_Name, i, 0);
|
||||||
|
int tx_can_count = int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.NODE_TX_CAN_COUNT, i, 0));
|
||||||
|
int tx_canfd_count = int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.NODE_TX_CANFD_COUNT, i, 0));
|
||||||
|
|
||||||
|
int rx_can_count = int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.NODE_RX_CAN_COUNT, i, 0));
|
||||||
|
int rx_canfd_count = int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.NODE_RX_CANFD_COUNT, i, 0));
|
||||||
|
|
||||||
|
//获取tx_can结构体
|
||||||
|
can_Network.can_nodes[i].tx_can_Messages = new can_message[tx_can_count + tx_canfd_count];
|
||||||
|
|
||||||
|
can_Network.can_nodes[i].rx_can_Messages = new can_message[rx_can_count + rx_canfd_count];
|
||||||
|
//获取报文名以及信号名
|
||||||
|
//1 get can_message idnex
|
||||||
|
//2 get can message name
|
||||||
|
//3 get signal count
|
||||||
|
//4 get signal name
|
||||||
|
int tx_index = -1;
|
||||||
|
for (int tx_can = 0; tx_can < tx_can_count; tx_can++)
|
||||||
|
{
|
||||||
|
int can_message_idnex = int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.NODE_TX_CAN_MESSAGE_INDEX, i, tx_can));
|
||||||
|
can_Network.can_nodes[i].tx_can_Messages[tx_can].message_name = tsdb_get_can_db_info(AID, (int)DBCINFO.CAN_MSG_NAME, can_message_idnex, 0);
|
||||||
|
|
||||||
|
can_Network.can_nodes[i].tx_can_Messages[tx_can].signals = new string[int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.CAN_MSG_SIGNAL_COUNT, can_message_idnex, 0))];
|
||||||
|
for (int signalcount = 0; signalcount < can_Network.can_nodes[i].tx_can_Messages[tx_can].signals.Length; signalcount++)
|
||||||
|
{
|
||||||
|
can_Network.can_nodes[i].tx_can_Messages[tx_can].signals[signalcount] = tsdb_get_can_db_info(AID, (int)DBCINFO.SIGNAL_NAME, int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.CAN_MSG_SIGNAL_NAME_INDEX, can_message_idnex, signalcount)), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
tx_index = tx_can;
|
||||||
|
}
|
||||||
|
for (int tx_can = 0; tx_can < tx_canfd_count; tx_can++, tx_index++)
|
||||||
|
{
|
||||||
|
int can_message_idnex = int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.NODE_TX_CANFD_MESSAGE_INDEX, i, tx_can));
|
||||||
|
can_Network.can_nodes[i].tx_can_Messages[tx_index + 1].message_name = tsdb_get_can_db_info(AID, (int)DBCINFO.CANFD_MSG_NAME, can_message_idnex, 0);
|
||||||
|
|
||||||
|
can_Network.can_nodes[i].tx_can_Messages[tx_index + 1].signals = new string[int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.CANFD_MSG_SIGNAL_COUNT, can_message_idnex, 0))];
|
||||||
|
for (int signalcount = 0; signalcount < can_Network.can_nodes[i].tx_can_Messages[tx_index + 1].signals.Length; signalcount++)
|
||||||
|
{
|
||||||
|
can_Network.can_nodes[i].tx_can_Messages[tx_index + 1].signals[signalcount] = tsdb_get_can_db_info(AID, (int)DBCINFO.SIGNAL_NAME, int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.CANFD_MSG_SIGNAL_NAME_INDEX, can_message_idnex, signalcount)), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int rx_index = -1;
|
||||||
|
for (int tx_can = 0; tx_can < rx_can_count; tx_can++)
|
||||||
|
{
|
||||||
|
int can_message_idnex = int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.NODE_RX_CAN_MESSAGE_INDEX, i, tx_can));
|
||||||
|
can_Network.can_nodes[i].rx_can_Messages[tx_can].message_name = tsdb_get_can_db_info(AID, (int)DBCINFO.CAN_MSG_NAME, can_message_idnex, 0);
|
||||||
|
|
||||||
|
can_Network.can_nodes[i].rx_can_Messages[tx_can].signals = new string[int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.CAN_MSG_SIGNAL_COUNT, can_message_idnex, 0))];
|
||||||
|
for (int signalcount = 0; signalcount < can_Network.can_nodes[i].rx_can_Messages[tx_can].signals.Length; signalcount++)
|
||||||
|
{
|
||||||
|
can_Network.can_nodes[i].rx_can_Messages[tx_can].signals[signalcount] = tsdb_get_can_db_info(AID, (int)DBCINFO.SIGNAL_NAME, int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.CAN_MSG_SIGNAL_NAME_INDEX, can_message_idnex, signalcount)), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
rx_index = tx_can;
|
||||||
|
}
|
||||||
|
for (int tx_can = 0; tx_can < rx_canfd_count; tx_can++, rx_index++)
|
||||||
|
{
|
||||||
|
int can_message_idnex = int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.NODE_RX_CANFD_MESSAGE_INDEX, i, tx_can));
|
||||||
|
can_Network.can_nodes[i].rx_can_Messages[rx_index + 1].message_name = tsdb_get_can_db_info(AID, (int)DBCINFO.CANFD_MSG_NAME, can_message_idnex, 0);
|
||||||
|
|
||||||
|
can_Network.can_nodes[i].rx_can_Messages[rx_index + 1].signals = new string[int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.CANFD_MSG_SIGNAL_COUNT, can_message_idnex, 0))];
|
||||||
|
for (int signalcount = 0; signalcount < can_Network.can_nodes[i].rx_can_Messages[rx_index + 1].signals.Length; signalcount++)
|
||||||
|
{
|
||||||
|
can_Network.can_nodes[i].rx_can_Messages[rx_index + 1].signals[signalcount] = tsdb_get_can_db_info(AID, (int)DBCINFO.SIGNAL_NAME, int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.CANFD_MSG_SIGNAL_NAME_INDEX, can_message_idnex, signalcount)), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
BIN
TSMasterCAN/Interop.TSMasterAPI.dll
Normal file
BIN
TSMasterCAN/Interop.TSMasterAPI.dll
Normal file
Binary file not shown.
BIN
TSMasterCAN/TSMaster.dll
Normal file
BIN
TSMasterCAN/TSMaster.dll
Normal file
Binary file not shown.
@@ -1,14 +1,20 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<UseWPF>false</UseWPF>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Common\Common.csproj" />
|
<ProjectReference Include="..\Common\Common.csproj" />
|
||||||
<ProjectReference Include="..\Logger\Logger.csproj" />
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Interop.TSMasterAPI">
|
||||||
|
<HintPath>..\CanDriver\bin\Debug\net8.0\Interop.TSMasterAPI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -4,8 +4,8 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using TSMasterCAN;
|
||||||
using UIShare.PubEvent;
|
using UIShare.PubEvent;
|
||||||
using ZLGUSBCANFD;
|
|
||||||
|
|
||||||
namespace UIShare.GlobalVariable
|
namespace UIShare.GlobalVariable
|
||||||
{
|
{
|
||||||
@@ -22,8 +22,7 @@ namespace UIShare.GlobalVariable
|
|||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
/// <summary>已订阅解码事件的 CANFD 实例 → 委托 映射</summary>
|
/// <summary>已订阅解码事件的 CANFD 实例 → 委托 映射</summary>
|
||||||
private readonly Dictionary<ZLGCANFD, Action<uint, ZDBC.DBCMessage>> _handlers = new();
|
private readonly Dictionary<CAN, Action<uint, DBCMessage>> _handlers = new();
|
||||||
|
|
||||||
public CANSignalBroadcaster(GlobalInfo globalInfo, IEventAggregator eventAggregator)
|
public CANSignalBroadcaster(GlobalInfo globalInfo, IEventAggregator eventAggregator)
|
||||||
{
|
{
|
||||||
_globalInfo = globalInfo;
|
_globalInfo = globalInfo;
|
||||||
@@ -48,7 +47,7 @@ namespace UIShare.GlobalVariable
|
|||||||
if (_handlers.ContainsKey(canfd)) continue;
|
if (_handlers.ContainsKey(canfd)) continue;
|
||||||
|
|
||||||
// 使用闭包捕获指纹,回调时即可区分不同 CAN 卡
|
// 使用闭包捕获指纹,回调时即可区分不同 CAN 卡
|
||||||
Action<uint, ZDBC.DBCMessage> handler = (channel, msg) => OnDbcMessageDecoded(fingerprint, channel, msg);
|
Action<uint, DBCMessage> handler = (channel, msg) => OnDbcMessageDecoded(fingerprint, channel, msg);
|
||||||
canfd.OnDbcMessageDecoded += handler;
|
canfd.OnDbcMessageDecoded += handler;
|
||||||
_handlers[canfd] = handler;
|
_handlers[canfd] = handler;
|
||||||
}
|
}
|
||||||
@@ -65,7 +64,7 @@ namespace UIShare.GlobalVariable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// DBC 解码回调:提取所有信号,向引用该 CAN 设备的作用域广播,并检查报警。
|
/// DBC 解码回调:提取所有信号,向引用该 CAN 设备的作用域广播,并检查报警。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnDbcMessageDecoded(string canFingerprint, uint channel, ZDBC.DBCMessage msg)
|
private void OnDbcMessageDecoded(string canFingerprint, uint channel, DBCMessage msg)
|
||||||
{
|
{
|
||||||
if (_disposed) return;
|
if (_disposed) return;
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ using System.IO;
|
|||||||
using System.IO.Ports;
|
using System.IO.Ports;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using TSMasterCAN;
|
||||||
using UIShare.PubEvent;
|
using UIShare.PubEvent;
|
||||||
using UIShare.UIViewModel;
|
using UIShare.UIViewModel;
|
||||||
using ZLGUSBCANFD;
|
using TSMasterCAN;
|
||||||
|
|
||||||
namespace UIShare.GlobalVariable
|
namespace UIShare.GlobalVariable
|
||||||
{
|
{
|
||||||
@@ -34,7 +35,7 @@ namespace UIShare.GlobalVariable
|
|||||||
|
|
||||||
/// <summary>类名 → Type 的反射缓存(仅扫描一次)。</summary>
|
/// <summary>类名 → Type 的反射缓存(仅扫描一次)。</summary>
|
||||||
private static readonly IReadOnlyDictionary<string, Type> _deviceTypeMap = BuildDeviceTypeMap();
|
private static readonly IReadOnlyDictionary<string, Type> _deviceTypeMap = BuildDeviceTypeMap();
|
||||||
public ZLGCANFD CANFD { get; set; }
|
public CAN CANFD { get; set; }
|
||||||
public IOBoardGroup IOGroup { get; set; }
|
public IOBoardGroup IOGroup { get; set; }
|
||||||
|
|
||||||
public DeviceManager(SystemConfig systemConfig, GlobalInfo globalInfo, IEventAggregator eventAggregator)
|
public DeviceManager(SystemConfig systemConfig, GlobalInfo globalInfo, IEventAggregator eventAggregator)
|
||||||
@@ -116,10 +117,10 @@ namespace UIShare.GlobalVariable
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 按指纹全局唯一创建 ZLGCANFD 实例(maxChannels 默认 4,对应 USBCANFD-400U)
|
// 按指纹全局唯一创建同星 CAN 实例(maxChannels 默认 4,对应 USBCANFD-400U)
|
||||||
// 波特率与终端电阻从 CANConfigVM 传入,初始化并启动通道 时直接使用
|
// 波特率与终端电阻从 CANConfigVM 传入,初始化并启动通道 时直接使用
|
||||||
var canLazy = _globalInfo.CanPool.GetOrAdd(fingerprint, key => new Lazy<ZLGCANFD>(() =>
|
var canLazy = _globalInfo.CanPool.GetOrAdd(fingerprint, key => new Lazy<CAN>(() =>
|
||||||
new ZLGCANFD(config.CANConfig.DeviceType, config.CANConfig.DeviceIndex, 4,
|
new CAN(config.CANConfig.DeviceType, config.CANConfig.DeviceIndex, 4,
|
||||||
config.CANConfig.ABitBaud, config.CANConfig.DBitBaud, config.CANConfig.EnableTerminalResistance)));
|
config.CANConfig.ABitBaud, config.CANConfig.DBitBaud, config.CANConfig.EnableTerminalResistance)));
|
||||||
|
|
||||||
_systemConfig.CANFD = canLazy.Value;
|
_systemConfig.CANFD = canLazy.Value;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using ZLGUSBCANFD;
|
using TSMasterCAN;
|
||||||
|
|
||||||
namespace UIShare.GlobalVariable
|
namespace UIShare.GlobalVariable
|
||||||
{
|
{
|
||||||
@@ -21,7 +21,7 @@ namespace UIShare.GlobalVariable
|
|||||||
public ConcurrentDictionary<string, Lazy<IBaseInterface>> HardwarePool { get; set; }
|
public ConcurrentDictionary<string, Lazy<IBaseInterface>> HardwarePool { get; set; }
|
||||||
|
|
||||||
/// <summary>CAN 硬件指纹 → ZLGCANFD 实例的并发池,确保同一 CAN 卡全局只创建一个驱动实例。</summary>
|
/// <summary>CAN 硬件指纹 → ZLGCANFD 实例的并发池,确保同一 CAN 卡全局只创建一个驱动实例。</summary>
|
||||||
public ConcurrentDictionary<string, Lazy<ZLGCANFD>> CanPool { get; set; }
|
public ConcurrentDictionary<string, Lazy<CAN>> CanPool { get; set; }
|
||||||
|
|
||||||
/// <summary>硬件指纹 → 正在使用该设备的作用域名称列表,用于引用计数与安全销毁。</summary>
|
/// <summary>硬件指纹 → 正在使用该设备的作用域名称列表,用于引用计数与安全销毁。</summary>
|
||||||
public ConcurrentDictionary<string, Lazy<List<string>>> DeviceAndScopeDic { get; set; }
|
public ConcurrentDictionary<string, Lazy<List<string>>> DeviceAndScopeDic { get; set; }
|
||||||
@@ -49,7 +49,7 @@ namespace UIShare.GlobalVariable
|
|||||||
ConfigDic = new();
|
ConfigDic = new();
|
||||||
ScopeDic = new();
|
ScopeDic = new();
|
||||||
HardwarePool = new ConcurrentDictionary<string, Lazy<IBaseInterface>>(StringComparer.OrdinalIgnoreCase);
|
HardwarePool = new ConcurrentDictionary<string, Lazy<IBaseInterface>>(StringComparer.OrdinalIgnoreCase);
|
||||||
CanPool = new ConcurrentDictionary<string, Lazy<ZLGCANFD>>(StringComparer.OrdinalIgnoreCase);
|
CanPool = new ConcurrentDictionary<string, Lazy<CAN>>(StringComparer.OrdinalIgnoreCase);
|
||||||
DeviceAndScopeDic = new ConcurrentDictionary<string, Lazy<List<string>>>(StringComparer.OrdinalIgnoreCase);
|
DeviceAndScopeDic = new ConcurrentDictionary<string, Lazy<List<string>>>(StringComparer.OrdinalIgnoreCase);
|
||||||
CurrentScope = "default";
|
CurrentScope = "default";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using TSMasterCAN;
|
||||||
using UIShare.UIViewModel;
|
using UIShare.UIViewModel;
|
||||||
using ZLGUSBCANFD;
|
|
||||||
using static UIShare.UIViewModel.ParameterVM;
|
using static UIShare.UIViewModel.ParameterVM;
|
||||||
|
|
||||||
namespace UIShare.GlobalVariable
|
namespace UIShare.GlobalVariable
|
||||||
@@ -40,7 +40,7 @@ namespace UIShare.GlobalVariable
|
|||||||
/// 监测通道持久化配置列表(包含 Fingerprint、MethodName、IsDisplayed)
|
/// 监测通道持久化配置列表(包含 Fingerprint、MethodName、IsDisplayed)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ObservableCollection<MonitorChannelConfig> MonitorChannels = new();
|
public ObservableCollection<MonitorChannelConfig> MonitorChannels = new();
|
||||||
public ZLGCANFD CANFD = new();
|
public CAN CANFD = new();
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public ObservableCollection<ParameterVM> ParameterList = new()
|
public ObservableCollection<ParameterVM> ParameterList = new()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,6 +23,6 @@
|
|||||||
<ProjectReference Include="..\Logger\Logger.csproj" />
|
<ProjectReference Include="..\Logger\Logger.csproj" />
|
||||||
<ProjectReference Include="..\ORM\ORM.csproj" />
|
<ProjectReference Include="..\ORM\ORM.csproj" />
|
||||||
<ProjectReference Include="..\Service\Service.csproj" />
|
<ProjectReference Include="..\Service\Service.csproj" />
|
||||||
<ProjectReference Include="..\ZLGUSBCANFD\ZLGUSBCANFD.csproj" />
|
<ProjectReference Include="..\TSMasterCAN\TSMasterCAN.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -7,7 +7,10 @@
|
|||||||
private int _报文ID;
|
private int _报文ID;
|
||||||
private double _时间戳;
|
private double _时间戳;
|
||||||
private byte _长度;
|
private byte _长度;
|
||||||
|
private bool _FD;
|
||||||
|
private bool _IsTx;
|
||||||
|
private byte[] _Bytes = new byte[64];
|
||||||
|
private string _报文 = "[]";
|
||||||
|
|
||||||
// 通道属性
|
// 通道属性
|
||||||
public byte 通道
|
public byte 通道
|
||||||
@@ -37,6 +40,32 @@
|
|||||||
set { SetProperty(ref _长度, value); }
|
set { SetProperty(ref _长度, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 是否FD帧
|
||||||
|
public bool FD
|
||||||
|
{
|
||||||
|
get { return _FD; }
|
||||||
|
set { SetProperty(ref _FD, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否发送帧
|
||||||
|
public bool IsTx
|
||||||
|
{
|
||||||
|
get { return _IsTx; }
|
||||||
|
set { SetProperty(ref _IsTx, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原始字节数据
|
||||||
|
public byte[] Bytes
|
||||||
|
{
|
||||||
|
get { return _Bytes; }
|
||||||
|
set { SetProperty(ref _Bytes, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 报文十六进制字符串
|
||||||
|
public string 报文
|
||||||
|
{
|
||||||
|
get { return _报文; }
|
||||||
|
set { SetProperty(ref _报文, value); }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,983 +0,0 @@
|
|||||||
using Common.Attributes;
|
|
||||||
using Logger;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace ZLGUSBCANFD
|
|
||||||
{
|
|
||||||
[ACPCommand]
|
|
||||||
public class ZLGCANFD : IDisposable
|
|
||||||
{
|
|
||||||
// 硬件设备与通道句柄
|
|
||||||
private IntPtr _deviceHandle = IntPtr.Zero;
|
|
||||||
private readonly IntPtr[] _channelHandles;
|
|
||||||
|
|
||||||
// 每个通道分配独立的 DBC 引擎句柄与锁,彻底解决台架间DBC冲突与锁竞争
|
|
||||||
private readonly uint[] _dbcHandles;
|
|
||||||
private readonly bool[] _isDbcLoadedArray;
|
|
||||||
private readonly object[] _channelLocks; // 通道级细粒度锁
|
|
||||||
|
|
||||||
// 异步高性能接收线程控制
|
|
||||||
private volatile bool _isRunning = false;
|
|
||||||
private volatile bool _isClosing = false; // 关闭流程标志:通知循环发送任务尽快退出
|
|
||||||
private readonly List<Thread> _receiveThreads = new List<Thread>();
|
|
||||||
|
|
||||||
// DBC 循环发送任务管理:Key = (通道号, 帧ID),Value = (CTS, Task)
|
|
||||||
private readonly ConcurrentDictionary<(uint 通道号, uint 帧ID), (CancellationTokenSource Cts, Task SendTask)> _cyclicSenders = new ConcurrentDictionary<(uint, uint), (CancellationTokenSource, Task)>();
|
|
||||||
|
|
||||||
// 信号值持久化覆盖表:Key = (通道号, 帧ID),Value = 信号名 → 物理值
|
|
||||||
// 每次 设置报文 会累积写入此表,发送时以 DBC 初始值为底再叠加此表覆盖值
|
|
||||||
private readonly ConcurrentDictionary<(uint 通道号, uint 帧ID), Dictionary<string, double>> _signalOverrides = new ConcurrentDictionary<(uint, uint), Dictionary<string, double>>();
|
|
||||||
|
|
||||||
// 动态硬件参数
|
|
||||||
private readonly uint _deviceType; // 76: USBCANFD-400U
|
|
||||||
private readonly uint _deviceIndex; // 设备索引
|
|
||||||
private readonly int _maxChannels; // 动态通道数
|
|
||||||
|
|
||||||
// 通道波特率与终端电阻配置(构造时传入,所有通道共用)
|
|
||||||
private readonly string _abitBaud;
|
|
||||||
private readonly string _dbitBaud;
|
|
||||||
private readonly bool _enableTerminalResistance;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 当前 CAN 卡专属的 DBC 解析器,按通道隔离报文数据库。
|
|
||||||
/// </summary>
|
|
||||||
public ZLGDBCParser DBCParser { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 事件:当接收到 CAN/CANFD 报文并且通过 DBC 成功解析后触发
|
|
||||||
/// 参数:uint 通道号 (0,1,2,3), ZDBC.DBCMessage 解析后的DBC消息结构体
|
|
||||||
/// </summary>
|
|
||||||
public event Action<uint, ZDBC.DBCMessage>? OnDbcMessageDecoded;
|
|
||||||
|
|
||||||
public ZLGCANFD(uint deviceType = 76, uint deviceIndex = 0, int maxChannels = 4,
|
|
||||||
string abitBaud = "500000", string dbitBaud = "2000000", bool enableTerminalResistance = true)
|
|
||||||
{
|
|
||||||
_deviceType = deviceType;
|
|
||||||
_deviceIndex = deviceIndex;
|
|
||||||
_maxChannels = maxChannels;
|
|
||||||
_abitBaud = abitBaud;
|
|
||||||
_dbitBaud = dbitBaud;
|
|
||||||
_enableTerminalResistance = enableTerminalResistance;
|
|
||||||
|
|
||||||
// 初始化通道相关状态数组
|
|
||||||
_channelHandles = new IntPtr[_maxChannels];
|
|
||||||
_dbcHandles = new uint[_maxChannels];
|
|
||||||
_isDbcLoadedArray = new bool[_maxChannels];
|
|
||||||
_channelLocks = new object[_maxChannels];
|
|
||||||
DBCParser = new ZLGDBCParser(_maxChannels);
|
|
||||||
|
|
||||||
for (int i = 0; i < _maxChannels; i++)
|
|
||||||
{
|
|
||||||
_channelHandles[i] = IntPtr.Zero;
|
|
||||||
_dbcHandles[i] = 0;
|
|
||||||
_isDbcLoadedArray[i] = false;
|
|
||||||
_channelLocks[i] = new object();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region 1. 硬件连接与通道初始化
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 仅仅打开设备,不做具体的通道波特率配置(留给具体的台架去分别配置)
|
|
||||||
/// </summary>
|
|
||||||
public virtual bool 打开设备()
|
|
||||||
{
|
|
||||||
if (_deviceHandle != IntPtr.Zero) return true;
|
|
||||||
_deviceHandle = ZLGCAN.ZCAN_OpenDevice(ZLGCAN.ZCAN_USBCANFD_400U, _deviceIndex, 0);
|
|
||||||
return _deviceHandle != IntPtr.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 针对特定通道进行参数初始化并启动(使用构造时传入的波特率与终端电阻配置)
|
|
||||||
/// </summary>
|
|
||||||
public virtual bool 初始化并启动通道(uint 通道号)
|
|
||||||
{
|
|
||||||
_isClosing = false; // 重置关闭标志,允许循环发送
|
|
||||||
if (_deviceHandle == IntPtr.Zero) throw new InvalidOperationException("请先调用 '打开设备()' 才能初始化通道。");
|
|
||||||
if (通道号 >= _maxChannels) return false;
|
|
||||||
|
|
||||||
lock (_channelLocks[通道号])
|
|
||||||
{
|
|
||||||
// 如果已经启动过,直接退出
|
|
||||||
if (_channelHandles[通道号] != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
//ZLGCAN.ZCAN_ResetCAN(_channelHandles[通道号]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. 设置该通道专属的仲裁域与数据域波特率
|
|
||||||
if (ZLGCAN.ZCAN_SetValue(_deviceHandle, $"{通道号}/canfd_abit_baud_rate", _abitBaud) != 1) return false;
|
|
||||||
if (ZLGCAN.ZCAN_SetValue(_deviceHandle, $"{通道号}/canfd_dbit_baud_rate", _dbitBaud) != 1) return false;
|
|
||||||
|
|
||||||
// 2. 设置该通道专属内部终端电阻状态
|
|
||||||
string resistanceStr = _enableTerminalResistance ? "1" : "0";
|
|
||||||
if (ZLGCAN.ZCAN_SetValue(_deviceHandle, $"{通道号}/initenal_resistance", resistanceStr) != 1) return false;
|
|
||||||
|
|
||||||
// 3. 规范配置通道结构体
|
|
||||||
ZLGCAN.ZCAN_CHANNEL_INIT_CONFIG config = new ZLGCAN.ZCAN_CHANNEL_INIT_CONFIG();
|
|
||||||
config.can_type = 1; // 1 代表 CANFD 模式
|
|
||||||
config.config.canfd.mode = 0; // 0 代表正常工作模式
|
|
||||||
|
|
||||||
IntPtr pConfig = Marshal.AllocHGlobal(Marshal.SizeOf(config));
|
|
||||||
Marshal.StructureToPtr(config, pConfig, true);
|
|
||||||
_channelHandles[通道号] = ZLGCAN.ZCAN_InitCAN(_deviceHandle, 通道号, pConfig);
|
|
||||||
Marshal.FreeHGlobal(pConfig);
|
|
||||||
|
|
||||||
if (_channelHandles[通道号] == IntPtr.Zero) return false;
|
|
||||||
|
|
||||||
// 4. 启动 CAN 通道
|
|
||||||
if (ZLGCAN.ZCAN_StartCAN(_channelHandles[通道号]) != 1) return false;
|
|
||||||
|
|
||||||
// 5. 为该通道启动专属的独立后台高性能轮询接收线程(如果尚未启动轮询)
|
|
||||||
if (!_isRunning) _isRunning = true;
|
|
||||||
|
|
||||||
int chnIdx = (int)通道号;
|
|
||||||
Thread rxThread = new Thread(() => 接收轮询核心(_channelHandles[chnIdx], (uint)chnIdx))
|
|
||||||
{
|
|
||||||
IsBackground = true,
|
|
||||||
Name = $"ZLGCANFD_Dev{_deviceIndex}_CH{chnIdx}_RxThread"
|
|
||||||
};
|
|
||||||
_receiveThreads.Add(rxThread);
|
|
||||||
rxThread.Start();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void 关闭CAN卡设备()
|
|
||||||
{
|
|
||||||
_isClosing = true; // 通知循环发送任务尽快退出
|
|
||||||
_isRunning = false; // 通知接收轮询线程退出
|
|
||||||
停止所有循环发送(); // 内部会等待所有循环发送 Task 退出
|
|
||||||
|
|
||||||
// 等待所有接收轮询线程真正退出(每个最多 1 秒)
|
|
||||||
foreach (var thread in _receiveThreads)
|
|
||||||
{
|
|
||||||
if (thread.IsAlive)
|
|
||||||
thread.Join(1000);
|
|
||||||
}
|
|
||||||
_receiveThreads.Clear();
|
|
||||||
|
|
||||||
// 动态复位所有通道并释放 DBC(使用 TryEnter 防止死锁)
|
|
||||||
for (uint i = 0; i < _maxChannels; i++)
|
|
||||||
{
|
|
||||||
if (Monitor.TryEnter(_channelLocks[i], TimeSpan.FromSeconds(2)))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_channelHandles[i] != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
ZLGCAN.ZCAN_ResetCAN(_channelHandles[i]);
|
|
||||||
_channelHandles[i] = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
释放通道DBC(i);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Monitor.Exit(_channelLocks[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 锁超时:循环发送 Task 可能仍阻塞在原生 API 调用中,强制清理
|
|
||||||
LoggerHelper.Info($"[ZLGCANFD] 关闭通道 {i} 时获取锁超时,强制清理");
|
|
||||||
if (_channelHandles[i] != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
try { ZLGCAN.ZCAN_ResetCAN(_channelHandles[i]); } catch { }
|
|
||||||
_channelHandles[i] = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
释放通道DBC(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭设备主句柄
|
|
||||||
if (_deviceHandle != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
ZLGCAN.ZCAN_CloseDevice(_deviceHandle);
|
|
||||||
_deviceHandle = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
_isClosing = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 2. DBC 矩阵文件操作
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 指定通道加载特定的 DBC 文件(支持不同通道加载不同的DBC矩阵)
|
|
||||||
/// </summary>
|
|
||||||
public virtual bool 加载通道DBC文件(uint 通道号, string dbcFilePath)
|
|
||||||
{
|
|
||||||
if (通道号 >= _maxChannels) return false;
|
|
||||||
|
|
||||||
lock (_channelLocks[通道号])
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_isDbcLoadedArray[通道号]) return true;
|
|
||||||
//加载前先卸载
|
|
||||||
释放通道DBC(通道号);
|
|
||||||
uint chDbcHandle = ZDBC.ZDBC_Init();
|
|
||||||
if (chDbcHandle == 0) return false;
|
|
||||||
|
|
||||||
IntPtr ptrPath = Marshal.StringToHGlobalAnsi(dbcFilePath);
|
|
||||||
bool success = ZDBC.ZDBC_LoadFile(chDbcHandle, ptrPath);
|
|
||||||
Marshal.FreeHGlobal(ptrPath);
|
|
||||||
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
_dbcHandles[通道号] = chDbcHandle;
|
|
||||||
_isDbcLoadedArray[通道号] = true;
|
|
||||||
DBCParser.ParseAndLoadToDatabase(chDbcHandle, (int)通道号);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ZDBC.ZDBC_Release(chDbcHandle);
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void 释放通道DBC(uint 通道号)
|
|
||||||
{
|
|
||||||
if (通道号 >= _maxChannels) return;
|
|
||||||
|
|
||||||
lock (_channelLocks[通道号])
|
|
||||||
{
|
|
||||||
if (_isDbcLoadedArray[通道号] && _dbcHandles[通道号] != 0)
|
|
||||||
{
|
|
||||||
ZDBC.ZDBC_Release(_dbcHandles[通道号]);
|
|
||||||
_dbcHandles[通道号] = 0;
|
|
||||||
_isDbcLoadedArray[通道号] = false;
|
|
||||||
DBCParser.MsgDatabase[(int)通道号].Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 3. 轮询接收与实时自动化 DBC 解码
|
|
||||||
|
|
||||||
private void 接收轮询核心(IntPtr channelHandle, uint channelIndex)
|
|
||||||
{
|
|
||||||
const int bufferSize = 100;
|
|
||||||
int canStructSize = Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Receive_Data));
|
|
||||||
int canfdStructSize = Marshal.SizeOf(typeof(ZLGCAN.ZCAN_ReceiveFD_Data));
|
|
||||||
int dbcMsgSize = Marshal.SizeOf(typeof(ZDBC.DBCMessage));
|
|
||||||
|
|
||||||
IntPtr ptrCanBuffer = Marshal.AllocHGlobal(canStructSize * bufferSize);
|
|
||||||
IntPtr ptrCanFDBuffer = Marshal.AllocHGlobal(canfdStructSize * bufferSize);
|
|
||||||
IntPtr ptrDbcMsg = Marshal.AllocHGlobal(dbcMsgSize);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (_isRunning)
|
|
||||||
{
|
|
||||||
bool currentTurnHasData = false;
|
|
||||||
|
|
||||||
// 1. 自动提取并解析经典 CAN 帧
|
|
||||||
uint canNum = ZLGCAN.ZCAN_GetReceiveNum(channelHandle, 0);
|
|
||||||
if (canNum > 0)
|
|
||||||
{
|
|
||||||
uint actualRecv = ZLGCAN.ZCAN_Receive(channelHandle, ptrCanBuffer, bufferSize, 5);
|
|
||||||
for (int i = 0; i < actualRecv; i++)
|
|
||||||
{
|
|
||||||
IntPtr framePtr = IntPtr.Add(ptrCanBuffer, i * canStructSize);
|
|
||||||
|
|
||||||
// 只锁定当前通道的DBC句柄进行解码,高并发完全不卡顿
|
|
||||||
lock (_channelLocks[channelIndex])
|
|
||||||
{
|
|
||||||
if (_isDbcLoadedArray[channelIndex] && ZDBC.ZDBC_Decode(_dbcHandles[channelIndex], ptrDbcMsg, framePtr, 1, 0))
|
|
||||||
{
|
|
||||||
var msg = (ZDBC.DBCMessage)Marshal.PtrToStructure(ptrDbcMsg, typeof(ZDBC.DBCMessage));
|
|
||||||
OnDbcMessageDecoded?.Invoke(channelIndex, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
currentTurnHasData = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 自动提取并解析高速 CANFD 帧
|
|
||||||
uint canfdNum = ZLGCAN.ZCAN_GetReceiveNum(channelHandle, 1);
|
|
||||||
if (canfdNum > 0)
|
|
||||||
{
|
|
||||||
uint actualRecvFd = ZLGCAN.ZCAN_ReceiveFD(channelHandle, ptrCanFDBuffer, bufferSize, 5);
|
|
||||||
for (int i = 0; i < actualRecvFd; i++)
|
|
||||||
{
|
|
||||||
IntPtr framePtr = IntPtr.Add(ptrCanFDBuffer, i * canfdStructSize);
|
|
||||||
|
|
||||||
lock (_channelLocks[channelIndex])
|
|
||||||
{
|
|
||||||
if (_isDbcLoadedArray[channelIndex] && ZDBC.ZDBC_Decode(_dbcHandles[channelIndex], ptrDbcMsg, framePtr, 1, 1))
|
|
||||||
{
|
|
||||||
var msg = (ZDBC.DBCMessage)Marshal.PtrToStructure(ptrDbcMsg, typeof(ZDBC.DBCMessage));
|
|
||||||
OnDbcMessageDecoded?.Invoke(channelIndex, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
currentTurnHasData = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!currentTurnHasData)
|
|
||||||
{
|
|
||||||
Thread.Sleep(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(ptrCanBuffer);
|
|
||||||
Marshal.FreeHGlobal(ptrCanFDBuffer);
|
|
||||||
Marshal.FreeHGlobal(ptrDbcMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 4. DBC 自动打包与智能报文发送
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 发送一条 DBC 定义报文。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="通道号">通道号</param>
|
|
||||||
/// <param name="帧ID">DBC 中定义的帧 ID</param>
|
|
||||||
/// <param name="信号物理值字典">信号名称 → 物理值;只设置字典中包含的信号,其余信号保持 DBC 默认值</param>
|
|
||||||
/// <param name="循环间隔毫秒">0 = 只发送一次;>0 = 按指定间隔循环发送(毫秒)</param>
|
|
||||||
/// <param name="是否使用CANFD">1 = CANFD,0 = CAN</param>
|
|
||||||
/// <returns>是否成功启动/发送</returns>
|
|
||||||
public virtual bool 发送DBC定义报文(
|
|
||||||
uint 通道号,
|
|
||||||
uint 帧ID,
|
|
||||||
Dictionary<string, double> 信号物理值字典,
|
|
||||||
int 循环间隔毫秒 = 0,
|
|
||||||
int 是否使用CANFD = 1)
|
|
||||||
{
|
|
||||||
if (通道号 >= _maxChannels || _channelHandles[通道号] == IntPtr.Zero) return false;
|
|
||||||
if (!_isDbcLoadedArray[通道号]) throw new InvalidOperationException($"通道 {通道号} 的 DBC 协议未加载,无法执行打包发送指令。");
|
|
||||||
if (循环间隔毫秒 < 0) throw new ArgumentOutOfRangeException(nameof(循环间隔毫秒), "循环间隔必须大于或等于 0。");
|
|
||||||
if (信号物理值字典 == null || 信号物理值字典.Count == 0)
|
|
||||||
throw new ArgumentException("信号物理值字典不能为空。", nameof(信号物理值字典));
|
|
||||||
|
|
||||||
if (循环间隔毫秒 == 0)
|
|
||||||
{
|
|
||||||
// 单次发送:同步执行
|
|
||||||
lock (_channelLocks[通道号])
|
|
||||||
{
|
|
||||||
return 发送DBC定义报文单次(通道号, 帧ID, 信号物理值字典, 是否使用CANFD);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 循环发送:先停止同通道同帧 ID 的旧循环,再启动新循环
|
|
||||||
停止循环发送(通道号, 帧ID);
|
|
||||||
|
|
||||||
var cts = new CancellationTokenSource();
|
|
||||||
var sendTask = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
while (!cts.Token.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
if (_isClosing) break; // 关闭流程中立即退出
|
|
||||||
try
|
|
||||||
{
|
|
||||||
lock (_channelLocks[通道号])
|
|
||||||
{
|
|
||||||
if (_channelHandles[通道号] == IntPtr.Zero || _isClosing) break;
|
|
||||||
发送DBC定义报文单次(通道号, 帧ID, 信号物理值字典, 是否使用CANFD);
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.Delay(循环间隔毫秒, cts.Token);
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LoggerHelper.Error($"[ZLGCANFD] 循环发送 DBC 报文失败: {ex.Message}");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清理:仅当字典中存的仍然是本任务创建的 CTS 时才移除,避免误删新任务的 CTS
|
|
||||||
if (_cyclicSenders.TryGetValue((通道号, 帧ID), out var current) && ReferenceEquals(current.Cts, cts))
|
|
||||||
{
|
|
||||||
_cyclicSenders.TryRemove((通道号, 帧ID), out _);
|
|
||||||
}
|
|
||||||
cts.Dispose();
|
|
||||||
}, cts.Token);
|
|
||||||
|
|
||||||
_cyclicSenders[(通道号, 帧ID)] = (cts, sendTask);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 停止指定通道、指定帧 ID 的循环发送。
|
|
||||||
/// </summary>
|
|
||||||
public virtual void 停止循环发送(uint 通道号, uint 帧ID)
|
|
||||||
{
|
|
||||||
if (_cyclicSenders.TryRemove((通道号, 帧ID), out var entry))
|
|
||||||
{
|
|
||||||
entry.Cts.Cancel();
|
|
||||||
// 不在这里 Dispose,由 Task 的清理代码负责释放,避免 Task 仍在使用已释放的 Token
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 停止所有 DBC 循环发送任务。
|
|
||||||
/// </summary>
|
|
||||||
public virtual void 停止所有循环发送()
|
|
||||||
{
|
|
||||||
var entries = _cyclicSenders.ToArray();
|
|
||||||
|
|
||||||
// 1. 取消所有 CTS(不 Dispose,由 Task 清理代码负责)
|
|
||||||
foreach (var kvp in entries)
|
|
||||||
{
|
|
||||||
kvp.Value.Cts.Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 等待所有循环发送 Task 退出(最多 3 秒)
|
|
||||||
var tasks = new List<Task>();
|
|
||||||
foreach (var kvp in entries)
|
|
||||||
{
|
|
||||||
if (!kvp.Value.SendTask.IsCompleted)
|
|
||||||
tasks.Add(kvp.Value.SendTask);
|
|
||||||
}
|
|
||||||
if (tasks.Count > 0)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Task.WaitAll(tasks.ToArray(), TimeSpan.FromSeconds(3));
|
|
||||||
}
|
|
||||||
catch { /* 忽略等待异常 */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
_cyclicSenders.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 清除指定通道、指定帧 ID 的信号覆盖值,恢复为 DBC 初始值。
|
|
||||||
/// </summary>
|
|
||||||
public virtual void 清除信号覆盖(uint 通道号, uint 帧ID)
|
|
||||||
{
|
|
||||||
if (_signalOverrides.TryRemove((通道号, 帧ID), out var dict))
|
|
||||||
{
|
|
||||||
lock (dict) dict.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 清除指定通道所有帧的信号覆盖值。
|
|
||||||
/// </summary>
|
|
||||||
public virtual void 清除通道信号覆盖(uint 通道号)
|
|
||||||
{
|
|
||||||
foreach (var key in _signalOverrides.Keys)
|
|
||||||
{
|
|
||||||
if (key.通道号 == 通道号 && _signalOverrides.TryRemove(key, out var dict))
|
|
||||||
{
|
|
||||||
lock (dict) dict.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 单次发送 DBC 报文(调用方已持有通道锁)。
|
|
||||||
/// </summary>
|
|
||||||
private bool 发送DBC定义报文单次(uint 通道号, uint 帧ID, Dictionary<string, double> 信号物理值字典, int 是否使用CANFD)
|
|
||||||
{
|
|
||||||
uint chDbcHandle = _dbcHandles[通道号];
|
|
||||||
|
|
||||||
IntPtr ptrDbcMsg = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCMessage)));
|
|
||||||
IntPtr ptrCount = Marshal.AllocHGlobal(sizeof(uint));
|
|
||||||
Marshal.WriteInt32(ptrCount, 1);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!ZDBC.ZDBC_GetMessageById(chDbcHandle, 帧ID, ptrDbcMsg)) return false;
|
|
||||||
var msg = (ZDBC.DBCMessage)Marshal.PtrToStructure(ptrDbcMsg, typeof(ZDBC.DBCMessage));
|
|
||||||
|
|
||||||
// 根据传入的物理值设置对应信号的原始值
|
|
||||||
for (int i = 0; i < msg.nSignalCount; i++)
|
|
||||||
{
|
|
||||||
var signal = msg.vSignals[i];
|
|
||||||
string signalName = Encoding.Default.GetString(signal.strName).TrimEnd('\0');
|
|
||||||
if (信号物理值字典.TryGetValue(signalName, out double physicalValue))
|
|
||||||
{
|
|
||||||
signal.nRawvalue = 物理值转原始值(signal, physicalValue);
|
|
||||||
msg.vSignals[i] = signal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Marshal.StructureToPtr(msg, ptrDbcMsg, true);
|
|
||||||
|
|
||||||
if (是否使用CANFD == 0)
|
|
||||||
{
|
|
||||||
IntPtr ptrCanFrame = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.can_frame)));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!ZDBC.ZDBC_Encode(chDbcHandle, ptrCanFrame, ptrCount, ptrDbcMsg, 0)) return false;
|
|
||||||
|
|
||||||
ZLGCAN.can_frame canFrame = (ZLGCAN.can_frame)Marshal.PtrToStructure(ptrCanFrame, typeof(ZLGCAN.can_frame));
|
|
||||||
canFrame.__pad |= 0x20;
|
|
||||||
|
|
||||||
ZLGCAN.ZCAN_Transmit_Data txData = new ZLGCAN.ZCAN_Transmit_Data { frame = canFrame, transmit_type = 2 };
|
|
||||||
IntPtr pTx = Marshal.AllocHGlobal(Marshal.SizeOf(txData));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Marshal.StructureToPtr(txData, pTx, true);
|
|
||||||
return ZLGCAN.ZCAN_Transmit(_channelHandles[通道号], pTx, 1) == 1;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(pTx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(ptrCanFrame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IntPtr ptrCanFDFrame = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.canfd_frame)));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!ZDBC.ZDBC_Encode(chDbcHandle, ptrCanFDFrame, ptrCount, ptrDbcMsg, 1)) return false;
|
|
||||||
|
|
||||||
ZLGCAN.canfd_frame canfdFrame = (ZLGCAN.canfd_frame)Marshal.PtrToStructure(ptrCanFDFrame, typeof(ZLGCAN.canfd_frame));
|
|
||||||
canfdFrame.flags |= 0x20;
|
|
||||||
|
|
||||||
ZLGCAN.ZCAN_TransmitFD_Data txFdData = new ZLGCAN.ZCAN_TransmitFD_Data { frame = canfdFrame, transmit_type = 2 };
|
|
||||||
IntPtr pTxFd = Marshal.AllocHGlobal(Marshal.SizeOf(txFdData));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Marshal.StructureToPtr(txFdData, pTxFd, true);
|
|
||||||
return ZLGCAN.ZCAN_TransmitFD(_channelHandles[通道号], pTxFd, 1) == 1;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(pTxFd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(ptrCanFDFrame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(ptrDbcMsg);
|
|
||||||
Marshal.FreeHGlobal(ptrCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 不依赖通道锁的物理值到原始值转换。
|
|
||||||
/// </summary>
|
|
||||||
private static ulong 物理值转原始值(ZDBC.DBCSignal 信号定义, double 实际物理值)
|
|
||||||
{
|
|
||||||
IntPtr pSignal = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCSignal)));
|
|
||||||
IntPtr pValue = Marshal.AllocHGlobal(sizeof(double));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Marshal.StructureToPtr(信号定义, pSignal, true);
|
|
||||||
Marshal.StructureToPtr(实际物理值, pValue, true);
|
|
||||||
return ZDBC.ZDBC_CalcRawValue(pSignal, pValue);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(pSignal);
|
|
||||||
Marshal.FreeHGlobal(pValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 4.1 报文设置与发送 (支持智能进制转换)
|
|
||||||
/// <summary>
|
|
||||||
/// 设置并发送指定信号值(帧 ID 支持 string 兼容模式)。
|
|
||||||
/// 其余未指定信号使用 DBC 中定义的初始值填充,避免总线数据被意外清零。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="通道号">CAN/CANFD 通道索引</param>
|
|
||||||
/// <param name="帧IDStr">对应报文的帧 ID(可以是 "26"、"0x1A"、"1A")</param>
|
|
||||||
/// <param name="信号名称">DBC 中定义的英文信号名称(不区分大小写)</param>
|
|
||||||
/// <param name="物理值">要写入的实际物理数值</param>
|
|
||||||
/// <param name="循环发送间隔毫秒">0 = 单次发送;>0 = 周期循环发送(单位毫秒)</param>
|
|
||||||
/// <returns>操作是否成功</returns>
|
|
||||||
public virtual bool 设置报文(uint 通道号, string 帧IDStr, string 信号名称, double 物理值, int 循环发送间隔毫秒 = 0, bool 直接发送 = false)
|
|
||||||
{
|
|
||||||
if (!TryParseFrameId(帧IDStr, out uint 帧ID))
|
|
||||||
{
|
|
||||||
Console.WriteLine($"[ZLGCANFD] 设置报文失败: 无法解析的帧 ID '{帧IDStr}'");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (通道号 >= _maxChannels || _channelHandles[通道号] == IntPtr.Zero) return false;
|
|
||||||
if (!_isDbcLoadedArray[通道号]) return false;
|
|
||||||
if (string.IsNullOrWhiteSpace(信号名称)) return false;
|
|
||||||
|
|
||||||
// 将本次设置的信号值写入持久化覆盖表(累积,不会丢失之前设置的值)
|
|
||||||
var overrides = _signalOverrides.GetOrAdd((通道号, 帧ID), _ => new Dictionary<string, double>(StringComparer.OrdinalIgnoreCase));
|
|
||||||
lock (overrides)
|
|
||||||
{
|
|
||||||
overrides[信号名称] = 物理值;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构建发送字典:DBC 初始值 + 持久化覆盖表(已包含本次设置)
|
|
||||||
var merged = 构建报文发送字典(通道号, 帧ID);
|
|
||||||
|
|
||||||
if (直接发送) return 发送DBC定义报文(通道号, 帧ID, merged, 循环发送间隔毫秒);
|
|
||||||
else return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 发送报文:以 DBC 中所有信号的初始值作为默认值发送(帧 ID 支持 string 兼容模式)。
|
|
||||||
/// 如果之前调用 设置报文 保存过覆盖值,则会优先使用覆盖值。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="通道号">CAN/CANFD 通道索引</param>
|
|
||||||
/// <param name="帧IDStr">DBC 中定义的帧 ID(可以是 "26"、"0x1A"、"1A")</param>
|
|
||||||
/// <param name="循环发送间隔毫秒">0 = 单次发送;>0 = 周期循环发送(毫秒)</param>
|
|
||||||
/// <param name="是否使用CANFD">1 = 以 CANFD 格式发送;0 = 经典 CAN 格式</param>
|
|
||||||
public virtual bool 发送报文(uint 通道号, string 帧IDStr, int 循环发送间隔毫秒 = 0, int 是否使用CANFD = 1)
|
|
||||||
{
|
|
||||||
if (!TryParseFrameId(帧IDStr, out uint 帧ID))
|
|
||||||
{
|
|
||||||
Console.WriteLine($"[ZLGCANFD] 发送报文失败: 无法解析的帧 ID '{帧IDStr}'");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (通道号 >= _maxChannels || _channelHandles[通道号] == IntPtr.Zero) return false;
|
|
||||||
if (!_isDbcLoadedArray[通道号]) return false;
|
|
||||||
if (循环发送间隔毫秒 < 0) throw new ArgumentOutOfRangeException(nameof(循环发送间隔毫秒));
|
|
||||||
|
|
||||||
var merged = 构建报文发送字典(通道号, 帧ID);
|
|
||||||
return 发送DBC定义报文(通道号, 帧ID, merged, 循环发送间隔毫秒, 是否使用CANFD);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 发送自定义报文(原始字节,帧 ID 支持 string 兼容模式)。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="通道号">CAN/CANFD 通道索引</param>
|
|
||||||
/// <param name="帧IDStr">帧 ID(可以是 "26"、"0x1A"、"1A");扩展帧会自动置位 0x80000000</param>
|
|
||||||
/// <param name="原始数据">原始帧数据</param>
|
|
||||||
/// <param name="dlcLength">数据长度;CANFD 仅允许 0-8,12,16,20,24,32,48,64</param>
|
|
||||||
/// <param name="是否是扩展帧">是否为 29 位扩展帧</param>
|
|
||||||
/// <param name="是否是CANFD">true = CANFD;false = 经典 CAN</param>
|
|
||||||
/// <param name="开启波特率加速BRS">CANFD 是否开启波特率加速</param>
|
|
||||||
public virtual bool 发送自定义报文(
|
|
||||||
uint 通道号,
|
|
||||||
string 帧IDStr,
|
|
||||||
byte[] 原始数据,
|
|
||||||
byte dlcLength,
|
|
||||||
bool 是否是扩展帧 = false,
|
|
||||||
bool 是否是CANFD = true,
|
|
||||||
bool 开启波特率加速BRS = true)
|
|
||||||
{
|
|
||||||
if (!TryParseFrameId(帧IDStr, out uint 帧ID))
|
|
||||||
{
|
|
||||||
Console.WriteLine($"[ZLGCANFD] 发送自定义报文失败: 无法解析的帧 ID '{帧IDStr}'");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (通道号 >= _maxChannels || _channelHandles[通道号] == IntPtr.Zero) return false;
|
|
||||||
if (原始数据 == null) return false;
|
|
||||||
|
|
||||||
// 扩展帧需要把最高位标志位置起来
|
|
||||||
uint canId = 帧ID & 0x7FFFFFFF;
|
|
||||||
if (是否是扩展帧) canId |= 0x80000000;
|
|
||||||
|
|
||||||
lock (_channelLocks[通道号])
|
|
||||||
{
|
|
||||||
if (是否是CANFD)
|
|
||||||
{
|
|
||||||
// CANFD 有效 DLC 校验
|
|
||||||
byte validFdDlc = 校验CANFD的Dlc(dlcLength);
|
|
||||||
|
|
||||||
ZLGCAN.canfd_frame fdFrame = new ZLGCAN.canfd_frame
|
|
||||||
{
|
|
||||||
can_id = canId,
|
|
||||||
len = validFdDlc,
|
|
||||||
flags = (byte)((是否是扩展帧 ? 0x01 : 0x00) | (开启波特率加速BRS ? 0x02 : 0x00) | 0x20), // 0x20 本地回显
|
|
||||||
data = new byte[64]
|
|
||||||
};
|
|
||||||
Array.Copy(原始数据, 0, fdFrame.data, 0, Math.Min(原始数据.Length, 64));
|
|
||||||
|
|
||||||
ZLGCAN.ZCAN_TransmitFD_Data txFdData = new ZLGCAN.ZCAN_TransmitFD_Data { frame = fdFrame, transmit_type = 2 };
|
|
||||||
IntPtr pTxFd = Marshal.AllocHGlobal(Marshal.SizeOf(txFdData));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Marshal.StructureToPtr(txFdData, pTxFd, true);
|
|
||||||
return ZLGCAN.ZCAN_TransmitFD(_channelHandles[通道号], pTxFd, 1) == 1;
|
|
||||||
}
|
|
||||||
finally { Marshal.FreeHGlobal(pTxFd); }
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ZLGCAN.can_frame standardFrame = new ZLGCAN.can_frame
|
|
||||||
{
|
|
||||||
can_id = canId,
|
|
||||||
can_dlc = dlcLength > 8 ? (byte)8 : dlcLength,
|
|
||||||
__pad = 0x20, // 发送回显
|
|
||||||
data = new byte[8]
|
|
||||||
};
|
|
||||||
Array.Copy(原始数据, 0, standardFrame.data, 0, Math.Min(原始数据.Length, 8));
|
|
||||||
|
|
||||||
ZLGCAN.ZCAN_Transmit_Data txData = new ZLGCAN.ZCAN_Transmit_Data { frame = standardFrame, transmit_type = 2 };
|
|
||||||
IntPtr pTx = Marshal.AllocHGlobal(Marshal.SizeOf(txData));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Marshal.StructureToPtr(txData, pTx, true);
|
|
||||||
return ZLGCAN.ZCAN_Transmit(_channelHandles[通道号], pTx, 1) == 1;
|
|
||||||
}
|
|
||||||
finally { Marshal.FreeHGlobal(pTx); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 直接发送 8 字节原始报文(经典 CAN 或 CANFD 均可)。
|
|
||||||
/// 无需加载 DBC,适合手动构造简单报文。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="通道号">通道号(0 ~ MaxChannels-1)</param>
|
|
||||||
/// <param name="帧ID">帧 ID(标准 11 位,扩展帧自动置位 0x80000000)</param>
|
|
||||||
/// <param name="data">原始数据;超过 8 字节会自动截断,不足 8 字节自动补零</param>
|
|
||||||
/// <param name="是否是扩展帧">是否为 29 位扩展帧</param>
|
|
||||||
/// <param name="是否是CANFD">true = CANFD 格式;false = 经典 CAN 格式</param>
|
|
||||||
/// <param name="开启波特率加速BRS">CANFD 是否开启波特率加速(经典 CAN 忽略此参数)</param>
|
|
||||||
/// <returns>是否发送成功</returns>
|
|
||||||
public virtual bool 发送原始报文(
|
|
||||||
uint 通道号,
|
|
||||||
uint 帧ID,
|
|
||||||
byte[] data,
|
|
||||||
bool 是否是扩展帧 = false,
|
|
||||||
bool 是否是CANFD = true,
|
|
||||||
bool 开启波特率加速BRS = true)
|
|
||||||
{
|
|
||||||
if (通道号 >= _maxChannels || _channelHandles[通道号] == IntPtr.Zero) return false;
|
|
||||||
|
|
||||||
byte[] buffer = new byte[8];
|
|
||||||
if (data != null)
|
|
||||||
{
|
|
||||||
Array.Copy(data, 0, buffer, 0, Math.Min(data.Length, 8));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint canId = 帧ID & 0x7FFFFFFF;
|
|
||||||
if (是否是扩展帧) canId |= 0x80000000;
|
|
||||||
|
|
||||||
lock (_channelLocks[通道号])
|
|
||||||
{
|
|
||||||
if (是否是CANFD)
|
|
||||||
{
|
|
||||||
ZLGCAN.canfd_frame fdFrame = new ZLGCAN.canfd_frame
|
|
||||||
{
|
|
||||||
can_id = canId,
|
|
||||||
len = 8,
|
|
||||||
flags = (byte)((是否是扩展帧 ? 0x01 : 0x00) | (开启波特率加速BRS ? 0x02 : 0x00) | 0x20),
|
|
||||||
data = new byte[64]
|
|
||||||
};
|
|
||||||
Array.Copy(buffer, 0, fdFrame.data, 0, 8);
|
|
||||||
|
|
||||||
ZLGCAN.ZCAN_TransmitFD_Data txFdData = new ZLGCAN.ZCAN_TransmitFD_Data { frame = fdFrame, transmit_type = 2 };
|
|
||||||
IntPtr pTxFd = Marshal.AllocHGlobal(Marshal.SizeOf(txFdData));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Marshal.StructureToPtr(txFdData, pTxFd, true);
|
|
||||||
return ZLGCAN.ZCAN_TransmitFD(_channelHandles[通道号], pTxFd, 1) == 1;
|
|
||||||
}
|
|
||||||
finally { Marshal.FreeHGlobal(pTxFd); }
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ZLGCAN.can_frame standardFrame = new ZLGCAN.can_frame
|
|
||||||
{
|
|
||||||
can_id = canId,
|
|
||||||
can_dlc = 8,
|
|
||||||
__pad = 0x20,
|
|
||||||
data = new byte[8]
|
|
||||||
};
|
|
||||||
Array.Copy(buffer, 0, standardFrame.data, 0, 8);
|
|
||||||
|
|
||||||
ZLGCAN.ZCAN_Transmit_Data txData = new ZLGCAN.ZCAN_Transmit_Data { frame = standardFrame, transmit_type = 2 };
|
|
||||||
IntPtr pTx = Marshal.AllocHGlobal(Marshal.SizeOf(txData));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Marshal.StructureToPtr(txData, pTx, true);
|
|
||||||
return ZLGCAN.ZCAN_Transmit(_channelHandles[通道号], pTx, 1) == 1;
|
|
||||||
}
|
|
||||||
finally { Marshal.FreeHGlobal(pTx); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 构建发送用信号字典:DBC 初始值 + 额外覆盖。
|
|
||||||
/// 调用方需自行保证通道已初始化且 DBC 已加载。
|
|
||||||
/// </summary>
|
|
||||||
private Dictionary<string, double> 构建报文发送字典(uint 通道号, uint 帧ID, Dictionary<string, double>? 额外覆盖 = null)
|
|
||||||
{
|
|
||||||
uint chDbcHandle = _dbcHandles[通道号];
|
|
||||||
IntPtr ptrDbcMsg = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCMessage)));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
lock (_channelLocks[通道号])
|
|
||||||
{
|
|
||||||
if (!ZDBC.ZDBC_GetMessageById(chDbcHandle, 帧ID, ptrDbcMsg))
|
|
||||||
return new Dictionary<string, double>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
var msg = (ZDBC.DBCMessage)Marshal.PtrToStructure(ptrDbcMsg, typeof(ZDBC.DBCMessage));
|
|
||||||
var dict = new Dictionary<string, double>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
// 1. DBC 初始值打底
|
|
||||||
for (int i = 0; i < msg.nSignalCount; i++)
|
|
||||||
{
|
|
||||||
var signal = msg.vSignals[i];
|
|
||||||
string signalName = Encoding.Default.GetString(signal.strName).TrimEnd('\0');
|
|
||||||
if (!string.IsNullOrEmpty(signalName) && signal.initialValueValid != 0)
|
|
||||||
{
|
|
||||||
dict[signalName] = signal.initialValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 叠加持久化覆盖表(之前 设置报文 累积的值)
|
|
||||||
if (_signalOverrides.TryGetValue((通道号, 帧ID), out var persisted))
|
|
||||||
{
|
|
||||||
lock (persisted)
|
|
||||||
{
|
|
||||||
foreach (var kvp in persisted)
|
|
||||||
dict[kvp.Key] = kvp.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 叠加本次调用传入的额外覆盖(优先级最高)
|
|
||||||
if (额外覆盖 != null)
|
|
||||||
{
|
|
||||||
foreach (var kvp in 额外覆盖)
|
|
||||||
{
|
|
||||||
dict[kvp.Key] = kvp.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dict;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
|
|
||||||
Marshal.FreeHGlobal(ptrDbcMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// CANFD 有效 DLC 校验,非法值自动就近取到下一个合法 DLC。
|
|
||||||
/// </summary>
|
|
||||||
private static byte 校验CANFD的Dlc(byte dlc)
|
|
||||||
{
|
|
||||||
if (dlc <= 8) return dlc;
|
|
||||||
if (dlc <= 12) return 12;
|
|
||||||
if (dlc <= 16) return 16;
|
|
||||||
if (dlc <= 20) return 20;
|
|
||||||
if (dlc <= 24) return 24;
|
|
||||||
if (dlc <= 32) return 32;
|
|
||||||
if (dlc <= 48) return 48;
|
|
||||||
return 64;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 5. 辅助工具子系统
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 计算物理值也需要传入对应通道,使用通道专属的DBC句柄进行计算
|
|
||||||
/// </summary>
|
|
||||||
/// <summary>
|
|
||||||
/// 智能解析帧 ID 字符串,兼容 10 进制、16 进制(如 "0x1A", "1A", "26")
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idStr">输入的帧 ID 字符串</param>
|
|
||||||
/// <param name="frameId">解析成功后的 uint 帧 ID</param>
|
|
||||||
/// <returns>是否解析成功</returns>
|
|
||||||
[Browsable(false)]
|
|
||||||
public static bool TryParseFrameId(string idStr, out uint frameId)
|
|
||||||
{
|
|
||||||
frameId = 0;
|
|
||||||
if (string.IsNullOrWhiteSpace(idStr)) return false;
|
|
||||||
|
|
||||||
idStr = idStr.Trim();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 1. 处理带 0x 或 0X 的标准 16 进制
|
|
||||||
if (idStr.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
frameId = Convert.ToUInt32(idStr.Substring(2), 16);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 尝试当做纯 10 进制解析(如 "26")
|
|
||||||
if (uint.TryParse(idStr, out frameId))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 如果不是纯数字,但符合 16 进制特征(如 "1A"、"ABC"),尝试按 16 进制解析
|
|
||||||
// 使用 System.Globalization.NumberStyles.HexNumber
|
|
||||||
if (uint.TryParse(idStr, System.Globalization.NumberStyles.HexNumber, null, out frameId))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Browsable(false)]
|
|
||||||
public ulong 物理值转原始寄存器值(uint 通道号, ZDBC.DBCSignal 信号定义, double 实际物理值)
|
|
||||||
{
|
|
||||||
if (通道号 >= _maxChannels) return 0;
|
|
||||||
|
|
||||||
IntPtr pSignal = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCSignal)));
|
|
||||||
IntPtr pValue = Marshal.AllocHGlobal(sizeof(double));
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Marshal.StructureToPtr(信号定义, pSignal, true);
|
|
||||||
Marshal.StructureToPtr(实际物理值, pValue, true);
|
|
||||||
lock (_channelLocks[通道号])
|
|
||||||
{
|
|
||||||
return ZDBC.ZDBC_CalcRawValue(pSignal, pValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(pSignal);
|
|
||||||
Marshal.FreeHGlobal(pValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
[Browsable(false)]
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
关闭CAN卡设备();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,125 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace ZLGUSBCANFD
|
|
||||||
{
|
|
||||||
public class _Msg_
|
|
||||||
{
|
|
||||||
public int msg_type { get; set; }
|
|
||||||
public int msg_id { get; set; }
|
|
||||||
public int msg_cyclic { get; set; }
|
|
||||||
public byte msg_dlc { get; set; }
|
|
||||||
public string msg_name { get; set; }
|
|
||||||
public string[] signal_Name { get; set; }
|
|
||||||
public int msg_brs { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Msg_Type
|
|
||||||
{
|
|
||||||
CAN = 0,
|
|
||||||
CANFD = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// DBC 报文解析器(实例类,每个 ZLGCANFD 拥有独立的解析器实例)。
|
|
||||||
/// 不同 CAN 卡的作用域各自维护自己的 MsgDatabase,互不干扰。
|
|
||||||
/// </summary>
|
|
||||||
public class ZLGDBCParser
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 每个通道的报文数据库缓存,供 ViewModel 直接读取。
|
|
||||||
/// 索引 = 通道号 (0 ~ maxChannels-1)。
|
|
||||||
/// </summary>
|
|
||||||
public List<List<_Msg_>> MsgDatabase { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 最大通道数(与所属 ZLGCANFD 一致)。
|
|
||||||
/// </summary>
|
|
||||||
public int MaxChannels { get; }
|
|
||||||
|
|
||||||
public ZLGDBCParser(int maxChannels = 4)
|
|
||||||
{
|
|
||||||
MaxChannels = maxChannels;
|
|
||||||
MsgDatabase = new List<List<_Msg_>>(
|
|
||||||
Enumerable.Range(0, maxChannels).Select(_ => new List<_Msg_>())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 解析特定通道的 DBC 并自动存入对应的 MsgDatabase 缓存中。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="chDbcHandle">ZDBC 句柄</param>
|
|
||||||
/// <param name="channel">通道号索引 (0 ~ maxChannels-1)</param>
|
|
||||||
public void ParseAndLoadToDatabase(uint chDbcHandle, int channel)
|
|
||||||
{
|
|
||||||
if (channel < 0 || channel >= MsgDatabase.Count) return;
|
|
||||||
|
|
||||||
// 清空当前通道旧缓存
|
|
||||||
MsgDatabase[channel].Clear();
|
|
||||||
|
|
||||||
// 解析并填充
|
|
||||||
var parsedList = ParseZlgDbc(chDbcHandle);
|
|
||||||
MsgDatabase[channel].AddRange(parsedList);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 全手工解析周立功 DBC 句柄下的所有消息和信号。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="chDbcHandle">对应通道的 ZDBC 句柄</param>
|
|
||||||
/// <returns>返回对齐同星业务层的自定义报文列表</returns>
|
|
||||||
public static List<_Msg_> ParseZlgDbc(uint chDbcHandle)
|
|
||||||
{
|
|
||||||
List<_Msg_> msgList = new List<_Msg_>();
|
|
||||||
|
|
||||||
if (chDbcHandle == 0 || chDbcHandle == ZDBC.INVALID_DBC_HANDLE)
|
|
||||||
return msgList;
|
|
||||||
|
|
||||||
int msgSize = Marshal.SizeOf(typeof(ZDBC.DBCMessage));
|
|
||||||
IntPtr pMsg = Marshal.AllocHGlobal(msgSize);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bool hasMessage = ZDBC.ZDBC_GetFirstMessage(chDbcHandle, pMsg);
|
|
||||||
|
|
||||||
while (hasMessage)
|
|
||||||
{
|
|
||||||
var zlgMsg = (ZDBC.DBCMessage)Marshal.PtrToStructure(pMsg, typeof(ZDBC.DBCMessage));
|
|
||||||
|
|
||||||
int nullIndex = Array.IndexOf(zlgMsg.strName, (byte)0);
|
|
||||||
|
|
||||||
_Msg_ myMsg = new _Msg_
|
|
||||||
{
|
|
||||||
msg_id = (int)zlgMsg.nID,
|
|
||||||
msg_name = nullIndex >= 0
|
|
||||||
? Encoding.Default.GetString(zlgMsg.strName, 0, nullIndex)
|
|
||||||
: Encoding.Default.GetString(zlgMsg.strName).TrimEnd('\0'),
|
|
||||||
msg_dlc = (byte)zlgMsg.nSize,
|
|
||||||
msg_cyclic = (int)zlgMsg.nCycleTime,
|
|
||||||
msg_type = zlgMsg.nExtend == 1 ? (int)Msg_Type.CANFD : (int)Msg_Type.CAN,
|
|
||||||
signal_Name = new string[zlgMsg.nSignalCount]
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int i = 0; i < zlgMsg.nSignalCount; i++)
|
|
||||||
{
|
|
||||||
var signal = zlgMsg.vSignals[i];
|
|
||||||
string sigName = Encoding.Default.GetString(signal.strName).TrimEnd('\0');
|
|
||||||
myMsg.signal_Name[i] = sigName;
|
|
||||||
}
|
|
||||||
|
|
||||||
msgList.Add(myMsg);
|
|
||||||
hasMessage = ZDBC.ZDBC_GetNextMessage(chDbcHandle, pMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(pMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
msgList.Sort((a, b) => a.msg_id.CompareTo(b.msg_id));
|
|
||||||
return msgList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user