Compare commits
8 Commits
2820b2f300
...
db8baed9c7
| Author | SHA1 | Date | |
|---|---|---|---|
| db8baed9c7 | |||
| 52d486e2b1 | |||
| 412b56935c | |||
| b21c1901e1 | |||
| f5145d10e4 | |||
| 3b41459dcd | |||
| aec0044595 | |||
| 6c2b975999 |
7
ADP.sln
7
ADP.sln
@@ -41,6 +41,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZLGUSBCANFD", "ZLGUSBCANFD\
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchMovementModule", "BenchMovementModule\BenchMovementModule.csproj", "{31F84D0E-3634-4C70-A830-21AF412E3BBF}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchMovementModule", "BenchMovementModule\BenchMovementModule.csproj", "{31F84D0E-3634-4C70-A830-21AF412E3BBF}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CANModule", "CANModule\CANModule.csproj", "{AF2533DD-599D-49D7-942B-B0C26534D15F}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -119,6 +121,10 @@ Global
|
|||||||
{31F84D0E-3634-4C70-A830-21AF412E3BBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{31F84D0E-3634-4C70-A830-21AF412E3BBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{31F84D0E-3634-4C70-A830-21AF412E3BBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{31F84D0E-3634-4C70-A830-21AF412E3BBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{31F84D0E-3634-4C70-A830-21AF412E3BBF}.Release|Any CPU.Build.0 = Release|Any CPU
|
{31F84D0E-3634-4C70-A830-21AF412E3BBF}.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.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.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -132,5 +138,6 @@ Global
|
|||||||
{850A5BE8-0BC1-4D6A-8351-DD812C9C4427} = {1E92D601-68FC-45F6-8251-BD0F39226E5B}
|
{850A5BE8-0BC1-4D6A-8351-DD812C9C4427} = {1E92D601-68FC-45F6-8251-BD0F39226E5B}
|
||||||
{170AD4C1-189D-4FBE-B10D-2A4304527834} = {1E92D601-68FC-45F6-8251-BD0F39226E5B}
|
{170AD4C1-189D-4FBE-B10D-2A4304527834} = {1E92D601-68FC-45F6-8251-BD0F39226E5B}
|
||||||
{31F84D0E-3634-4C70-A830-21AF412E3BBF} = {1E92D601-68FC-45F6-8251-BD0F39226E5B}
|
{31F84D0E-3634-4C70-A830-21AF412E3BBF} = {1E92D601-68FC-45F6-8251-BD0F39226E5B}
|
||||||
|
{AF2533DD-599D-49D7-942B-B0C26534D15F} = {1E92D601-68FC-45F6-8251-BD0F39226E5B}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\BenchMovementModule\BenchMovementModule.csproj" />
|
<ProjectReference Include="..\BenchMovementModule\BenchMovementModule.csproj" />
|
||||||
|
<ProjectReference Include="..\CANModule\CANModule.csproj" />
|
||||||
<ProjectReference Include="..\Command\Command.csproj" />
|
<ProjectReference Include="..\Command\Command.csproj" />
|
||||||
<ProjectReference Include="..\DeviceCommand\DeviceCommand.csproj" />
|
<ProjectReference Include="..\DeviceCommand\DeviceCommand.csproj" />
|
||||||
<ProjectReference Include="..\DeviceEditModule\DeviceEditModule.csproj" />
|
<ProjectReference Include="..\DeviceEditModule\DeviceEditModule.csproj" />
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ namespace ADP
|
|||||||
containerRegistry.RegisterScoped<DeviceManager>();
|
containerRegistry.RegisterScoped<DeviceManager>();
|
||||||
containerRegistry.RegisterSingleton<GlobalInfo>();
|
containerRegistry.RegisterSingleton<GlobalInfo>();
|
||||||
containerRegistry.RegisterSingleton<HardwareDataBroadcaster>();
|
containerRegistry.RegisterSingleton<HardwareDataBroadcaster>();
|
||||||
|
containerRegistry.RegisterSingleton<CANSignalBroadcaster>();
|
||||||
//注册AutoMapper
|
//注册AutoMapper
|
||||||
var config = new MapperConfiguration(
|
var config = new MapperConfiguration(
|
||||||
cfg => cfg.AddProfile<AutoMapperProfile>(),
|
cfg => cfg.AddProfile<AutoMapperProfile>(),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
using Logger;
|
using Logger;
|
||||||
using MaterialDesignThemes.Wpf;
|
using MaterialDesignThemes.Wpf;
|
||||||
|
using Microsoft.Win32;
|
||||||
using Model.Models;
|
using Model.Models;
|
||||||
using Notifications.Wpf.Core;
|
using Notifications.Wpf.Core;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
@@ -15,6 +16,7 @@ using UIShare;
|
|||||||
using UIShare.GlobalVariable;
|
using UIShare.GlobalVariable;
|
||||||
using UIShare.PubEvent;
|
using UIShare.PubEvent;
|
||||||
using UIShare.UIViewModel;
|
using UIShare.UIViewModel;
|
||||||
|
using ZLGUSBCANFD;
|
||||||
|
|
||||||
namespace ADP.ViewModels
|
namespace ADP.ViewModels
|
||||||
{
|
{
|
||||||
@@ -29,6 +31,7 @@ namespace ADP.ViewModels
|
|||||||
|
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
private readonly IRegionManager _regionManager;
|
private readonly IRegionManager _regionManager;
|
||||||
|
private readonly IDialogService _dialogService;
|
||||||
private readonly IContainerProvider _containerProvider;
|
private readonly IContainerProvider _containerProvider;
|
||||||
private readonly INotificationManager _notificationManager;
|
private readonly INotificationManager _notificationManager;
|
||||||
private readonly IModuleManager _moduleManager;
|
private readonly IModuleManager _moduleManager;
|
||||||
@@ -145,11 +148,16 @@ namespace ADP.ViewModels
|
|||||||
public ICommand NewCommand { get; set; }
|
public ICommand NewCommand { get; set; }
|
||||||
public ICommand SetDefaultCommand { get; set; }
|
public ICommand SetDefaultCommand { get; set; }
|
||||||
public ICommand ShowDialogManagerViewCommand { get; set; }
|
public ICommand ShowDialogManagerViewCommand { get; set; }
|
||||||
|
public ICommand SelectCANSignalMonitorCommand { get; set; }
|
||||||
|
public ICommand SelectCANMessageCommand { get; set; }
|
||||||
|
public ICommand MonitorValueSettingCommand { get; set; }
|
||||||
|
public ICommand GetFileStringCommand { get; set; }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public ShellViewModel(IContainerProvider containerProvider)
|
public ShellViewModel(IContainerProvider containerProvider)
|
||||||
{
|
{
|
||||||
_containerProvider = containerProvider;
|
_containerProvider = containerProvider;
|
||||||
|
_dialogService= containerProvider.Resolve<IDialogService>();
|
||||||
_globalInfo = containerProvider.Resolve<GlobalInfo>();
|
_globalInfo = containerProvider.Resolve<GlobalInfo>();
|
||||||
_eventAggregator = containerProvider.Resolve<IEventAggregator>();
|
_eventAggregator = containerProvider.Resolve<IEventAggregator>();
|
||||||
_regionManager = containerProvider.Resolve<IRegionManager>();
|
_regionManager = containerProvider.Resolve<IRegionManager>();
|
||||||
@@ -173,6 +181,10 @@ namespace ADP.ViewModels
|
|||||||
SaveAsCommand = new DelegateCommand(SaveAs);
|
SaveAsCommand = new DelegateCommand(SaveAs);
|
||||||
SaveCommand = new DelegateCommand(Save);
|
SaveCommand = new DelegateCommand(Save);
|
||||||
SetDefaultCommand = new DelegateCommand(SetDefault);
|
SetDefaultCommand = new DelegateCommand(SetDefault);
|
||||||
|
SelectCANSignalMonitorCommand = new DelegateCommand(SelectCANSignalMonitor);
|
||||||
|
SelectCANMessageCommand = new DelegateCommand(SelectCANMessage);
|
||||||
|
MonitorValueSettingCommand = new DelegateCommand(MonitorValueSetting);
|
||||||
|
GetFileStringCommand = new DelegateCommand(GetFileString);
|
||||||
|
|
||||||
_globalInfo.ContextDic.Add("default", new ScopedContext());
|
_globalInfo.ContextDic.Add("default", new ScopedContext());
|
||||||
|
|
||||||
@@ -196,7 +208,6 @@ namespace ADP.ViewModels
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void RefreshAllContextProperties()
|
private void RefreshAllContextProperties()
|
||||||
{
|
{
|
||||||
RaisePropertyChanged(nameof(RunState));
|
RaisePropertyChanged(nameof(RunState));
|
||||||
@@ -207,6 +218,64 @@ namespace ADP.ViewModels
|
|||||||
}
|
}
|
||||||
|
|
||||||
#region 命令处理与事件
|
#region 命令处理与事件
|
||||||
|
private void GetFileString()
|
||||||
|
{
|
||||||
|
var openFileDialog = new OpenFileDialog
|
||||||
|
{
|
||||||
|
Title = "选择文件",
|
||||||
|
Filter = "所有文件 (*.*)|*.*",
|
||||||
|
};
|
||||||
|
if (openFileDialog.ShowDialog() == true)
|
||||||
|
{
|
||||||
|
string filePath = openFileDialog.FileName;
|
||||||
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
Clipboard.SetDataObject(filePath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void MonitorValueSetting()
|
||||||
|
{
|
||||||
|
if ( _globalInfo.CurrentScope == "default")
|
||||||
|
{
|
||||||
|
LoggerHelper.Warn("当前作用域未配置 CAN 设备,无法打开报文选择窗口。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_dialogService.ShowDialog("ValueLimitView", new DialogParameters
|
||||||
|
{
|
||||||
|
{ "Scope", _globalInfo.ScopeDic[_globalInfo.CurrentScope] }
|
||||||
|
}, _ => { });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectCANSignalMonitor()
|
||||||
|
{
|
||||||
|
var canfd = CurrentConfig?.CANFD;
|
||||||
|
if (canfd == null || _globalInfo.CurrentScope == "default")
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_dialogService.ShowDialog("CollectCANSignalSettingView", new DialogParameters
|
||||||
|
{
|
||||||
|
{ "SystemConfig", CurrentConfig }
|
||||||
|
}, _ => { });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectCANMessage()
|
||||||
|
{
|
||||||
|
var canfd = CurrentConfig?.CANFD;
|
||||||
|
if (canfd == null||_globalInfo.CurrentScope=="default")
|
||||||
|
{
|
||||||
|
LoggerHelper.Warn("当前作用域未配置 CAN 设备,无法打开报文选择窗口。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_dialogService.ShowDialog("SelectCanMessageView", new DialogParameters
|
||||||
|
{
|
||||||
|
{ "CANFD", canfd }
|
||||||
|
}, _ => { });
|
||||||
|
}
|
||||||
|
|
||||||
private void ShowDialogManagerView()
|
private void ShowDialogManagerView()
|
||||||
{
|
{
|
||||||
_eventAggregator.GetEvent<CancelMinimizeEvent>().Publish();
|
_eventAggregator.GetEvent<CancelMinimizeEvent>().Publish();
|
||||||
|
|||||||
@@ -158,6 +158,41 @@
|
|||||||
Foreground="Black" />
|
Foreground="Black" />
|
||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
<!-- CAN信号监控 -->
|
||||||
|
<MenuItem Header="CAN信号监控"
|
||||||
|
Foreground="Black"
|
||||||
|
Command="{Binding SelectCANSignalMonitorCommand}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<materialDesign:PackIcon Kind="Message"
|
||||||
|
Foreground="Black" />
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
<!-- 监控设置 -->
|
||||||
|
<MenuItem Header="监控设置"
|
||||||
|
Foreground="Black"
|
||||||
|
Command="{Binding MonitorValueSettingCommand}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<materialDesign:PackIcon Kind="Monitor"
|
||||||
|
Foreground="Black" />
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem Header="CAN报文选择"
|
||||||
|
Foreground="Black"
|
||||||
|
Command="{Binding SelectCANMessageCommand}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<materialDesign:PackIcon Kind="Message"
|
||||||
|
Foreground="Black" />
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
<!-- 获得文件路径字符串 -->
|
||||||
|
<MenuItem Header="获得文件路径字符串"
|
||||||
|
Foreground="Black"
|
||||||
|
Command="{Binding GetFileStringCommand}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<materialDesign:PackIcon Kind="File"
|
||||||
|
Foreground="Black" />
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
|
||||||
<!-- 蜂鸣器消音 -->
|
<!-- 蜂鸣器消音 -->
|
||||||
<MenuItem Header="弹窗管理器"
|
<MenuItem Header="弹窗管理器"
|
||||||
|
|||||||
18
CANModule/CANModule.cs
Normal file
18
CANModule/CANModule.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using CANModule.Views;
|
||||||
|
|
||||||
|
namespace CANModule
|
||||||
|
{
|
||||||
|
public class CANModule : IModule
|
||||||
|
{
|
||||||
|
public void OnInitialized(IContainerProvider containerProvider)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterTypes(IContainerRegistry containerRegistry)
|
||||||
|
{
|
||||||
|
containerRegistry.RegisterDialog<SelectCanMessageView>("SelectCanMessageView");
|
||||||
|
containerRegistry.RegisterDialog<CollectCANSignalSettingView>("CollectCANSignalSettingView");
|
||||||
|
containerRegistry.RegisterForNavigation<ZLGCANFDView>("ZLGCANFDView");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
CANModule/CANModule.csproj
Normal file
14
CANModule/CANModule.csproj
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0-windows</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<UseWPF>true</UseWPF>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\UIShare\UIShare.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
249
CANModule/ViewModels/CollectCANSingalSettingViewModel.cs
Normal file
249
CANModule/ViewModels/CollectCANSingalSettingViewModel.cs
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using UIShare.GlobalVariable;
|
||||||
|
using UIShare.PubEvent;
|
||||||
|
using UIShare.UIViewModel;
|
||||||
|
using UIShare.ViewModelBase;
|
||||||
|
using ZLGUSBCANFD;
|
||||||
|
|
||||||
|
namespace CANModule.ViewModels
|
||||||
|
{
|
||||||
|
public class CollectCANSignalSettingViewModel : DialogViewModelBase
|
||||||
|
{
|
||||||
|
#region 字段
|
||||||
|
private ZLGCANFD? _canfd;
|
||||||
|
private SystemConfig? _systemConfig;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 属性
|
||||||
|
public ObservableCollection<CANSignalConfig> ConfigurationList
|
||||||
|
{
|
||||||
|
get => _systemConfig?.ConfigurationList ?? new ObservableCollection<CANSignalConfig>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private ObservableCollection<string> _messageList;
|
||||||
|
public ObservableCollection<string> MessageList
|
||||||
|
{
|
||||||
|
get => _messageList;
|
||||||
|
set => SetProperty(ref _messageList, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObservableCollection<string> _signalList;
|
||||||
|
public ObservableCollection<string> SignalList
|
||||||
|
{
|
||||||
|
get => _signalList;
|
||||||
|
set => SetProperty(ref _signalList, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _title = "信号采集设置";
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get => _title;
|
||||||
|
set => SetProperty(ref _title, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _selectedMessage;
|
||||||
|
public string SelectedMessage
|
||||||
|
{
|
||||||
|
get => _selectedMessage;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedMessage, value))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(value) && _canfd != null)
|
||||||
|
{
|
||||||
|
var db = _canfd.DBCParser.MsgDatabase;
|
||||||
|
if (MessageChannel >= 0 && MessageChannel < db.Count)
|
||||||
|
{
|
||||||
|
var targetMsg = db[MessageChannel]
|
||||||
|
.FirstOrDefault(x => $"[0x{x.msg_id:X}]{x.msg_name}" == value);
|
||||||
|
|
||||||
|
if (targetMsg != null && targetMsg.signal_Name != null)
|
||||||
|
{
|
||||||
|
SignalList = new ObservableCollection<string>(targetMsg.signal_Name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SignalList = new ObservableCollection<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SignalList = new ObservableCollection<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private CANSignalConfig _SelectedItem;
|
||||||
|
public CANSignalConfig SelectedItem
|
||||||
|
{
|
||||||
|
get => _SelectedItem;
|
||||||
|
set => SetProperty(ref _SelectedItem, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _selectedSignal;
|
||||||
|
public string SelectedSignal
|
||||||
|
{
|
||||||
|
get => _selectedSignal;
|
||||||
|
set => SetProperty(ref _selectedSignal, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _MessageChannel=-1;
|
||||||
|
public int MessageChannel
|
||||||
|
{
|
||||||
|
get => _MessageChannel;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_MessageChannel != value)
|
||||||
|
{
|
||||||
|
_MessageChannel = value;
|
||||||
|
|
||||||
|
SelectedMessage = null;
|
||||||
|
SelectedSignal = null;
|
||||||
|
SignalList = new ObservableCollection<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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _collectionInterval = 1000;
|
||||||
|
public int CollectionInterval
|
||||||
|
{
|
||||||
|
get => _collectionInterval;
|
||||||
|
set => SetProperty(ref _collectionInterval, value);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 命令
|
||||||
|
public ICommand AddCommand { get; set; }
|
||||||
|
public ICommand CloseCommand { get; set; }
|
||||||
|
public ICommand SaveCommand { get; set; }
|
||||||
|
public ICommand DeleteCommand { get; set; }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public CollectCANSignalSettingViewModel(IContainerProvider containerProvider) : base(containerProvider)
|
||||||
|
{
|
||||||
|
CloseCommand = new DelegateCommand(Close);
|
||||||
|
AddCommand = new DelegateCommand(Add);
|
||||||
|
SaveCommand = new DelegateCommand(Save);
|
||||||
|
DeleteCommand = new DelegateCommand(Delete);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Dialog 生命周期
|
||||||
|
public override void OnDialogOpened(IDialogParameters parameters)
|
||||||
|
{
|
||||||
|
_systemConfig = parameters.GetValue<SystemConfig>("SystemConfig");
|
||||||
|
_canfd = _systemConfig?.CANFD;
|
||||||
|
string title = _systemConfig.Title;
|
||||||
|
char lastChar = title[title.Length - 1];
|
||||||
|
// 3. "TestCellX"转int
|
||||||
|
MessageChannel = int.Parse(lastChar.ToString())-1;
|
||||||
|
RaisePropertyChanged(nameof(ConfigurationList));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 命令处理
|
||||||
|
private void Delete()
|
||||||
|
{
|
||||||
|
if (SelectedItem != null)
|
||||||
|
{
|
||||||
|
_systemConfig?.ConfigurationList.Remove(SelectedItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Save()
|
||||||
|
{
|
||||||
|
ConfigService.Save(_systemConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void Add()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(SelectedSignal) || string.IsNullOrEmpty(SelectedMessage) || CollectionInterval < 100)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_systemConfig == null) return;
|
||||||
|
|
||||||
|
string hexString = ExtractHex(SelectedMessage);
|
||||||
|
if (string.IsNullOrEmpty(hexString)) return;
|
||||||
|
|
||||||
|
int decimalValue = ConvertHexToDecimal(hexString);
|
||||||
|
if (_systemConfig.ConfigurationList.Any(x => x.SignalName == SelectedSignal && x.Channel == MessageChannel))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_systemConfig.ConfigurationList.Add(new CANSignalConfig
|
||||||
|
{
|
||||||
|
CollectionID = Guid.NewGuid(),
|
||||||
|
Channel = MessageChannel,
|
||||||
|
MessageName = ExtractMessageName(SelectedMessage),
|
||||||
|
SignalName = SelectedSignal,
|
||||||
|
MessageID = decimalValue,
|
||||||
|
CollectionInterval = CollectionInterval
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ExtractHex(string input)
|
||||||
|
{
|
||||||
|
int startIndex = input.IndexOf("0x") + 2;
|
||||||
|
int endIndex = input.IndexOf("]", startIndex);
|
||||||
|
if (startIndex >= 2 && endIndex > startIndex)
|
||||||
|
{
|
||||||
|
return input.Substring(startIndex, endIndex - startIndex);
|
||||||
|
}
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int ConvertHexToDecimal(string hex)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Convert.ToInt32(hex, 16);
|
||||||
|
}
|
||||||
|
catch (FormatException)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ExtractMessageName(string input)
|
||||||
|
{
|
||||||
|
var parts = input.Split(']');
|
||||||
|
return parts.Length > 1 ? parts[1] : string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Close()
|
||||||
|
{
|
||||||
|
RequestClose.Invoke();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
187
CANModule/ViewModels/SelectCanMessageViewModel.cs
Normal file
187
CANModule/ViewModels/SelectCanMessageViewModel.cs
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using UIShare.ViewModelBase;
|
||||||
|
using ZLGUSBCANFD;
|
||||||
|
|
||||||
|
namespace CANModule.ViewModels
|
||||||
|
{
|
||||||
|
public class SelectCanMessageViewModel : DialogViewModelBase
|
||||||
|
{
|
||||||
|
#region 字段
|
||||||
|
private ZLGCANFD? _canfd;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 属性
|
||||||
|
public DialogCloseListener RequestClose { get; set; }
|
||||||
|
|
||||||
|
private ObservableCollection<int> _ChannelList;
|
||||||
|
public ObservableCollection<int> ChannelList
|
||||||
|
{
|
||||||
|
get => _ChannelList;
|
||||||
|
set => SetProperty(ref _ChannelList, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObservableCollection<string> _messageList;
|
||||||
|
public ObservableCollection<string> MessageList
|
||||||
|
{
|
||||||
|
get => _messageList;
|
||||||
|
set => SetProperty(ref _messageList, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObservableCollection<string> _signalList;
|
||||||
|
public ObservableCollection<string> SignalList
|
||||||
|
{
|
||||||
|
get => _signalList;
|
||||||
|
set => SetProperty(ref _signalList, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _title = "CollectCANSignalSetting";
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get => _title;
|
||||||
|
set => SetProperty(ref _title, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _selectedMessage;
|
||||||
|
public string SelectedMessage
|
||||||
|
{
|
||||||
|
get => _selectedMessage;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedMessage, value))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(value) && _canfd != null)
|
||||||
|
{
|
||||||
|
var db = _canfd.DBCParser.MsgDatabase;
|
||||||
|
if (MessageChannel >= 0 && MessageChannel < db.Count)
|
||||||
|
{
|
||||||
|
var targetMsg = db[MessageChannel]
|
||||||
|
.FirstOrDefault(x => $"[0x{x.msg_id:X}]{x.msg_name}" == value);
|
||||||
|
|
||||||
|
if (targetMsg != null && targetMsg.signal_Name != null)
|
||||||
|
{
|
||||||
|
SignalList = new ObservableCollection<string>(targetMsg.signal_Name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SignalList = new ObservableCollection<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SignalList = new ObservableCollection<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _selectedSignal;
|
||||||
|
public string SelectedSignal
|
||||||
|
{
|
||||||
|
get => _selectedSignal;
|
||||||
|
set => SetProperty(ref _selectedSignal, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _MessageChannel = -1;
|
||||||
|
public int MessageChannel
|
||||||
|
{
|
||||||
|
get => _MessageChannel;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_MessageChannel != value)
|
||||||
|
{
|
||||||
|
_MessageChannel = value;
|
||||||
|
|
||||||
|
// 1. 先安全清空旧的选择项和信号列表
|
||||||
|
SelectedMessage = 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 命令
|
||||||
|
public ICommand CloseCommand { get; set; }
|
||||||
|
public ICommand GetSelectedMessageCommand { get; set; }
|
||||||
|
public ICommand GetSelectedSignalCommand { get; set; }
|
||||||
|
public ICommand LoadCommand { get; set; }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public SelectCanMessageViewModel(IContainerProvider containerProvider) : base(containerProvider)
|
||||||
|
{
|
||||||
|
CloseCommand = new DelegateCommand(Close);
|
||||||
|
GetSelectedMessageCommand = new DelegateCommand(GetSelectedMessage);
|
||||||
|
GetSelectedSignalCommand = new DelegateCommand(GetSelectedSignal);
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
int maxCh = _canfd?.DBCParser.MaxChannels ?? 4;
|
||||||
|
ChannelList = new ObservableCollection<int>(Enumerable.Range(0, maxCh));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GetSelectedSignal()
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(SelectedSignal))
|
||||||
|
{
|
||||||
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
Clipboard.SetDataObject(SelectedSignal);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GetSelectedMessage()
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(SelectedMessage) && SelectedMessage.Contains(']'))
|
||||||
|
{
|
||||||
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
Clipboard.SetDataObject(SelectedMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
649
CANModule/ViewModels/ZLGCANFDViewModel.cs
Normal file
649
CANModule/ViewModels/ZLGCANFDViewModel.cs
Normal file
@@ -0,0 +1,649 @@
|
|||||||
|
using Microsoft.Win32;
|
||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using UIShare.GlobalVariable;
|
||||||
|
using UIShare.UIViewModel;
|
||||||
|
using UIShare.ViewModelBase;
|
||||||
|
using ZLGUSBCANFD;
|
||||||
|
|
||||||
|
namespace CANModule.ViewModels
|
||||||
|
{
|
||||||
|
public class ZLGCANFDViewModel : NavigateViewModelBase
|
||||||
|
{
|
||||||
|
#region 字段
|
||||||
|
private ZLGCANFD? _canfd;
|
||||||
|
private SystemConfig? _systemConfig;
|
||||||
|
private CancellationTokenSource? _refreshCts;
|
||||||
|
private readonly DeviceManager _deviceManager;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 属性
|
||||||
|
private string _Title = "周立功CAN";
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get => _Title;
|
||||||
|
set => SetProperty(ref _Title, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObservableCollection<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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpLoadDBC()
|
||||||
|
{
|
||||||
|
if (_canfd == null) return;
|
||||||
|
|
||||||
|
// 释放当前通道的 DBC
|
||||||
|
if (DBCChannel >= 0)
|
||||||
|
{
|
||||||
|
_canfd.释放通道DBC((uint)DBCChannel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ChangeDBC()
|
||||||
|
{
|
||||||
|
var dialog = new OpenFileDialog
|
||||||
|
{
|
||||||
|
Title = "更换DBC文件",
|
||||||
|
Filter = "DBC 文件 (*.dbc)|*.dbc|所有文件 (*.*)|*.*",
|
||||||
|
DefaultExt = ".dbc",
|
||||||
|
};
|
||||||
|
if (dialog.ShowDialog() == true)
|
||||||
|
{
|
||||||
|
DBCPath = System.IO.Path.GetFullPath(dialog.FileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadDBC()
|
||||||
|
{
|
||||||
|
if (_canfd == null || DBCChannel < 0) return;
|
||||||
|
|
||||||
|
bool re = _canfd.加载通道DBC文件((uint)DBCChannel, DBCPath);
|
||||||
|
|
||||||
|
if (re && IsSaved)
|
||||||
|
{
|
||||||
|
var item = DBCAutoLoadList.FirstOrDefault(s => s.DBCChannel == DBCChannel);
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
DBCAutoLoadList.Add(new AutoDBCLoadItem
|
||||||
|
{
|
||||||
|
DBCFilePath = DBCPath,
|
||||||
|
DBCChannel = DBCChannel
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DBCAutoLoadList.Remove(item);
|
||||||
|
DBCAutoLoadList.Add(new AutoDBCLoadItem
|
||||||
|
{
|
||||||
|
DBCFilePath = DBCPath,
|
||||||
|
DBCChannel = DBCChannel
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ConfigService.Save(_systemConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void ClearCycle()
|
||||||
|
{
|
||||||
|
_canfd?.停止所有循环发送();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CloseCan()
|
||||||
|
{
|
||||||
|
if (_canfd == null) return;
|
||||||
|
|
||||||
|
_canfd.关闭CAN卡设备();
|
||||||
|
IsDeviceOpened = false;
|
||||||
|
_refreshCts?.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConnectCan()
|
||||||
|
{
|
||||||
|
if (_canfd == null) return;
|
||||||
|
|
||||||
|
bool re = _canfd.打开设备();
|
||||||
|
IsDeviceOpened = re;
|
||||||
|
|
||||||
|
if (re)
|
||||||
|
{
|
||||||
|
// 启动实时报文刷新
|
||||||
|
_refreshCts?.Cancel();
|
||||||
|
_refreshCts = new CancellationTokenSource();
|
||||||
|
_ = Refresh(_refreshCts.Token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EndTranscribe()
|
||||||
|
{
|
||||||
|
// TODO: BLF 录制功能在 ZLGCANFD 中未实现,暂不支持
|
||||||
|
ShowInfoMessageBox("BLF 录制功能尚未在此硬件驱动中实现。", () => { });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartTranscribe()
|
||||||
|
{
|
||||||
|
// TODO: BLF 录制功能在 ZLGCANFD 中未实现,暂不支持
|
||||||
|
ShowInfoMessageBox("BLF 录制功能尚未在此硬件驱动中实现。", () => { });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveBLFPath()
|
||||||
|
{
|
||||||
|
// 路径已直接绑定到 SystemConfig,无需额外操作
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectBLFPath()
|
||||||
|
{
|
||||||
|
var dialog = new SaveFileDialog
|
||||||
|
{
|
||||||
|
Title = "保存 CAN报文回放 BLF 文件",
|
||||||
|
Filter = "BLF 文件 (*.blf)|*.blf|所有文件 (*.*)|*.*",
|
||||||
|
DefaultExt = ".blf",
|
||||||
|
FileName = "log.blf",
|
||||||
|
OverwritePrompt = true
|
||||||
|
};
|
||||||
|
if (dialog.ShowDialog() == true)
|
||||||
|
{
|
||||||
|
BLFPath = System.IO.Path.GetFullPath(dialog.FileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 辅助方法
|
||||||
|
public byte[] 报文转换(string s)
|
||||||
|
{
|
||||||
|
s = s.Replace("[", string.Empty).Replace("]", string.Empty);
|
||||||
|
var strs = s.Split(',', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
byte[] bytes = new byte[strs.Length];
|
||||||
|
for (int i = 0; i < strs.Length; i++)
|
||||||
|
{
|
||||||
|
bytes[i] = Convert.ToByte(strs[i], 16);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Refresh(CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
while (!ct.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
// 通过 OnDbcMessageDecoded 事件已实时更新 CanMessageList
|
||||||
|
// 此处仅做周期性 UI 刷新触发
|
||||||
|
await Task.Delay(50, ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDbcMessageDecoded(uint channel, ZDBC.DBCMessage msg)
|
||||||
|
{
|
||||||
|
// DBC 解码后的报文回调
|
||||||
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
var msgName = System.Text.Encoding.Default.GetString(msg.strName).TrimEnd('\0');
|
||||||
|
var find = CanMessageList.FirstOrDefault(s => s.报文ID == (int)msg.nID);
|
||||||
|
if (find != null)
|
||||||
|
{
|
||||||
|
find.通道 = (byte)channel;
|
||||||
|
find.报文ID = (int)msg.nID;
|
||||||
|
find.时间戳 = (ulong)DateTimeOffset.Now.ToUnixTimeMilliseconds(); ;
|
||||||
|
find.长度 = (byte)msg.nSize;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CanMessageList.Add(new CanMessageShowVM
|
||||||
|
{
|
||||||
|
通道 = (byte)channel,
|
||||||
|
报文ID = (int)msg.nID,
|
||||||
|
时间戳 = (ulong)DateTimeOffset.Now.ToUnixTimeMilliseconds(),
|
||||||
|
长度 = (byte)msg.nSize
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private string 十六进制字符串转换(System.Span<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
|
||||||
|
}
|
||||||
|
}
|
||||||
120
CANModule/Views/CollectCANSignalSettingView.xaml
Normal file
120
CANModule/Views/CollectCANSignalSettingView.xaml
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<UserControl x:Class="CANModule.Views.CollectCANSignalSettingView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
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:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
xmlns:prism="http://prismlibrary.com/"
|
||||||
|
xmlns:cons="clr-namespace:UIShare.Converters;assembly=UIShare"
|
||||||
|
Background="White"
|
||||||
|
prism:ViewModelLocator.AutoWireViewModel="True"
|
||||||
|
Height="400"
|
||||||
|
Width="1000">
|
||||||
|
<prism:Dialog.WindowStyle>
|
||||||
|
<Style BasedOn="{StaticResource DialogUserManageStyle}"
|
||||||
|
TargetType="Window" />
|
||||||
|
</prism:Dialog.WindowStyle>
|
||||||
|
<UserControl.Resources>
|
||||||
|
<cons:HexConverter x:Key="HexConverter" />
|
||||||
|
</UserControl.Resources>
|
||||||
|
<GroupBox Padding="0,0,0,0"
|
||||||
|
MouseLeftButtonDown="MouseLeftButtonDown">
|
||||||
|
<GroupBox.Header>
|
||||||
|
<Grid Margin="0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Text="CAN信号采集设置"
|
||||||
|
Foreground="White"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Margin="5,0,10,0" />
|
||||||
|
<Button Content="关闭"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="150 0 0 0"
|
||||||
|
Width="60"
|
||||||
|
Command="{Binding CloseCommand}"
|
||||||
|
Height="25" />
|
||||||
|
</Grid>
|
||||||
|
</GroupBox.Header>
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition />
|
||||||
|
<RowDefinition Height="auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<DataGrid x:Name="配置表格"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
ItemsSource="{Binding ConfigurationList}"
|
||||||
|
SelectedItem="{Binding SelectedItem}"
|
||||||
|
IsReadOnly="True">
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu>
|
||||||
|
<MenuItem Header="删除"
|
||||||
|
Command="{Binding DeleteCommand}"
|
||||||
|
/>
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="通道"
|
||||||
|
Binding="{Binding Channel}" />
|
||||||
|
<DataGridTextColumn Header="报文ID"
|
||||||
|
Binding="{Binding MessageID,Converter={StaticResource HexConverter}}" />
|
||||||
|
<DataGridTextColumn Header="报文名称"
|
||||||
|
Binding="{Binding MessageName}" />
|
||||||
|
<DataGridTextColumn Header="信号名称"
|
||||||
|
Binding="{Binding SignalName}" />
|
||||||
|
<DataGridTextColumn Header="采集间隔"
|
||||||
|
Binding="{Binding CollectionInterval}" />
|
||||||
|
<DataGridTextColumn Header="采集id"
|
||||||
|
Binding="{Binding CollectionID}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
<StackPanel Orientation="Horizontal"
|
||||||
|
Grid.Row="1"
|
||||||
|
Margin="10">
|
||||||
|
<Label Content="通道(从0开始)"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<TextBox Name="SignalComboBox"
|
||||||
|
Text="{Binding MessageChannel}"
|
||||||
|
MaxWidth="200"
|
||||||
|
MinWidth="120"
|
||||||
|
Margin="10"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
materialDesign:HintAssist.Hint="信号通道" />
|
||||||
|
<Label Content="报文"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<ComboBox MinWidth="100"
|
||||||
|
materialDesign:HintAssist.Hint=""
|
||||||
|
SelectedItem="{Binding SelectedMessage,Mode=TwoWay}"
|
||||||
|
ItemsSource="{Binding MessageList}"
|
||||||
|
/>
|
||||||
|
<Label Content="信号"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<ComboBox SelectedItem="{Binding SelectedSignal}"
|
||||||
|
materialDesign:HintAssist.Hint=""
|
||||||
|
MaxWidth="200"
|
||||||
|
ItemsSource="{Binding SignalList,Mode=TwoWay}"
|
||||||
|
MinWidth="100" />
|
||||||
|
<Label Content="采集间隔(ms)"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<TextBox Text="{Binding CollectionInterval}"
|
||||||
|
MinWidth="100"
|
||||||
|
materialDesign:HintAssist.Hint=""
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<Button Content="添加"
|
||||||
|
Margin="10"
|
||||||
|
Command="{Binding AddCommand}"
|
||||||
|
/>
|
||||||
|
<Button Content="保存"
|
||||||
|
Margin="10"
|
||||||
|
Command="{Binding SaveCommand}"
|
||||||
|
/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</GroupBox>
|
||||||
|
</UserControl>
|
||||||
25
CANModule/Views/CollectCANSignalSettingView.xaml.cs
Normal file
25
CANModule/Views/CollectCANSignalSettingView.xaml.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace CANModule.Views
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// CollectCANSignalSettingView.xaml 的交互逻辑
|
||||||
|
/// </summary>
|
||||||
|
public partial class CollectCANSignalSettingView : UserControl
|
||||||
|
{
|
||||||
|
public CollectCANSignalSettingView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.LeftButton == MouseButtonState.Pressed)
|
||||||
|
{
|
||||||
|
Window.GetWindow(this)?.DragMove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
69
CANModule/Views/SelectCanMessageView.xaml
Normal file
69
CANModule/Views/SelectCanMessageView.xaml
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<UserControl x:Class="CANModule.Views.SelectCanMessageView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
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:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
xmlns:prism="http://prismlibrary.com/"
|
||||||
|
xmlns:cons="clr-namespace:UIShare.Converters;assembly=UIShare"
|
||||||
|
Background="White"
|
||||||
|
prism:ViewModelLocator.AutoWireViewModel="True"
|
||||||
|
Height="350"
|
||||||
|
Width="350">
|
||||||
|
<prism:Dialog.WindowStyle>
|
||||||
|
<Style BasedOn="{StaticResource DialogUserManageStyle}"
|
||||||
|
TargetType="Window" />
|
||||||
|
</prism:Dialog.WindowStyle>
|
||||||
|
<i:Interaction.Triggers>
|
||||||
|
<i:EventTrigger EventName="Loaded">
|
||||||
|
<i:InvokeCommandAction Command="{Binding LoadCommand}" />
|
||||||
|
</i:EventTrigger>
|
||||||
|
</i:Interaction.Triggers>
|
||||||
|
<GroupBox Padding="0,0,0,0"
|
||||||
|
MouseLeftButtonDown="MouseLeftButtonDown">
|
||||||
|
<GroupBox.Header>
|
||||||
|
<Grid Margin="0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Text="CAN报文选择"
|
||||||
|
Foreground="White"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Margin="5,0,10,0" />
|
||||||
|
<Button Content="关闭"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="0 0 0 0"
|
||||||
|
Width="60"
|
||||||
|
Command="{Binding CloseCommand}"
|
||||||
|
Height="25" />
|
||||||
|
</Grid>
|
||||||
|
</GroupBox.Header>
|
||||||
|
<Grid>
|
||||||
|
<StackPanel Grid.Row="1" Margin="10">
|
||||||
|
<Label Content="通道" VerticalAlignment="Center"/>
|
||||||
|
<ComboBox ItemsSource="{Binding ChannelList}"
|
||||||
|
SelectedItem="{Binding MessageChannel,Mode=TwoWay}"
|
||||||
|
MinWidth="100"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
materialDesign:HintAssist.Hint="" />
|
||||||
|
<Label Content="报文" VerticalAlignment="Center"/>
|
||||||
|
<ComboBox materialDesign:HintAssist.Hint=""
|
||||||
|
MinWidth="100"
|
||||||
|
SelectedItem="{Binding SelectedMessage,Mode=TwoWay}"
|
||||||
|
ItemsSource="{Binding MessageList}" />
|
||||||
|
<Label Content="信号" VerticalAlignment="Center"/>
|
||||||
|
<ComboBox materialDesign:HintAssist.Hint=""
|
||||||
|
SelectedItem="{Binding SelectedSignal,Mode=TwoWay}"
|
||||||
|
ItemsSource="{Binding SignalList}" MinWidth="100" />
|
||||||
|
<Button Content="选择报文名" Margin="10" Command="{Binding GetSelectedMessageCommand}"/>
|
||||||
|
<Button Content="选择信号名" Margin="10" Command="{Binding GetSelectedSignalCommand}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</GroupBox>
|
||||||
|
</UserControl>
|
||||||
36
CANModule/Views/SelectCanMessageView.xaml.cs
Normal file
36
CANModule/Views/SelectCanMessageView.xaml.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
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.Data;
|
||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
using System.Windows.Navigation;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
|
||||||
|
namespace CANModule.Views
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// SelectCanMessageView.xaml 的交互逻辑
|
||||||
|
/// </summary>
|
||||||
|
public partial class SelectCanMessageView : UserControl
|
||||||
|
{
|
||||||
|
public SelectCanMessageView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.LeftButton == MouseButtonState.Pressed)
|
||||||
|
{
|
||||||
|
Window.GetWindow(this)?.DragMove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
286
CANModule/Views/ZLGCANFDView.xaml
Normal file
286
CANModule/Views/ZLGCANFDView.xaml
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
<UserControl x:Class="CANModule.Views.ZLGCANFDView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="clr-namespace:CANModule.Views"
|
||||||
|
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
|
||||||
|
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
xmlns:prism="http://prismlibrary.com/"
|
||||||
|
xmlns:cons="clr-namespace:UIShare.Converters;assembly=UIShare"
|
||||||
|
Background="White"
|
||||||
|
prism:ViewModelLocator.AutoWireViewModel="True"
|
||||||
|
Height="850"
|
||||||
|
Width="900">
|
||||||
|
<prism:Dialog.WindowStyle>
|
||||||
|
<Style BasedOn="{StaticResource DialogUserManageStyle}"
|
||||||
|
TargetType="Window" />
|
||||||
|
</prism:Dialog.WindowStyle>
|
||||||
|
<i:Interaction.Triggers>
|
||||||
|
<i:EventTrigger EventName="Loaded">
|
||||||
|
<i:InvokeCommandAction Command="{Binding LoadCommand}" />
|
||||||
|
</i:EventTrigger>
|
||||||
|
</i:Interaction.Triggers>
|
||||||
|
<UserControl.Resources>
|
||||||
|
<cons:HexConverter x:Key="HexConverter" />
|
||||||
|
</UserControl.Resources>
|
||||||
|
<GroupBox Padding="0,0,0,0"
|
||||||
|
>
|
||||||
|
<GroupBox.Header>
|
||||||
|
<Grid Margin="0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Text="CAN手动设置"
|
||||||
|
Foreground="White"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Margin="5,0,10,0" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</GroupBox.Header>
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- CAN卡连接 -->
|
||||||
|
<StackPanel Orientation="Horizontal"
|
||||||
|
Margin="5"
|
||||||
|
Grid.Row="0">
|
||||||
|
<Button Content="打开设备"
|
||||||
|
Command="{Binding ConnectCanCommand}"
|
||||||
|
Margin="5" />
|
||||||
|
<Button Content="关闭设备"
|
||||||
|
Command="{Binding CloseCanCommand}"
|
||||||
|
Margin="5" />
|
||||||
|
<Button Content="清空循环发送报文"
|
||||||
|
Command="{Binding ClearCycleCommand}"
|
||||||
|
Margin="5" />
|
||||||
|
|
||||||
|
</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="监视">
|
||||||
|
<DataGrid x:Name="表格"
|
||||||
|
Width="550"
|
||||||
|
Height="350"
|
||||||
|
VirtualizingPanel.IsVirtualizing="True"
|
||||||
|
IsReadOnly="True"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
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>
|
||||||
|
</TreeViewItem>
|
||||||
|
|
||||||
|
<!-- DBC 配置 -->
|
||||||
|
<TreeViewItem Header="DBC">
|
||||||
|
<Label Content="请注意,要在断开CAN卡的情况下才能操作DBC!"
|
||||||
|
Foreground="Red" />
|
||||||
|
<Button Content="卸载当前通道DBC"
|
||||||
|
Command="{Binding UpLoadDBCCommand}" />
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Button Content="更换DBC"
|
||||||
|
Command="{Binding ChangeDBCCommand}" />
|
||||||
|
<Label Content="DBC文件路径"
|
||||||
|
VerticalContentAlignment="Center" />
|
||||||
|
<TextBox Text="{Binding DBCPath,Mode=TwoWay}"
|
||||||
|
MinWidth="100"
|
||||||
|
materialDesign:HintAssist.Hint=""
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Button Content="加载DBC"
|
||||||
|
Command="{Binding LoadDBCCommand}" />
|
||||||
|
<Label Content="通道(从0开始)"
|
||||||
|
VerticalContentAlignment="Center" />
|
||||||
|
<TextBox Text="{Binding DBCChannel,Mode=TwoWay}"
|
||||||
|
materialDesign:HintAssist.Hint=""
|
||||||
|
MinWidth="100"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<CheckBox Margin="5 0 0 0"
|
||||||
|
IsChecked="{Binding IsSaved}" />
|
||||||
|
<Label Content="加载记录保存"
|
||||||
|
VerticalContentAlignment="Center" />
|
||||||
|
</StackPanel>
|
||||||
|
<DataGrid ItemsSource="{Binding DBCAutoLoadList}"
|
||||||
|
Width="650"
|
||||||
|
IsReadOnly="True"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
SelectedItem="{Binding SelectedDcAutoLoad}">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="通道"
|
||||||
|
Binding="{Binding DBCChannel}"
|
||||||
|
Width="60" />
|
||||||
|
<DataGridTextColumn Header="文件路径"
|
||||||
|
Binding="{Binding DBCFilePath}"
|
||||||
|
Width="*" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu>
|
||||||
|
<MenuItem Header="删除"
|
||||||
|
Foreground="Red"
|
||||||
|
Command="{Binding DeleteDcAutoLoadCommand}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
|
</DataGrid>
|
||||||
|
</TreeViewItem>
|
||||||
|
|
||||||
|
<!-- 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>
|
||||||
|
<Label Content="报文" />
|
||||||
|
<ComboBox MinWidth="100"
|
||||||
|
materialDesign:HintAssist.Hint=""
|
||||||
|
SelectedItem="{Binding SelectedMessage}"
|
||||||
|
ItemsSource="{Binding Message}" />
|
||||||
|
</DockPanel>
|
||||||
|
<DockPanel>
|
||||||
|
<Label Content="信号" />
|
||||||
|
<ComboBox SelectedItem="{Binding SelectedSignal}"
|
||||||
|
ItemsSource="{Binding Signal}"
|
||||||
|
materialDesign:HintAssist.Hint=""
|
||||||
|
MinWidth="100" />
|
||||||
|
</DockPanel>
|
||||||
|
<DockPanel>
|
||||||
|
<Label Content="信号值" />
|
||||||
|
<TextBox Text="{Binding SignalValue}"
|
||||||
|
materialDesign:HintAssist.Hint=""
|
||||||
|
MinWidth="100" />
|
||||||
|
</DockPanel>
|
||||||
|
<DockPanel>
|
||||||
|
<Label Content="循环发送时间间隔(ms)(0为单次发送)" />
|
||||||
|
<TextBox Text="{Binding CycleSendInterval}"
|
||||||
|
materialDesign:HintAssist.Hint=""
|
||||||
|
MinWidth="100" />
|
||||||
|
</DockPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Button Content="设置信号并发送"
|
||||||
|
Command="{Binding SetAndSendMessageCommand}" />
|
||||||
|
<CheckBox Content="直接发送"
|
||||||
|
IsChecked="{Binding IsDirectlySend}" />
|
||||||
|
</StackPanel>
|
||||||
|
</TreeViewItem>
|
||||||
|
|
||||||
|
<!-- 自定义报文发送 -->
|
||||||
|
<TreeViewItem Header="自定义报文发送">
|
||||||
|
<DockPanel>
|
||||||
|
<Label Content="通道" />
|
||||||
|
<ComboBox MinWidth="100"
|
||||||
|
materialDesign:HintAssist.Hint=""
|
||||||
|
SelectedItem="{Binding AIdxChn}"
|
||||||
|
ItemsSource="{Binding APP_CHANNEL}" />
|
||||||
|
</DockPanel>
|
||||||
|
<DockPanel>
|
||||||
|
<Label Content="ID" />
|
||||||
|
<TextBox MinWidth="100"
|
||||||
|
materialDesign:HintAssist.Hint=""
|
||||||
|
Text="{Binding AID, Converter={StaticResource HexConverter}, Mode=TwoWay}" />
|
||||||
|
</DockPanel>
|
||||||
|
<DockPanel>
|
||||||
|
<Label Content="DLC" />
|
||||||
|
<TextBox MinWidth="200"
|
||||||
|
materialDesign:HintAssist.Hint=""
|
||||||
|
Text="{Binding ADLC}" />
|
||||||
|
<CheckBox Content="自动设定"
|
||||||
|
IsChecked="{Binding AutoSetting}" />
|
||||||
|
</DockPanel>
|
||||||
|
<DockPanel>
|
||||||
|
<Label Content="报文" />
|
||||||
|
<TextBox MinWidth="200"
|
||||||
|
materialDesign:HintAssist.Hint=""
|
||||||
|
Text="{Binding CustomMessage}" />
|
||||||
|
</DockPanel>
|
||||||
|
<DockPanel>
|
||||||
|
<Label Content="循环发送时间间隔(ms)(0为单次发送)" />
|
||||||
|
<TextBox Text="{Binding CustomSendCycleInterval}"
|
||||||
|
materialDesign:HintAssist.Hint=""
|
||||||
|
MinWidth="100" />
|
||||||
|
</DockPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<CheckBox Content="发送帧"
|
||||||
|
IsChecked="{Binding AIsTx}" />
|
||||||
|
<CheckBox Content="扩展帧"
|
||||||
|
IsChecked="{Binding AIsExt}" />
|
||||||
|
<CheckBox Content="远程帧"
|
||||||
|
IsChecked="{Binding AIsRemote}" />
|
||||||
|
<CheckBox Content="FD"
|
||||||
|
IsChecked="{Binding AIsFD}" />
|
||||||
|
<CheckBox Content="BRS"
|
||||||
|
IsChecked="{Binding AIsBRS}" />
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Button Content="发送"
|
||||||
|
Command="{Binding SendCustomMessageCommand}" />
|
||||||
|
<!--<CheckBox Margin="10"
|
||||||
|
Content="更新到数据库"
|
||||||
|
IsChecked="{Binding UpdateToDB}" />-->
|
||||||
|
</StackPanel>
|
||||||
|
</TreeViewItem>
|
||||||
|
|
||||||
|
<!-- 报文录制 -->
|
||||||
|
<TreeViewItem Header="报文录制">
|
||||||
|
<DockPanel>
|
||||||
|
<Label Content="路径(开始录制时使用)"
|
||||||
|
VerticalContentAlignment="Center" />
|
||||||
|
<TextBox MinWidth="100"
|
||||||
|
materialDesign:HintAssist.Hint=""
|
||||||
|
Text="{Binding BLFPath}"
|
||||||
|
VerticalContentAlignment="Center" />
|
||||||
|
<Button Content="选择路径"
|
||||||
|
Margin="10 0"
|
||||||
|
Command="{Binding SelectBLFPathCommand}" />
|
||||||
|
<Button Content="保存路径"
|
||||||
|
Command="{Binding SaveBLFPathCommand}" />
|
||||||
|
</DockPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Button Content="开始录制"
|
||||||
|
Command="{Binding StartTranscribeCommand}" />
|
||||||
|
<Button Content="结束录制"
|
||||||
|
Margin="10 0"
|
||||||
|
Command="{Binding EndTranscribeCommand}" />
|
||||||
|
</StackPanel>
|
||||||
|
</TreeViewItem>
|
||||||
|
</TreeView>
|
||||||
|
</Grid>
|
||||||
|
</GroupBox>
|
||||||
|
</UserControl>
|
||||||
35
CANModule/Views/ZLGCANFDView.xaml.cs
Normal file
35
CANModule/Views/ZLGCANFDView.xaml.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
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.Data;
|
||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
using System.Windows.Navigation;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
|
||||||
|
namespace CANModule.Views
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ZLGCANFDView.xaml 的交互逻辑
|
||||||
|
/// </summary>
|
||||||
|
public partial class ZLGCANFDView : UserControl
|
||||||
|
{
|
||||||
|
public ZLGCANFDView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
private void MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.LeftButton == MouseButtonState.Pressed)
|
||||||
|
{
|
||||||
|
Window.GetWindow(this)?.DragMove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,51 +13,7 @@
|
|||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<converters:BooleanToVisibilityConverter x:Key="BoolToVis"/>
|
<converters:BooleanToVisibilityConverter x:Key="BoolToVis"/>
|
||||||
|
|
||||||
<!-- 普通操作按钮 -->
|
|
||||||
<Style x:Key="CmdBtn" TargetType="Button" BasedOn="{StaticResource MaterialDesignRaisedButton}">
|
|
||||||
<Setter Property="Height" Value="32"/>
|
|
||||||
<Setter Property="Padding" Value="12,0"/>
|
|
||||||
<Setter Property="FontSize" Value="12"/>
|
|
||||||
<Setter Property="Margin" Value="4,0"/>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<!-- 危险/警告操作按钮(橙色/红色) -->
|
|
||||||
<Style x:Key="WarnBtn" TargetType="Button" BasedOn="{StaticResource MaterialDesignRaisedButton}">
|
|
||||||
<Setter Property="Height" Value="32"/>
|
|
||||||
<Setter Property="Padding" Value="12,0"/>
|
|
||||||
<Setter Property="FontSize" Value="12"/>
|
|
||||||
<Setter Property="Margin" Value="4,0"/>
|
|
||||||
<Setter Property="Background" Value="#EF6C00"/>
|
|
||||||
<Setter Property="Foreground" Value="White"/>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<!-- 数字输入框 -->
|
|
||||||
<Style x:Key="NumInput" TargetType="TextBox" BasedOn="{StaticResource MaterialDesignOutlinedTextBox}">
|
|
||||||
<Setter Property="Width" Value="100"/>
|
|
||||||
<Setter Property="Height" Value="32"/>
|
|
||||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
|
||||||
<Setter Property="FontSize" Value="12"/>
|
|
||||||
<Setter Property="Margin" Value="4,0"/>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<!-- 测量值只读显示 -->
|
|
||||||
<Style x:Key="MeasureBox" TargetType="TextBox" BasedOn="{StaticResource MaterialDesignOutlinedTextBox}">
|
|
||||||
<Setter Property="Width" Value="110"/>
|
|
||||||
<Setter Property="Height" Value="32"/>
|
|
||||||
<Setter Property="IsReadOnly" Value="True"/>
|
|
||||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
|
||||||
<Setter Property="FontSize" Value="12"/>
|
|
||||||
<Setter Property="Margin" Value="4,0"/>
|
|
||||||
<Setter Property="Background" Value="#F5F5F5"/>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<!-- 标签 -->
|
|
||||||
<Style x:Key="ParamLabel" TargetType="TextBlock">
|
|
||||||
<Setter Property="Width" Value="80"/>
|
|
||||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
|
||||||
<Setter Property="FontSize" Value="12"/>
|
|
||||||
<Setter Property="Margin" Value="0,0,4,0"/>
|
|
||||||
</Style>
|
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
|
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||||
|
|||||||
@@ -12,47 +12,6 @@
|
|||||||
|
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<converters:BooleanToVisibilityConverter x:Key="BoolToVis"/>
|
<converters:BooleanToVisibilityConverter x:Key="BoolToVis"/>
|
||||||
|
|
||||||
<Style x:Key="CmdBtn" TargetType="Button" BasedOn="{StaticResource MaterialDesignRaisedButton}">
|
|
||||||
<Setter Property="Height" Value="32"/>
|
|
||||||
<Setter Property="Padding" Value="12,0"/>
|
|
||||||
<Setter Property="FontSize" Value="12"/>
|
|
||||||
<Setter Property="Margin" Value="4,0"/>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style x:Key="WarnBtn" TargetType="Button" BasedOn="{StaticResource MaterialDesignRaisedButton}">
|
|
||||||
<Setter Property="Height" Value="32"/>
|
|
||||||
<Setter Property="Padding" Value="12,0"/>
|
|
||||||
<Setter Property="FontSize" Value="12"/>
|
|
||||||
<Setter Property="Margin" Value="4,0"/>
|
|
||||||
<Setter Property="Background" Value="#EF6C00"/>
|
|
||||||
<Setter Property="Foreground" Value="White"/>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style x:Key="NumInput" TargetType="TextBox" BasedOn="{StaticResource MaterialDesignOutlinedTextBox}">
|
|
||||||
<Setter Property="Width" Value="100"/>
|
|
||||||
<Setter Property="Height" Value="32"/>
|
|
||||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
|
||||||
<Setter Property="FontSize" Value="12"/>
|
|
||||||
<Setter Property="Margin" Value="4,0"/>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style x:Key="MeasureBox" TargetType="TextBox" BasedOn="{StaticResource MaterialDesignOutlinedTextBox}">
|
|
||||||
<Setter Property="Width" Value="110"/>
|
|
||||||
<Setter Property="Height" Value="32"/>
|
|
||||||
<Setter Property="IsReadOnly" Value="True"/>
|
|
||||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
|
||||||
<Setter Property="FontSize" Value="12"/>
|
|
||||||
<Setter Property="Margin" Value="4,0"/>
|
|
||||||
<Setter Property="Background" Value="#F5F5F5"/>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style x:Key="ParamLabel" TargetType="TextBlock">
|
|
||||||
<Setter Property="Width" Value="80"/>
|
|
||||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
|
||||||
<Setter Property="FontSize" Value="12"/>
|
|
||||||
<Setter Property="Margin" Value="0,0,4,0"/>
|
|
||||||
</Style>
|
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
|
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
using MonitorModule.Views;
|
using MonitorModule.Views;
|
||||||
|
using MonitorModule.Views.Dialogs;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace MonitorModule
|
namespace MonitorModule
|
||||||
@@ -16,6 +17,7 @@ namespace MonitorModule
|
|||||||
{
|
{
|
||||||
containerRegistry.RegisterForNavigation<MonitorView>("MonitorView");
|
containerRegistry.RegisterForNavigation<MonitorView>("MonitorView");
|
||||||
containerRegistry.RegisterForNavigation<RecordView>("RecordView");
|
containerRegistry.RegisterForNavigation<RecordView>("RecordView");
|
||||||
|
containerRegistry.RegisterDialog<ValueLimitView>("ValueLimitView");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
382
MonitorModule/ViewModels/Dialogs/ValueLimitViewModel.cs
Normal file
382
MonitorModule/ViewModels/Dialogs/ValueLimitViewModel.cs
Normal file
@@ -0,0 +1,382 @@
|
|||||||
|
using Common.Attributes;
|
||||||
|
using DeviceCommand.Base;
|
||||||
|
using Prism.Events;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using UIShare.GlobalVariable;
|
||||||
|
using UIShare.PubEvent;
|
||||||
|
using UIShare.UIViewModel;
|
||||||
|
using UIShare.ViewModelBase;
|
||||||
|
using ZLGUSBCANFD;
|
||||||
|
|
||||||
|
namespace MonitorModule.ViewModels.Dialogs
|
||||||
|
{
|
||||||
|
public class ValueLimitViewModel : DialogViewModelBase
|
||||||
|
{
|
||||||
|
#region 属性
|
||||||
|
private string _Title = "报警设置";
|
||||||
|
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get { return _Title; }
|
||||||
|
set { SetProperty(ref _Title, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private ValueLimitVM _SelectValueLimitVM;
|
||||||
|
|
||||||
|
public ValueLimitVM SelectValueLimitVM
|
||||||
|
{
|
||||||
|
get => _SelectValueLimitVM;
|
||||||
|
set => SetProperty(ref _SelectValueLimitVM, value);
|
||||||
|
}
|
||||||
|
private ObservableCollection<ValueLimitVM> _ValueLimitList;
|
||||||
|
public ObservableCollection<ValueLimitVM> ValueLimitList
|
||||||
|
{
|
||||||
|
get => _ValueLimitList;
|
||||||
|
set => SetProperty(ref _ValueLimitList, value);
|
||||||
|
}
|
||||||
|
//private ObservableCollection<string> _DeviceSingleList = new();
|
||||||
|
|
||||||
|
//public ObservableCollection<string> DeviceSingleList
|
||||||
|
//{
|
||||||
|
// get => _DeviceSingleList;
|
||||||
|
// set => SetProperty(ref _DeviceSingleList, value);
|
||||||
|
//}
|
||||||
|
//private string _SelectCurve;
|
||||||
|
|
||||||
|
//public string SelectCurve
|
||||||
|
//{
|
||||||
|
// get => _SelectCurve;
|
||||||
|
// set => SetProperty(ref _SelectCurve, value);
|
||||||
|
//}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
#region 命令
|
||||||
|
public ICommand SaveCommand { get; set; }
|
||||||
|
public ICommand CloseCommand { get; set; }
|
||||||
|
//public ICommand SelectSignalCommand { get; set; }
|
||||||
|
public ICommand DeleteCommand { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
private GlobalInfo _globalInfo { get; set; }
|
||||||
|
private SystemConfig _systemConfig { get; set; }
|
||||||
|
private IScopedProvider _scope { get; set; }
|
||||||
|
private DeviceManager? _deviceManager;
|
||||||
|
|
||||||
|
/// <summary>SignalName → CAN信号标识,用于DBC加载/卸载时清理</summary>
|
||||||
|
private readonly Dictionary<string, (string Fingerprint, string MethodName)> _canSignalMap = new();
|
||||||
|
|
||||||
|
private SubscriptionToken? _dbcLoadedToken;
|
||||||
|
private SubscriptionToken? _dbcUnloadedToken;
|
||||||
|
|
||||||
|
public ValueLimitViewModel(IContainerProvider containerProvider) : base(containerProvider)
|
||||||
|
{
|
||||||
|
_globalInfo = containerProvider.Resolve<GlobalInfo>();
|
||||||
|
_eventAggregator = containerProvider.Resolve<IEventAggregator>();
|
||||||
|
SaveCommand = new DelegateCommand(OnSave);
|
||||||
|
CloseCommand = new DelegateCommand(OnClose);
|
||||||
|
//SelectSignalCommand = new DelegateCommand(SelectSignal);
|
||||||
|
DeleteCommand = new DelegateCommand(Deletel);
|
||||||
|
}
|
||||||
|
#region 命令处理
|
||||||
|
private void Deletel()
|
||||||
|
{
|
||||||
|
if (SelectValueLimitVM == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ValueLimitList.Remove(SelectValueLimitVM);
|
||||||
|
SelectValueLimitVM = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void InitData()
|
||||||
|
{
|
||||||
|
ValueLimitList = new ObservableCollection<ValueLimitVM>();
|
||||||
|
//DeviceSingleList = new ObservableCollection<string>();
|
||||||
|
_canSignalMap.Clear();
|
||||||
|
|
||||||
|
// 1. 加载已保存的 ValueLimitList(保留用户设置)
|
||||||
|
if (_systemConfig?.ValueLimitList != null)
|
||||||
|
{
|
||||||
|
foreach (var item in _systemConfig.ValueLimitList)
|
||||||
|
ValueLimitList.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 发现设备方法信号并加入 ValueLimitList / DeviceSingleList
|
||||||
|
foreach (var signal in DiscoverDeviceSignals())
|
||||||
|
{
|
||||||
|
//DeviceSingleList.Add(signal);
|
||||||
|
EnsureValueLimit(signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 发现 CAN 信号并加入 ValueLimitList / DeviceSingleList
|
||||||
|
foreach (var (displayName, fingerprint, methodName) in DiscoverCanSignals())
|
||||||
|
{
|
||||||
|
//DeviceSingleList.Add(displayName);
|
||||||
|
_canSignalMap[displayName] = (fingerprint, methodName);
|
||||||
|
EnsureValueLimit(displayName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发现当前作用域所有可监测的设备方法信号(与 MonitorViewModel 逻辑一致)。
|
||||||
|
/// 返回 DisplayName 列表。
|
||||||
|
/// </summary>
|
||||||
|
private IEnumerable<string> DiscoverDeviceSignals()
|
||||||
|
{
|
||||||
|
if (_deviceManager?.DeviceMap == null) yield break;
|
||||||
|
|
||||||
|
var instanceToFp = BuildInstanceToFingerprint();
|
||||||
|
foreach (var kvp in _deviceManager.DeviceMap)
|
||||||
|
{
|
||||||
|
string deviceName = kvp.Key;
|
||||||
|
var device = kvp.Value;
|
||||||
|
|
||||||
|
if (!instanceToFp.TryGetValue(device, out string? fingerprint))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var methods = device.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance)
|
||||||
|
.Where(m =>
|
||||||
|
{
|
||||||
|
if (m.GetCustomAttribute<MonitorableAttribute>() == null) return false;
|
||||||
|
if (m.ReturnType != typeof(Task<string>)) return false;
|
||||||
|
var parms = m.GetParameters();
|
||||||
|
return parms.Length == 0 ||
|
||||||
|
(parms.Length == 1 && parms[0].ParameterType == typeof(CancellationToken));
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach (var method in methods)
|
||||||
|
{
|
||||||
|
var attr = method.GetCustomAttribute<MonitorableAttribute>();
|
||||||
|
string displayName = !string.IsNullOrEmpty(attr?.Description)
|
||||||
|
? $"{deviceName}.{attr.Description}"
|
||||||
|
: $"{deviceName}.{method.Name}";
|
||||||
|
yield return displayName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构建设备实例 → 硬件指纹的反向查找表。
|
||||||
|
/// </summary>
|
||||||
|
private Dictionary<object, string> BuildInstanceToFingerprint()
|
||||||
|
{
|
||||||
|
var map = new Dictionary<object, string>(ReferenceEqualityComparer.Instance);
|
||||||
|
if (_globalInfo?.HardwarePool == null) return map;
|
||||||
|
|
||||||
|
foreach (var poolEntry in _globalInfo.HardwarePool)
|
||||||
|
{
|
||||||
|
var lazy = poolEntry.Value;
|
||||||
|
if (lazy.IsValueCreated && lazy.Value != null)
|
||||||
|
map[lazy.Value] = poolEntry.Key;
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发现当前已加载 DBC 中的 CAN 信号(以 ConfigurationList 为白名单)。
|
||||||
|
/// 返回 (DisplayName, Fingerprint, MethodName) 列表。
|
||||||
|
/// </summary>
|
||||||
|
private IEnumerable<(string DisplayName, string Fingerprint, string MethodName)> DiscoverCanSignals()
|
||||||
|
{
|
||||||
|
if (_deviceManager?.CANFD?.DBCParser?.MsgDatabase == null) yield break;
|
||||||
|
if (_systemConfig?.ConfigurationList == null) yield break;
|
||||||
|
|
||||||
|
var msgDb = _deviceManager.CANFD.DBCParser.MsgDatabase;
|
||||||
|
foreach (var cfg in _systemConfig.ConfigurationList)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(cfg.SignalName)) continue;
|
||||||
|
if (cfg.Channel < 0 || cfg.Channel >= msgDb.Count) continue;
|
||||||
|
|
||||||
|
string fingerprint = CANSignalBroadcaster.BuildFingerprint((uint)cfg.Channel);
|
||||||
|
string methodName = CANSignalBroadcaster.BuildMethodName((uint)cfg.MessageID, cfg.SignalName);
|
||||||
|
string displayName = CANSignalBroadcaster.BuildDisplayName(cfg.MessageName, cfg.SignalName);
|
||||||
|
|
||||||
|
if (ExistsInDbc(methodName, msgDb[cfg.Channel]))
|
||||||
|
yield return (displayName, fingerprint, methodName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断指定的 MethodName 是否存在于该通道的 DBC 消息数据库中。
|
||||||
|
/// </summary>
|
||||||
|
private static bool ExistsInDbc(string methodName, List<_Msg_> messages)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(methodName)) return false;
|
||||||
|
int dotIndex = methodName.IndexOf('.');
|
||||||
|
if (dotIndex <= 0 || dotIndex >= methodName.Length - 1) return false;
|
||||||
|
|
||||||
|
if (!uint.TryParse(methodName.Substring(0, dotIndex), System.Globalization.NumberStyles.HexNumber, null, out uint msgId))
|
||||||
|
return false;
|
||||||
|
string signalName = methodName.Substring(dotIndex + 1);
|
||||||
|
|
||||||
|
return messages.Any(m => m.msg_id == msgId && m.signal_Name != null && m.signal_Name.Contains(signalName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 确保 ValueLimitList 中存在指定信号;不存在则添加默认值。
|
||||||
|
/// </summary>
|
||||||
|
private void EnsureValueLimit(string signalName)
|
||||||
|
{
|
||||||
|
if (ValueLimitList.Any(x => x.SignalName == signalName)) return;
|
||||||
|
|
||||||
|
ValueLimitList.Add(new ValueLimitVM
|
||||||
|
{
|
||||||
|
SignalName = signalName,
|
||||||
|
Upper = 9999,
|
||||||
|
Lower = -9999,
|
||||||
|
UpperExtreme = 9999,
|
||||||
|
LowerExtreme = -9999,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从 ValueLimitList 和 DeviceSingleList 中移除指定信号。
|
||||||
|
/// </summary>
|
||||||
|
private void RemoveValueLimit(string signalName)
|
||||||
|
{
|
||||||
|
var item = ValueLimitList.FirstOrDefault(x => x.SignalName == signalName);
|
||||||
|
if (item != null) ValueLimitList.Remove(item);
|
||||||
|
|
||||||
|
//if (DeviceSingleList.Contains(signalName))
|
||||||
|
// DeviceSingleList.Remove(signalName);
|
||||||
|
}
|
||||||
|
|
||||||
|
//private void SelectSignal()
|
||||||
|
//{
|
||||||
|
// if (ValueLimitList.Where(x => x.SignalName == SelectCurve).Count() > 0)
|
||||||
|
// {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// ValueLimitList.Add(new ValueLimitVM
|
||||||
|
// {
|
||||||
|
// SignalName = SelectCurve,
|
||||||
|
// Upper = 9999,
|
||||||
|
// Lower = -9999,
|
||||||
|
// UpperExtreme = 9999,
|
||||||
|
// LowerExtreme = -9999,
|
||||||
|
// });
|
||||||
|
//}
|
||||||
|
|
||||||
|
private void OnClose()
|
||||||
|
{
|
||||||
|
RequestClose.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSave()
|
||||||
|
{
|
||||||
|
_systemConfig.ValueLimitList = ValueLimitList;
|
||||||
|
ConfigService.Save(_systemConfig);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
public override void OnDialogOpened(IDialogParameters parameters)
|
||||||
|
{
|
||||||
|
// 1. 优先执行基类的打开逻辑(如果基类有需要初始化的通用事务)
|
||||||
|
base.OnDialogOpened(parameters);
|
||||||
|
|
||||||
|
// 2. 解析参数
|
||||||
|
if (parameters != null && parameters.ContainsKey("Scope")) // 修复:Prism 中应使用 ContainsKey
|
||||||
|
{
|
||||||
|
_scope = parameters.GetValue<IScopedProvider>("Scope");
|
||||||
|
_systemConfig = _scope.Resolve<SystemConfig>();
|
||||||
|
_deviceManager = _scope.Resolve<DeviceManager>();
|
||||||
|
|
||||||
|
// 3. 订阅 DBC 加载/卸载事件
|
||||||
|
_dbcLoadedToken = _eventAggregator.GetEvent<DBCLoadedEvent>()
|
||||||
|
.Subscribe(OnDbcLoaded, ThreadOption.UIThread);
|
||||||
|
_dbcUnloadedToken = _eventAggregator.GetEvent<DBCUnloadedEvent>()
|
||||||
|
.Subscribe(OnDbcUnloaded, ThreadOption.UIThread);
|
||||||
|
|
||||||
|
// 4. 初始化数据
|
||||||
|
InitData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnDialogClosed()
|
||||||
|
{
|
||||||
|
base.OnDialogClosed();
|
||||||
|
|
||||||
|
// 取消 DBC 事件订阅,避免内存泄漏
|
||||||
|
if (_dbcLoadedToken != null)
|
||||||
|
_eventAggregator.GetEvent<DBCLoadedEvent>().Unsubscribe(_dbcLoadedToken);
|
||||||
|
if (_dbcUnloadedToken != null)
|
||||||
|
_eventAggregator.GetEvent<DBCUnloadedEvent>().Unsubscribe(_dbcUnloadedToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DBCLoadedEvent 回调:清理该通道中不存在于 DBC 的 CAN 信号限制项。
|
||||||
|
/// </summary>
|
||||||
|
private void OnDbcLoaded(DBCLoadedArgs args)
|
||||||
|
{
|
||||||
|
if (args.Scope != _systemConfig?.Title) return;
|
||||||
|
if (_deviceManager?.CANFD?.DBCParser?.MsgDatabase == null) return;
|
||||||
|
|
||||||
|
int channel = (int)args.Channel;
|
||||||
|
var msgDb = _deviceManager.CANFD.DBCParser.MsgDatabase;
|
||||||
|
if (channel < 0 || channel >= msgDb.Count) return;
|
||||||
|
|
||||||
|
string fingerprint = CANSignalBroadcaster.BuildFingerprint(args.Channel);
|
||||||
|
|
||||||
|
// 1. 移除该通道中已失效的 CAN 信号限制项
|
||||||
|
var staleSignals = _canSignalMap
|
||||||
|
.Where(kvp => kvp.Value.Fingerprint == fingerprint &&
|
||||||
|
!ExistsInDbc(kvp.Value.MethodName, msgDb[channel]))
|
||||||
|
.Select(kvp => kvp.Key)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var signalName in staleSignals)
|
||||||
|
{
|
||||||
|
RemoveValueLimit(signalName);
|
||||||
|
_canSignalMap.Remove(signalName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 根据 ConfigurationList 补充新加载的有效信号
|
||||||
|
if (_systemConfig?.ConfigurationList != null)
|
||||||
|
{
|
||||||
|
foreach (var cfg in _systemConfig.ConfigurationList.Where(c => c.Channel == channel))
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(cfg.SignalName)) continue;
|
||||||
|
|
||||||
|
string methodName = CANSignalBroadcaster.BuildMethodName((uint)cfg.MessageID, cfg.SignalName);
|
||||||
|
string displayName = CANSignalBroadcaster.BuildDisplayName(cfg.MessageName, cfg.SignalName);
|
||||||
|
|
||||||
|
if (ExistsInDbc(methodName, msgDb[channel]))
|
||||||
|
{
|
||||||
|
_canSignalMap[displayName] = (fingerprint, methodName);
|
||||||
|
//if (!DeviceSingleList.Contains(displayName))
|
||||||
|
// DeviceSingleList.Add(displayName);
|
||||||
|
EnsureValueLimit(displayName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DBCUnloadedEvent 回调:删除该通道所有 CAN 信号的限制项。
|
||||||
|
/// </summary>
|
||||||
|
private void OnDbcUnloaded(DBCUnloadedArgs args)
|
||||||
|
{
|
||||||
|
if (args.Scope != _systemConfig?.Title) return;
|
||||||
|
|
||||||
|
string fingerprint = CANSignalBroadcaster.BuildFingerprint(args.Channel);
|
||||||
|
var toRemove = _canSignalMap
|
||||||
|
.Where(kvp => kvp.Value.Fingerprint == fingerprint)
|
||||||
|
.Select(kvp => kvp.Key)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var signalName in toRemove)
|
||||||
|
{
|
||||||
|
RemoveValueLimit(signalName);
|
||||||
|
_canSignalMap.Remove(signalName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using Common.Attributes;
|
using Common.Attributes;
|
||||||
using Model.Entity;
|
using Model.Entity;
|
||||||
using Model.Models;
|
using Model.Models;
|
||||||
|
using NLog;
|
||||||
using OxyPlot;
|
using OxyPlot;
|
||||||
using OxyPlot.Axes;
|
using OxyPlot.Axes;
|
||||||
using OxyPlot.Legends;
|
using OxyPlot.Legends;
|
||||||
@@ -19,7 +20,9 @@ using System.Windows.Input;
|
|||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
using UIShare.GlobalVariable;
|
using UIShare.GlobalVariable;
|
||||||
using UIShare.PubEvent;
|
using UIShare.PubEvent;
|
||||||
|
using UIShare.UIViewModel;
|
||||||
using UIShare.ViewModelBase;
|
using UIShare.ViewModelBase;
|
||||||
|
using ZLGUSBCANFD;
|
||||||
|
|
||||||
|
|
||||||
namespace MonitorModule.ViewModels
|
namespace MonitorModule.ViewModels
|
||||||
@@ -42,13 +45,25 @@ namespace MonitorModule.ViewModels
|
|||||||
public PlotModel Plot { get; }
|
public PlotModel Plot { get; }
|
||||||
|
|
||||||
/// <summary>已添加的监测通道列表</summary>
|
/// <summary>已添加的监测通道列表</summary>
|
||||||
public ObservableCollection<MonitorChannel> Channels { get; } = new();
|
public ObservableCollection<MonitorChannelVM> Channels
|
||||||
|
{
|
||||||
|
get => _systemConfig?.Channels ?? new ObservableCollection<MonitorChannelVM>();
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_systemConfig != null && _systemConfig.Channels != value)
|
||||||
|
{
|
||||||
|
_systemConfig.Channels = value;
|
||||||
|
RaisePropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>可添加的设备方法列表(供用户选择)</summary>
|
/// <summary>可添加的设备方法列表(供用户选择)</summary>
|
||||||
public ObservableCollection<AvailableMethodItem> AvailableMethods { get; } = new();
|
public ObservableCollection<AvailableMethodItem> AvailableMethods { get; } = new();
|
||||||
|
|
||||||
private MonitorChannel? _selectedChannel;
|
private MonitorChannelVM? _selectedChannel;
|
||||||
public MonitorChannel? SelectedChannel
|
public MonitorChannelVM? SelectedChannel
|
||||||
{
|
{
|
||||||
get => _selectedChannel;
|
get => _selectedChannel;
|
||||||
set => SetProperty(ref _selectedChannel, value);
|
set => SetProperty(ref _selectedChannel, value);
|
||||||
@@ -75,12 +90,14 @@ namespace MonitorModule.ViewModels
|
|||||||
public ICommand ResetViewCommand { get; }
|
public ICommand ResetViewCommand { get; }
|
||||||
public ICommand RefreshDataCommand { get; }
|
public ICommand RefreshDataCommand { get; }
|
||||||
public ICommand RefreshCommand { get; }
|
public ICommand RefreshCommand { get; }
|
||||||
|
public ICommand SaveCommand { get; }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 私有字段
|
#region 私有字段
|
||||||
private bool IsInitiated = false;
|
private bool IsInitiated = false;
|
||||||
private IScopedProvider? _scope;
|
private IScopedProvider? _scope;
|
||||||
private DeviceManager? _deviceManager;
|
private DeviceManager? _deviceManager;
|
||||||
|
private SystemConfig? _systemConfig;
|
||||||
private CancellationTokenSource? _monitorCTS = new();
|
private CancellationTokenSource? _monitorCTS = new();
|
||||||
|
|
||||||
// 颜色调色盘
|
// 颜色调色盘
|
||||||
@@ -92,9 +109,12 @@ namespace MonitorModule.ViewModels
|
|||||||
};
|
};
|
||||||
private int _colorIndex;
|
private int _colorIndex;
|
||||||
|
|
||||||
/// <summary>广播器(由 UIShare 层注入,每 Scope 一个实例)</summary>
|
/// <summary>设备数据广播器(全局单例)</summary>
|
||||||
private HardwareDataBroadcaster? _broadcaster;
|
private HardwareDataBroadcaster? _broadcaster;
|
||||||
|
|
||||||
|
/// <summary>CAN 信号广播器(全局单例)</summary>
|
||||||
|
private CANSignalBroadcaster? _canSignalBroadcaster;
|
||||||
|
|
||||||
/// <summary>用于 OxyPlot X 轴相对秒数</summary>
|
/// <summary>用于 OxyPlot X 轴相对秒数</summary>
|
||||||
private readonly Stopwatch _stopwatch = new();
|
private readonly Stopwatch _stopwatch = new();
|
||||||
|
|
||||||
@@ -103,6 +123,8 @@ namespace MonitorModule.ViewModels
|
|||||||
|
|
||||||
/// <summary>事件订阅令牌,用于 Dispose 时取消订阅</summary>
|
/// <summary>事件订阅令牌,用于 Dispose 时取消订阅</summary>
|
||||||
private SubscriptionToken? _subscriptionToken;
|
private SubscriptionToken? _subscriptionToken;
|
||||||
|
private SubscriptionToken? _dbcLoadedToken;
|
||||||
|
private SubscriptionToken? _dbcUnloadedToken;
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// 批量入库核心结构
|
// 批量入库核心结构
|
||||||
@@ -125,6 +147,7 @@ namespace MonitorModule.ViewModels
|
|||||||
ResetViewCommand = new DelegateCommand(OnResetView);
|
ResetViewCommand = new DelegateCommand(OnResetView);
|
||||||
RefreshDataCommand = new DelegateCommand(OnRefreshData);
|
RefreshDataCommand = new DelegateCommand(OnRefreshData);
|
||||||
RefreshCommand = new DelegateCommand(OnExpand);
|
RefreshCommand = new DelegateCommand(OnExpand);
|
||||||
|
SaveCommand = new DelegateCommand(OnSave);
|
||||||
|
|
||||||
// OxyPlot 刷新定时器(1000ms,只负责视觉更新)
|
// OxyPlot 刷新定时器(1000ms,只负责视觉更新)
|
||||||
_plotRefreshTimer = new DispatcherTimer(DispatcherPriority.Background)
|
_plotRefreshTimer = new DispatcherTimer(DispatcherPriority.Background)
|
||||||
@@ -134,6 +157,8 @@ namespace MonitorModule.ViewModels
|
|||||||
_plotRefreshTimer.Tick += OnPlotRefreshTick;
|
_plotRefreshTimer.Tick += OnPlotRefreshTick;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_monitorCTS?.Cancel();
|
_monitorCTS?.Cancel();
|
||||||
@@ -147,20 +172,37 @@ namespace MonitorModule.ViewModels
|
|||||||
// 取消 EventAggregator 订阅
|
// 取消 EventAggregator 订阅
|
||||||
if (_subscriptionToken != null)
|
if (_subscriptionToken != null)
|
||||||
_eventAggregator.GetEvent<HardwareDataReportedEvent>().Unsubscribe(_subscriptionToken);
|
_eventAggregator.GetEvent<HardwareDataReportedEvent>().Unsubscribe(_subscriptionToken);
|
||||||
|
if (_dbcLoadedToken != null)
|
||||||
|
_eventAggregator.GetEvent<DBCLoadedEvent>().Unsubscribe(_dbcLoadedToken);
|
||||||
|
if (_dbcUnloadedToken != null)
|
||||||
|
_eventAggregator.GetEvent<DBCUnloadedEvent>().Unsubscribe(_dbcUnloadedToken);
|
||||||
|
|
||||||
if (_dbFlushTask != null)
|
if (_dbFlushTask != null)
|
||||||
{
|
{
|
||||||
try { _dbFlushTask.Wait(TimeSpan.FromSeconds(1.5)); } catch { }
|
try { _dbFlushTask.Wait(TimeSpan.FromSeconds(1.5)); } catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
_sampleTimer.Tick -= OnSampleTick;
|
|
||||||
|
|
||||||
// 3. 释放容器作用域
|
// 3. 释放容器作用域
|
||||||
_scope?.Dispose();
|
_scope?.Dispose();
|
||||||
Channels.Clear();
|
Channels.Clear();
|
||||||
AvailableMethods.Clear();
|
AvailableMethods.Clear();
|
||||||
}
|
}
|
||||||
|
private void OnSave()
|
||||||
|
{
|
||||||
|
// 将当前 Channels 序列化为 MonitorChannelConfig 列表保存到 MonitorChannels
|
||||||
|
if (_systemConfig != null)
|
||||||
|
{
|
||||||
|
_systemConfig.MonitorChannels = new ObservableCollection<MonitorChannelConfig>(Channels
|
||||||
|
.Select(c => new MonitorChannelConfig
|
||||||
|
{
|
||||||
|
Fingerprint = c.Fingerprint,
|
||||||
|
MethodName = c.MethodName,
|
||||||
|
IsDisplayed = c.IsDisplayed
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
ConfigService.Save(_systemConfig);
|
||||||
|
}
|
||||||
#region 事件驱动:接收广播数据
|
#region 事件驱动:接收广播数据
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 由 HardwareDataBroadcaster 通过 EventAggregator 广播触发。
|
/// 由 HardwareDataBroadcaster 通过 EventAggregator 广播触发。
|
||||||
@@ -276,7 +318,10 @@ namespace MonitorModule.ViewModels
|
|||||||
_scope = _globalInfo.ScopeDic[TestStatus];
|
_scope = _globalInfo.ScopeDic[TestStatus];
|
||||||
_scopedContext = _scope.Resolve<ScopedContext>();
|
_scopedContext = _scope.Resolve<ScopedContext>();
|
||||||
_deviceManager = _scope.Resolve<DeviceManager>();
|
_deviceManager = _scope.Resolve<DeviceManager>();
|
||||||
|
_systemConfig = _scope.Resolve<SystemConfig>();
|
||||||
|
RaisePropertyChanged(nameof(Channels));
|
||||||
_broadcaster = _scope.Resolve<HardwareDataBroadcaster>();
|
_broadcaster = _scope.Resolve<HardwareDataBroadcaster>();
|
||||||
|
_canSignalBroadcaster = _scope.Resolve<CANSignalBroadcaster>();
|
||||||
IsInitiated = true;
|
IsInitiated = true;
|
||||||
|
|
||||||
if (_monitorCTS == null) _monitorCTS = new CancellationTokenSource();
|
if (_monitorCTS == null) _monitorCTS = new CancellationTokenSource();
|
||||||
@@ -287,15 +332,29 @@ namespace MonitorModule.ViewModels
|
|||||||
// 2. 扫描可监测方法(供 UI 选择)
|
// 2. 扫描可监测方法(供 UI 选择)
|
||||||
DiscoverAvailableMethods();
|
DiscoverAvailableMethods();
|
||||||
|
|
||||||
// 3. 启动广播器(Discover + Start)
|
// 3. 从配置自动恢复已保存的监测通道
|
||||||
|
RestoreChannelsFromConfig();
|
||||||
|
|
||||||
|
// 4. 启动广播器(Discover + Start)
|
||||||
_broadcaster.Discover();
|
_broadcaster.Discover();
|
||||||
_broadcaster.Start();
|
_broadcaster.Start();
|
||||||
|
_canSignalBroadcaster.Discover();
|
||||||
|
_canSignalBroadcaster.Start();
|
||||||
|
|
||||||
// 4. 订阅 HardwareDataReportedEvent(UI 线程回调)
|
// 5. 订阅 HardwareDataReportedEvent(UI 线程回调)
|
||||||
_subscriptionToken = _eventAggregator.GetEvent<HardwareDataReportedEvent>()
|
_subscriptionToken = _eventAggregator.GetEvent<HardwareDataReportedEvent>()
|
||||||
.Subscribe(OnHardwareDataReceived, ThreadOption.UIThread);
|
.Subscribe(OnHardwareDataReceived, ThreadOption.UIThread);
|
||||||
|
|
||||||
// 5. 启动 OxyPlot 视觉刷新定时器
|
// 5b. 订阅 DBC 加载/卸载事件,动态发现或清除 CAN 信号
|
||||||
|
_dbcLoadedToken = _eventAggregator.GetEvent<DBCLoadedEvent>()
|
||||||
|
.Subscribe(OnDbcLoaded, ThreadOption.UIThread);
|
||||||
|
_dbcUnloadedToken = _eventAggregator.GetEvent<DBCUnloadedEvent>()
|
||||||
|
.Subscribe(OnDbcUnloaded, ThreadOption.UIThread);
|
||||||
|
|
||||||
|
// 5c. 根据 SystemConfig.ConfigurationList 刷新已加载 DBC 中的 CAN 信号
|
||||||
|
RefreshConfiguredCanSignals();
|
||||||
|
|
||||||
|
// 6. 启动 OxyPlot 视觉刷新定时器
|
||||||
_stopwatch.Start();
|
_stopwatch.Start();
|
||||||
_plotRefreshTimer.Start();
|
_plotRefreshTimer.Start();
|
||||||
}
|
}
|
||||||
@@ -405,6 +464,253 @@ namespace MonitorModule.ViewModels
|
|||||||
? $"发现 {AvailableMethods.Count} 个可监测项"
|
? $"发现 {AvailableMethods.Count} 个可监测项"
|
||||||
: "未发现可监测的设备方法";
|
: "未发现可监测的设备方法";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从 SystemConfig.MonitorChannels 持久化列表自动还原监测通道。
|
||||||
|
/// </summary>
|
||||||
|
private void RestoreChannelsFromConfig()
|
||||||
|
{
|
||||||
|
if (_systemConfig?.MonitorChannels == null || _systemConfig.MonitorChannels.Count == 0) return;
|
||||||
|
|
||||||
|
int restored = 0;
|
||||||
|
foreach (var config in _systemConfig.MonitorChannels)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(config.Fingerprint) ||
|
||||||
|
string.IsNullOrWhiteSpace(config.MethodName)) continue;
|
||||||
|
|
||||||
|
// 已在 Channels 中存在则跳过
|
||||||
|
if (Channels.Any(c => c.Fingerprint == config.Fingerprint && c.MethodName == config.MethodName))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// 在 AvailableMethods 中查找匹配项
|
||||||
|
var method = AvailableMethods.FirstOrDefault(m =>
|
||||||
|
m.Fingerprint == config.Fingerprint && m.MethodName == config.MethodName);
|
||||||
|
if (method == null) continue;
|
||||||
|
|
||||||
|
var color = _palette[_colorIndex % _palette.Length];
|
||||||
|
_colorIndex++;
|
||||||
|
|
||||||
|
var channel = new MonitorChannelVM
|
||||||
|
{
|
||||||
|
DeviceName = method.DeviceName,
|
||||||
|
Fingerprint = method.Fingerprint,
|
||||||
|
MethodName = method.MethodName,
|
||||||
|
DisplayName = method.DisplayName,
|
||||||
|
Color = color,
|
||||||
|
IsMonitored = true,
|
||||||
|
IsDisplayed = config.IsDisplayed
|
||||||
|
};
|
||||||
|
|
||||||
|
// 仅当 IsDisplayed=true 时才创建 LineSeries
|
||||||
|
if (channel.IsDisplayed)
|
||||||
|
{
|
||||||
|
channel.Series = new LineSeries
|
||||||
|
{
|
||||||
|
Title = channel.DisplayName,
|
||||||
|
Color = color,
|
||||||
|
StrokeThickness = 1.5
|
||||||
|
};
|
||||||
|
Plot.Series.Add(channel.Series);
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.PropertyChanged += (s, e) =>
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(MonitorChannelVM.IsDisplayed))
|
||||||
|
OnChannelDisplayChanged(channel);
|
||||||
|
};
|
||||||
|
|
||||||
|
Channels.Add(channel);
|
||||||
|
restored++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (restored > 0)
|
||||||
|
{
|
||||||
|
Plot.InvalidatePlot(true);
|
||||||
|
StatusMessage = $"已从配置自动恢复 {restored} 个监测通道";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据 SystemConfig.ConfigurationList 刷新所有已加载 DBC 中的 CAN 信号。
|
||||||
|
/// 只添加 ConfigurationList 中配置且实际存在于 DBC 的信号;不存在则清理。
|
||||||
|
/// </summary>
|
||||||
|
private void RefreshConfiguredCanSignals()
|
||||||
|
{
|
||||||
|
if (_systemConfig?.ConfigurationList == null || _systemConfig.ConfigurationList.Count == 0) return;
|
||||||
|
if (_deviceManager?.CANFD?.DBCParser?.MsgDatabase == null) return;
|
||||||
|
|
||||||
|
var msgDb = _deviceManager.CANFD.DBCParser.MsgDatabase;
|
||||||
|
foreach (var channel in _systemConfig.ConfigurationList.Select(c => c.Channel).Distinct())
|
||||||
|
{
|
||||||
|
if (channel < 0 || channel >= msgDb.Count) continue;
|
||||||
|
RefreshCanSignalsForChannel((uint)channel, msgDb[channel]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 刷新单个通道的 CAN 信号:以 ConfigurationList 为白名单,对照 DBC 实际存在性。
|
||||||
|
/// - DBC 中存在 → 加入 AvailableMethods;若 MonitorChannels 中也有,则恢复 Channel。
|
||||||
|
/// - DBC 中不存在 → 从 AvailableMethods 移除;若已在 Channels 中,则删除。
|
||||||
|
/// </summary>
|
||||||
|
private void RefreshCanSignalsForChannel(uint channel, List<_Msg_> messages)
|
||||||
|
{
|
||||||
|
if (_systemConfig?.ConfigurationList == null) return;
|
||||||
|
|
||||||
|
string fingerprint = CANSignalBroadcaster.BuildFingerprint(channel);
|
||||||
|
var configs = _systemConfig.ConfigurationList.Where(c => c.Channel == (int)channel).ToList();
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
foreach (var cfg in configs)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(cfg.SignalName)) continue;
|
||||||
|
|
||||||
|
string methodName = CANSignalBroadcaster.BuildMethodName((uint)cfg.MessageID, cfg.SignalName);
|
||||||
|
bool existsInDbc = messages.Any(m =>
|
||||||
|
m.msg_id == cfg.MessageID &&
|
||||||
|
m.signal_Name != null &&
|
||||||
|
m.signal_Name.Contains(cfg.SignalName));
|
||||||
|
|
||||||
|
if (existsInDbc)
|
||||||
|
{
|
||||||
|
// 加入 AvailableMethods(去重)
|
||||||
|
if (!AvailableMethods.Any(m => m.Fingerprint == fingerprint && m.MethodName == methodName))
|
||||||
|
{
|
||||||
|
string displayName = CANSignalBroadcaster.BuildDisplayName(cfg.MessageName, cfg.SignalName);
|
||||||
|
AvailableMethods.Add(new AvailableMethodItem
|
||||||
|
{
|
||||||
|
DeviceName = $"CAN{channel}",
|
||||||
|
Fingerprint = fingerprint,
|
||||||
|
MethodName = methodName,
|
||||||
|
DisplayName = displayName,
|
||||||
|
MethodInfo = null!,
|
||||||
|
Device = null!
|
||||||
|
});
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果 MonitorChannels 中有持久化记录,自动恢复 Channel
|
||||||
|
var monitorConfig = _systemConfig.MonitorChannels?.FirstOrDefault(m =>
|
||||||
|
m.Fingerprint == fingerprint && m.MethodName == methodName);
|
||||||
|
if (monitorConfig != null && !Channels.Any(c =>
|
||||||
|
c.Fingerprint == fingerprint && c.MethodName == methodName))
|
||||||
|
{
|
||||||
|
AddCanChannel(cfg, fingerprint, methodName, monitorConfig.IsDisplayed);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// DBC 中不存在:从 AvailableMethods 移除
|
||||||
|
var methodToRemove = AvailableMethods.FirstOrDefault(m =>
|
||||||
|
m.Fingerprint == fingerprint && m.MethodName == methodName);
|
||||||
|
if (methodToRemove != null)
|
||||||
|
{
|
||||||
|
AvailableMethods.Remove(methodToRemove);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果该信号已经在 Channels(或 MonitorChannels)中,删除
|
||||||
|
var channelToRemove = Channels.FirstOrDefault(c =>
|
||||||
|
c.Fingerprint == fingerprint && c.MethodName == methodName);
|
||||||
|
if (channelToRemove != null)
|
||||||
|
{
|
||||||
|
if (channelToRemove.Series != null) Plot.Series.Remove(channelToRemove.Series);
|
||||||
|
Channels.Remove(channelToRemove);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
{
|
||||||
|
Plot.InvalidatePlot(true);
|
||||||
|
StatusMessage = $"CAN{channel} 已刷新配置信号";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DBCLoadedEvent 回调:DBC 加载后刷新该通道的配置信号。
|
||||||
|
/// </summary>
|
||||||
|
private void OnDbcLoaded(DBCLoadedArgs args)
|
||||||
|
{
|
||||||
|
if (args.Scope != TestStatus) return;
|
||||||
|
if (_deviceManager?.CANFD?.DBCParser?.MsgDatabase == null) return;
|
||||||
|
|
||||||
|
int channel = (int)args.Channel;
|
||||||
|
var msgDb = _deviceManager.CANFD.DBCParser.MsgDatabase;
|
||||||
|
if (channel < 0 || channel >= msgDb.Count) return;
|
||||||
|
|
||||||
|
RefreshCanSignalsForChannel(args.Channel, msgDb[channel]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DBCUnloadedEvent 回调:移除对应通道的 CAN 信号(AvailableMethods + Channels)。
|
||||||
|
/// </summary>
|
||||||
|
private void OnDbcUnloaded(DBCUnloadedArgs args)
|
||||||
|
{
|
||||||
|
if (args.Scope != TestStatus) return;
|
||||||
|
|
||||||
|
string fingerprint = CANSignalBroadcaster.BuildFingerprint(args.Channel);
|
||||||
|
|
||||||
|
// 从 AvailableMethods 中移除
|
||||||
|
var toRemoveMethods = AvailableMethods.Where(m => m.Fingerprint == fingerprint).ToList();
|
||||||
|
foreach (var m in toRemoveMethods)
|
||||||
|
AvailableMethods.Remove(m);
|
||||||
|
|
||||||
|
// 从 Channels 中移除(并清理 Plot)
|
||||||
|
var toRemoveChannels = Channels.Where(c => c.Fingerprint == fingerprint).ToList();
|
||||||
|
foreach (var c in toRemoveChannels)
|
||||||
|
{
|
||||||
|
if (c.Series != null) Plot.Series.Remove(c.Series);
|
||||||
|
Channels.Remove(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toRemoveMethods.Count > 0 || toRemoveChannels.Count > 0)
|
||||||
|
{
|
||||||
|
Plot.InvalidatePlot(true);
|
||||||
|
StatusMessage = $"CAN{args.Channel} DBC 已卸载,移除 {toRemoveMethods.Count} 个信号";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将单个 CAN 信号添加为监测通道。
|
||||||
|
/// </summary>
|
||||||
|
private void AddCanChannel(CANSignalConfig cfg, string fingerprint, string methodName, bool isDisplayed)
|
||||||
|
{
|
||||||
|
var color = _palette[_colorIndex % _palette.Length];
|
||||||
|
_colorIndex++;
|
||||||
|
|
||||||
|
string displayName = CANSignalBroadcaster.BuildDisplayName(cfg.MessageName, cfg.SignalName);
|
||||||
|
var channel = new MonitorChannelVM
|
||||||
|
{
|
||||||
|
DeviceName = $"CAN{cfg.Channel}",
|
||||||
|
Fingerprint = fingerprint,
|
||||||
|
MethodName = methodName,
|
||||||
|
DisplayName = displayName,
|
||||||
|
Color = color,
|
||||||
|
IsMonitored = true,
|
||||||
|
IsDisplayed = isDisplayed
|
||||||
|
};
|
||||||
|
|
||||||
|
if (channel.IsDisplayed)
|
||||||
|
{
|
||||||
|
channel.Series = new LineSeries
|
||||||
|
{
|
||||||
|
Title = channel.DisplayName,
|
||||||
|
Color = color,
|
||||||
|
StrokeThickness = 1.5
|
||||||
|
};
|
||||||
|
Plot.Series.Add(channel.Series);
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.PropertyChanged += (s, e) =>
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(MonitorChannelVM.IsDisplayed))
|
||||||
|
OnChannelDisplayChanged(channel);
|
||||||
|
};
|
||||||
|
|
||||||
|
Channels.Add(channel);
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 命令处理
|
#region 命令处理
|
||||||
@@ -427,7 +733,7 @@ namespace MonitorModule.ViewModels
|
|||||||
var color = _palette[_colorIndex % _palette.Length];
|
var color = _palette[_colorIndex % _palette.Length];
|
||||||
_colorIndex++;
|
_colorIndex++;
|
||||||
|
|
||||||
var channel = new MonitorChannel
|
var channel = new MonitorChannelVM
|
||||||
{
|
{
|
||||||
DeviceName = method.DeviceName,
|
DeviceName = method.DeviceName,
|
||||||
Fingerprint = method.Fingerprint,
|
Fingerprint = method.Fingerprint,
|
||||||
@@ -448,7 +754,7 @@ namespace MonitorModule.ViewModels
|
|||||||
|
|
||||||
channel.PropertyChanged += (s, e) =>
|
channel.PropertyChanged += (s, e) =>
|
||||||
{
|
{
|
||||||
if (e.PropertyName == nameof(MonitorChannel.IsDisplayed))
|
if (e.PropertyName == nameof(MonitorChannelVM.IsDisplayed))
|
||||||
OnChannelDisplayChanged(channel);
|
OnChannelDisplayChanged(channel);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -498,7 +804,7 @@ namespace MonitorModule.ViewModels
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 显示/隐藏切换与数学变换
|
#region 显示/隐藏切换与数学变换
|
||||||
public void OnChannelDisplayChanged(MonitorChannel channel)
|
public void OnChannelDisplayChanged(MonitorChannelVM channel)
|
||||||
{
|
{
|
||||||
if (channel.IsDisplayed)
|
if (channel.IsDisplayed)
|
||||||
{
|
{
|
||||||
@@ -531,7 +837,7 @@ namespace MonitorModule.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnChannelMathChanged(MonitorChannel channel)
|
public void OnChannelMathChanged(MonitorChannelVM channel)
|
||||||
{
|
{
|
||||||
if (channel.Series == null) return;
|
if (channel.Series == null) return;
|
||||||
var points = channel.DataPoints.ToArray();
|
var points = channel.DataPoints.ToArray();
|
||||||
|
|||||||
110
MonitorModule/Views/Dialogs/ValueLimitView.xaml
Normal file
110
MonitorModule/Views/Dialogs/ValueLimitView.xaml
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
<UserControl x:Class="MonitorModule.Views.Dialogs.ValueLimitView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="clr-namespace:MonitorModule.Views.Dialogs"
|
||||||
|
xmlns:oxy="http://oxyplot.org/wpf"
|
||||||
|
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
|
||||||
|
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
xmlns:prism="http://prismlibrary.com/"
|
||||||
|
xmlns:cons="clr-namespace:UIShare.Converters;assembly=UIShare"
|
||||||
|
Background="White"
|
||||||
|
prism:ViewModelLocator.AutoWireViewModel="True"
|
||||||
|
Height="500"
|
||||||
|
Width="900">
|
||||||
|
<prism:Dialog.WindowStyle>
|
||||||
|
<Style BasedOn="{StaticResource DialogUserManageStyle}"
|
||||||
|
TargetType="Window" />
|
||||||
|
</prism:Dialog.WindowStyle>
|
||||||
|
<GroupBox Padding="0,0,0,0"
|
||||||
|
MouseLeftButtonDown="MouseLeftButtonDown">
|
||||||
|
<GroupBox.Header>
|
||||||
|
<Grid Margin="0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Text="上下限设置"
|
||||||
|
Foreground="White"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Margin="5,0,10,0" />
|
||||||
|
<Button Content="关闭"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="150 0 0 0"
|
||||||
|
Width="60"
|
||||||
|
Command="{Binding CloseCommand}"
|
||||||
|
Height="25" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</GroupBox.Header>
|
||||||
|
<Grid Margin="10">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<DataGrid SelectedItem="{Binding SelectValueLimitVM}"
|
||||||
|
SelectionUnit="FullRow"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
IsReadOnly="False"
|
||||||
|
ItemsSource="{Binding ValueLimitList}"
|
||||||
|
Margin="0,0,0,10">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="信号名"
|
||||||
|
Binding="{Binding SignalName}"
|
||||||
|
Width="auto" />
|
||||||
|
<DataGridTextColumn Header="上限"
|
||||||
|
Binding="{Binding Upper, Mode=TwoWay}"
|
||||||
|
Width="auto" />
|
||||||
|
<DataGridTextColumn Header="下限"
|
||||||
|
Binding="{Binding Lower, Mode=TwoWay}"
|
||||||
|
Width="auto" />
|
||||||
|
<DataGridTextColumn Header="上极限"
|
||||||
|
Binding="{Binding UpperExtreme, Mode=TwoWay}"
|
||||||
|
Width="auto" />
|
||||||
|
<DataGridTextColumn Header="下极限"
|
||||||
|
Binding="{Binding LowerExtreme, Mode=TwoWay}"
|
||||||
|
Width="auto" />
|
||||||
|
<DataGridTextColumn Header="报警状态"
|
||||||
|
Binding="{Binding AlarmSatus}"
|
||||||
|
Width="auto" />
|
||||||
|
|
||||||
|
</DataGrid.Columns>
|
||||||
|
<!--<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu>
|
||||||
|
<MenuItem Header="删除"
|
||||||
|
Foreground="Red"
|
||||||
|
Command="{Binding DeleteCommand}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>-->
|
||||||
|
</DataGrid>
|
||||||
|
<!--<StackPanel Grid.Row="1"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Margin="0,10,0,0">
|
||||||
|
<Label Content="待添加监控值"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<ComboBox ItemsSource="{Binding DeviceSingleList}"
|
||||||
|
SelectedItem="{Binding SelectCurve}"
|
||||||
|
materialDesign:HintAssist.Hint=""
|
||||||
|
MinWidth="130"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<Button Content="添加"
|
||||||
|
Margin="10,0,10,0"
|
||||||
|
Command="{Binding SelectSignalCommand}" />
|
||||||
|
</StackPanel>-->
|
||||||
|
<StackPanel Grid.Row="1"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Margin="0,10,0,0">
|
||||||
|
<Button Content="保存"
|
||||||
|
Width="80"
|
||||||
|
Margin="0,0,10,0"
|
||||||
|
Command="{Binding SaveCommand}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</GroupBox>
|
||||||
|
</UserControl>
|
||||||
36
MonitorModule/Views/Dialogs/ValueLimitView.xaml.cs
Normal file
36
MonitorModule/Views/Dialogs/ValueLimitView.xaml.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
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.Data;
|
||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
using System.Windows.Navigation;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
|
||||||
|
namespace MonitorModule.Views.Dialogs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ValueLimitView.xaml 的交互逻辑
|
||||||
|
/// </summary>
|
||||||
|
public partial class ValueLimitView : UserControl
|
||||||
|
{
|
||||||
|
public ValueLimitView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.LeftButton == MouseButtonState.Pressed)
|
||||||
|
{
|
||||||
|
Window.GetWindow(this)?.DragMove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -89,6 +89,10 @@
|
|||||||
Command="{Binding RefreshDataCommand}"
|
Command="{Binding RefreshDataCommand}"
|
||||||
Padding="12,4" Margin="6,0,0,0"
|
Padding="12,4" Margin="6,0,0,0"
|
||||||
ToolTip="重新绘制图表"/>
|
ToolTip="重新绘制图表"/>
|
||||||
|
<Button Content=" 保存"
|
||||||
|
Command="{Binding SaveCommand}"
|
||||||
|
Padding="12,4" Margin="6,0,0,0"
|
||||||
|
ToolTip="保存到配置文件"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
@@ -333,7 +333,7 @@ namespace TestingModule.ViewModels
|
|||||||
foreach (var type in validTypes)
|
foreach (var type in validTypes)
|
||||||
{
|
{
|
||||||
//拦截没有在设备列表中的设备类型
|
//拦截没有在设备列表中的设备类型
|
||||||
//if (SystemConfig.Instance.DeviceList.Where(x=>x.Remark==type.Name).ToList().Count==0 && type.FullName.Contains("DeviceCommand.Device"))
|
//if (_systemConfig.DeviceList.Where(x=>x.Remark==type.Name).ToList().Count==0 && type.FullName.Contains("DeviceCommand.Device"))
|
||||||
//{
|
//{
|
||||||
// continue;
|
// continue;
|
||||||
//}
|
//}
|
||||||
|
|||||||
152
UIShare/GlobalVariable/CANSignalBroadcaster.cs
Normal file
152
UIShare/GlobalVariable/CANSignalBroadcaster.cs
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
using Model.Models;
|
||||||
|
using Prism.Events;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UIShare.PubEvent;
|
||||||
|
using ZLGUSBCANFD;
|
||||||
|
|
||||||
|
namespace UIShare.GlobalVariable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// CAN 信号广播器(全局单例):
|
||||||
|
/// 统一订阅 GlobalInfo.CanPool 中所有已实例化的 ZLGCANFD 设备,
|
||||||
|
/// 将 DBC 解码后的信号值广播给所有引用该 CAN 设备的作用域,
|
||||||
|
/// 并同步检查各作用域 ValueLimitList 的超限报警。
|
||||||
|
/// </summary>
|
||||||
|
public class CANSignalBroadcaster : IDisposable
|
||||||
|
{
|
||||||
|
private readonly IEventAggregator _eventAggregator;
|
||||||
|
private readonly GlobalInfo _globalInfo;
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
/// <summary>已订阅解码事件的 CANFD 实例 → 委托 映射</summary>
|
||||||
|
private readonly Dictionary<ZLGCANFD, Action<uint, ZDBC.DBCMessage>> _handlers = new();
|
||||||
|
|
||||||
|
public CANSignalBroadcaster(GlobalInfo globalInfo, IEventAggregator eventAggregator)
|
||||||
|
{
|
||||||
|
_globalInfo = globalInfo;
|
||||||
|
_eventAggregator = eventAggregator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 扫描 GlobalInfo.CanPool 中已创建的 CANFD 实例并订阅解码事件。
|
||||||
|
/// 幂等:已订阅的实例不会重复订阅。
|
||||||
|
/// </summary>
|
||||||
|
public void Discover()
|
||||||
|
{
|
||||||
|
if (_disposed) return;
|
||||||
|
|
||||||
|
foreach (var kvp in _globalInfo.CanPool)
|
||||||
|
{
|
||||||
|
string fingerprint = kvp.Key;
|
||||||
|
var lazy = kvp.Value;
|
||||||
|
if (!lazy.IsValueCreated || lazy.Value == null) continue;
|
||||||
|
|
||||||
|
var canfd = lazy.Value;
|
||||||
|
if (_handlers.ContainsKey(canfd)) continue;
|
||||||
|
|
||||||
|
// 使用闭包捕获指纹,回调时即可区分不同 CAN 卡
|
||||||
|
Action<uint, ZDBC.DBCMessage> handler = (channel, msg) => OnDbcMessageDecoded(fingerprint, channel, msg);
|
||||||
|
canfd.OnDbcMessageDecoded += handler;
|
||||||
|
_handlers[canfd] = handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动广播器(目前 Discover 已完成订阅,此处保留以兼容 HardwareDataBroadcaster 的使用模式)。
|
||||||
|
/// </summary>
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
Discover();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DBC 解码回调:提取所有信号,向引用该 CAN 设备的作用域广播,并检查报警。
|
||||||
|
/// </summary>
|
||||||
|
private void OnDbcMessageDecoded(string canFingerprint, uint channel, ZDBC.DBCMessage msg)
|
||||||
|
{
|
||||||
|
if (_disposed) return;
|
||||||
|
|
||||||
|
string signalFingerprint = BuildFingerprint(channel);
|
||||||
|
var now = DateTime.Now;
|
||||||
|
|
||||||
|
// 获取引用该 CAN 设备的所有作用域
|
||||||
|
var scopes = GetScopesForFingerprint(canFingerprint);
|
||||||
|
if (scopes.Count == 0) return;
|
||||||
|
|
||||||
|
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)) continue;
|
||||||
|
|
||||||
|
double physicalValue = signal.nRawvalue * signal.nFactor + signal.nOffset;
|
||||||
|
string methodName = BuildMethodName(msg.nID, signalName);
|
||||||
|
|
||||||
|
foreach (var scope in scopes)
|
||||||
|
{
|
||||||
|
_eventAggregator.GetEvent<HardwareDataReportedEvent>().Publish(new HardwareReportArgs
|
||||||
|
{
|
||||||
|
Scope = scope,
|
||||||
|
HardwareFingerprint = signalFingerprint,
|
||||||
|
MethodName = methodName,
|
||||||
|
Value = physicalValue,
|
||||||
|
Time = now
|
||||||
|
});
|
||||||
|
|
||||||
|
ValueLimitAlarmHelper.CheckAlarm(scope, signalFingerprint, methodName, physicalValue, _globalInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>获取指定硬件指纹当前被哪些作用域引用</summary>
|
||||||
|
private List<string> GetScopesForFingerprint(string fingerprint)
|
||||||
|
{
|
||||||
|
if (_globalInfo.DeviceAndScopeDic.TryGetValue(fingerprint, out var lazy))
|
||||||
|
{
|
||||||
|
var scopeList = lazy.Value;
|
||||||
|
lock (scopeList) return scopeList.ToList();
|
||||||
|
}
|
||||||
|
return new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 生成 CAN 信号的 DisplayName 格式:"{MessageName}.{SignalName}"
|
||||||
|
/// </summary>
|
||||||
|
public static string BuildDisplayName(string messageName, string signalName)
|
||||||
|
{
|
||||||
|
return $"{messageName}.{signalName}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 生成 CAN 信号的 MethodName 格式:"{MessageId:X}.{SignalName}"
|
||||||
|
/// </summary>
|
||||||
|
public static string BuildMethodName(uint messageId, string signalName)
|
||||||
|
{
|
||||||
|
return $"{messageId:X}.{signalName}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 生成 CAN 信号的 Fingerprint 格式:"CAN:{Channel}"
|
||||||
|
/// </summary>
|
||||||
|
public static string BuildFingerprint(uint channel)
|
||||||
|
{
|
||||||
|
return $"CAN:{channel}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed) return;
|
||||||
|
_disposed = true;
|
||||||
|
|
||||||
|
foreach (var kvp in _handlers)
|
||||||
|
{
|
||||||
|
if (kvp.Key != null)
|
||||||
|
kvp.Key.OnDbcMessageDecoded -= kvp.Value;
|
||||||
|
}
|
||||||
|
_handlers.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
using DeviceCommand.Base;
|
using DeviceCommand.Base;
|
||||||
using Logger;
|
using Logger;
|
||||||
using Model.Models;
|
using Model.Models;
|
||||||
|
using Prism.Events;
|
||||||
using Prism.Ioc;
|
using Prism.Ioc;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.IO.Ports;
|
using System.IO.Ports;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using UIShare.PubEvent;
|
||||||
using UIShare.UIViewModel;
|
using UIShare.UIViewModel;
|
||||||
using ZLGUSBCANFD;
|
using ZLGUSBCANFD;
|
||||||
|
|
||||||
@@ -22,6 +25,7 @@ namespace UIShare.GlobalVariable
|
|||||||
public SystemConfig _systemConfig { get; set; }
|
public SystemConfig _systemConfig { get; set; }
|
||||||
private readonly GlobalInfo _globalInfo;
|
private readonly GlobalInfo _globalInfo;
|
||||||
private readonly string _scopeName;
|
private readonly string _scopeName;
|
||||||
|
private readonly IEventAggregator _eventAggregator;
|
||||||
|
|
||||||
/// <summary>按 DeviceName 索引的设备字典,便于业务层按名取实例。</summary>
|
/// <summary>按 DeviceName 索引的设备字典,便于业务层按名取实例。</summary>
|
||||||
public IDictionary<string, IBaseInterface> DeviceMap { get; private set; }
|
public IDictionary<string, IBaseInterface> DeviceMap { get; private set; }
|
||||||
@@ -31,10 +35,11 @@ namespace UIShare.GlobalVariable
|
|||||||
private static readonly IReadOnlyDictionary<string, Type> _deviceTypeMap = BuildDeviceTypeMap();
|
private static readonly IReadOnlyDictionary<string, Type> _deviceTypeMap = BuildDeviceTypeMap();
|
||||||
public ZLGCANFD CANFD { get; set; }
|
public ZLGCANFD CANFD { get; set; }
|
||||||
|
|
||||||
public DeviceManager(SystemConfig systemConfig, GlobalInfo globalInfo)
|
public DeviceManager(SystemConfig systemConfig, GlobalInfo globalInfo, IEventAggregator eventAggregator)
|
||||||
{
|
{
|
||||||
_systemConfig = systemConfig;
|
_systemConfig = systemConfig;
|
||||||
_globalInfo = globalInfo;
|
_globalInfo = globalInfo;
|
||||||
|
_eventAggregator = eventAggregator;
|
||||||
// 用 SystemConfig.Title 作为作用域唯一标识,无需反查 ConfigDic
|
// 用 SystemConfig.Title 作为作用域唯一标识,无需反查 ConfigDic
|
||||||
_scopeName = _systemConfig.Title;
|
_scopeName = _systemConfig.Title;
|
||||||
InitDevices();
|
InitDevices();
|
||||||
@@ -96,8 +101,10 @@ namespace UIShare.GlobalVariable
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 按指纹全局唯一创建 ZLGCANFD 实例(maxChannels 默认 4,对应 USBCANFD-400U)
|
// 按指纹全局唯一创建 ZLGCANFD 实例(maxChannels 默认 4,对应 USBCANFD-400U)
|
||||||
|
// 波特率与终端电阻从 CANConfigVM 传入,初始化并启动通道 时直接使用
|
||||||
var canLazy = _globalInfo.CanPool.GetOrAdd(fingerprint, key => new Lazy<ZLGCANFD>(() =>
|
var canLazy = _globalInfo.CanPool.GetOrAdd(fingerprint, key => new Lazy<ZLGCANFD>(() =>
|
||||||
new ZLGCANFD(config.CANConfig.DeviceType, config.CANConfig.DeviceIndex, 4)));
|
new ZLGCANFD(config.CANConfig.DeviceType, config.CANConfig.DeviceIndex, 4,
|
||||||
|
config.CANConfig.ABitBaud, config.CANConfig.DBitBaud, config.CANConfig.EnableTerminalResistance)));
|
||||||
|
|
||||||
_systemConfig.CANFD = canLazy.Value;
|
_systemConfig.CANFD = canLazy.Value;
|
||||||
CANFD = canLazy.Value;
|
CANFD = canLazy.Value;
|
||||||
@@ -190,15 +197,22 @@ namespace UIShare.GlobalVariable
|
|||||||
|
|
||||||
public async Task ConnectAllDevices(CancellationToken ct = default)
|
public async Task ConnectAllDevices(CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
if (_systemConfig?.DeviceList == null || DeviceMap.Count == 0) return;
|
if (_systemConfig?.DeviceList == null) return;
|
||||||
|
|
||||||
var tasks = new List<Task>();
|
var tasks = new List<Task>();
|
||||||
foreach (var info in _systemConfig.DeviceList)
|
foreach (var info in _systemConfig.DeviceList)
|
||||||
{
|
{
|
||||||
if (info == null || !info.IsEnabled) continue;
|
if (info == null || !info.IsEnabled) continue;
|
||||||
if (string.IsNullOrWhiteSpace(info.DeviceName)) continue;
|
if (string.IsNullOrWhiteSpace(info.DeviceName)) continue;
|
||||||
if (!DeviceMap.TryGetValue(info.DeviceName, out var device)) continue;
|
|
||||||
|
|
||||||
|
// CAN 设备:直接打开 CAN 卡
|
||||||
|
if (string.Equals(info.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
tasks.Add(ConnectCanAsync(info));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!DeviceMap.TryGetValue(info.DeviceName, out var device)) continue;
|
||||||
tasks.Add(ConnectInternalAsync(info, device, ct));
|
tasks.Add(ConnectInternalAsync(info, device, ct));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,15 +228,28 @@ namespace UIShare.GlobalVariable
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var info = _systemConfig?.DeviceList?
|
||||||
|
.FirstOrDefault(d => d != null && string.Equals(d.DeviceName, deviceName, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
if (info == null)
|
||||||
|
{
|
||||||
|
LoggerHelper.Warn($"ConnectSpecifiedDevice:未找到设备配置 [{deviceName}]。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAN 设备:直接打开 CAN 卡
|
||||||
|
if (string.Equals(info.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
await ConnectCanAsync(info);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!DeviceMap.TryGetValue(deviceName, out var device))
|
if (!DeviceMap.TryGetValue(deviceName, out var device))
|
||||||
{
|
{
|
||||||
LoggerHelper.Warn($"ConnectSpecifiedDevice:未找到设备 [{deviceName}]。");
|
LoggerHelper.Warn($"ConnectSpecifiedDevice:未找到设备 [{deviceName}]。");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var info = _systemConfig?.DeviceList?
|
|
||||||
.FirstOrDefault(d => d != null && string.Equals(d.DeviceName, deviceName, StringComparison.OrdinalIgnoreCase));
|
|
||||||
|
|
||||||
await ConnectInternalAsync(info, device, ct);
|
await ConnectInternalAsync(info, device, ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,15 +260,21 @@ namespace UIShare.GlobalVariable
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(deviceName)) return;
|
if (string.IsNullOrWhiteSpace(deviceName)) return;
|
||||||
|
|
||||||
|
var info = _systemConfig?.DeviceList?
|
||||||
|
.FirstOrDefault(d => d != null && string.Equals(d.DeviceName, deviceName, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
// CAN 设备:直接关闭 CAN 卡
|
||||||
|
if (info != null && string.Equals(info.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
await CloseCanAsync(info);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
IBaseInterface? device;
|
IBaseInterface? device;
|
||||||
DeviceInfoVM? info;
|
|
||||||
|
|
||||||
lock (_lockObj)
|
lock (_lockObj)
|
||||||
{
|
{
|
||||||
if (!DeviceMap.TryGetValue(deviceName, out device)) return;
|
if (!DeviceMap.TryGetValue(deviceName, out device)) return;
|
||||||
|
|
||||||
info = _systemConfig?.DeviceList?
|
|
||||||
.FirstOrDefault(d => d != null && string.Equals(d.DeviceName, deviceName, StringComparison.OrdinalIgnoreCase));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await CloseInternalAsync(info, device);
|
await CloseInternalAsync(info, device);
|
||||||
@@ -256,7 +289,7 @@ namespace UIShare.GlobalVariable
|
|||||||
|
|
||||||
lock (_lockObj)
|
lock (_lockObj)
|
||||||
{
|
{
|
||||||
if (DeviceMap.Count == 0) return;
|
if (DeviceMap.Count == 0 && (CANFD == null)) return;
|
||||||
|
|
||||||
foreach (var kvp in DeviceMap)
|
foreach (var kvp in DeviceMap)
|
||||||
{
|
{
|
||||||
@@ -269,6 +302,17 @@ namespace UIShare.GlobalVariable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CAN 设备:直接关闭 CAN 卡
|
||||||
|
if (CANFD != null)
|
||||||
|
{
|
||||||
|
var canInfo = _systemConfig?.DeviceList?
|
||||||
|
.FirstOrDefault(d => d != null && string.Equals(d.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (canInfo != null)
|
||||||
|
{
|
||||||
|
tasks.Add(CloseCanAsync(canInfo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await Task.WhenAll(tasks);
|
await Task.WhenAll(tasks);
|
||||||
LoggerHelper.Info("所有设备已执行关闭操作。");
|
LoggerHelper.Info("所有设备已执行关闭操作。");
|
||||||
}
|
}
|
||||||
@@ -366,6 +410,131 @@ namespace UIShare.GlobalVariable
|
|||||||
}
|
}
|
||||||
return await sp.ConnectAsync(ct);
|
return await sp.ConnectAsync(ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 打开 CAN 卡:打开设备 + 初始化并启动所有通道 + 自动加载 DBC + 启动信号广播
|
||||||
|
/// </summary>
|
||||||
|
private async Task<bool> ConnectCanAsync(DeviceInfoVM info)
|
||||||
|
{
|
||||||
|
string name = info.DeviceName ?? "CAN";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (CANFD == null)
|
||||||
|
{
|
||||||
|
LoggerHelper.Warn($"CAN 设备 [{name}] 尚未实例化,无法连接。");
|
||||||
|
info.IsConnected = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.IsConnected)
|
||||||
|
{
|
||||||
|
LoggerHelper.Info($"CAN 设备 [{name}] 已连接,跳过。");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok = await Task.Run(() =>
|
||||||
|
{
|
||||||
|
if (!CANFD.打开设备()) return false;
|
||||||
|
// 自动加载 DBC 文件
|
||||||
|
if (_systemConfig?.DBCAutoLoadList != null)
|
||||||
|
{
|
||||||
|
foreach (var item in _systemConfig.DBCAutoLoadList)
|
||||||
|
{
|
||||||
|
if (item.DBCChannel < 0 || item.DBCChannel >= 4) continue;
|
||||||
|
if (string.IsNullOrWhiteSpace(item.DBCFilePath)) continue;
|
||||||
|
|
||||||
|
if (!File.Exists(item.DBCFilePath))
|
||||||
|
{
|
||||||
|
// 发布 DBC 卸载事件,通知监控系统清除对应信号
|
||||||
|
_eventAggregator.GetEvent<DBCUnloadedEvent>().Publish(new DBCUnloadedArgs
|
||||||
|
{
|
||||||
|
Channel = (uint)item.DBCChannel,
|
||||||
|
Scope = _scopeName
|
||||||
|
});
|
||||||
|
LoggerHelper.Warn($"CAN 通道 {item.DBCChannel} 自动加载 DBC 失败:文件不存在 [{item.DBCFilePath}]");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
CANFD.初始化并启动通道((uint)item.DBCChannel);
|
||||||
|
bool loadOk = CANFD.加载通道DBC文件((uint)item.DBCChannel, item.DBCFilePath);
|
||||||
|
if (loadOk)
|
||||||
|
{
|
||||||
|
LoggerHelper.Info($"CAN 通道 {item.DBCChannel} 已自动加载 DBC:{item.DBCFilePath}");
|
||||||
|
// 发布 DBC 加载完成事件,通知监控系统刷新信号列表
|
||||||
|
_eventAggregator.GetEvent<DBCLoadedEvent>().Publish(new DBCLoadedArgs
|
||||||
|
{
|
||||||
|
Channel = (uint)item.DBCChannel,
|
||||||
|
Scope = _scopeName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LoggerHelper.Warn($"CAN 通道 {item.DBCChannel} 自动加载 DBC 失败:{item.DBCFilePath}");
|
||||||
|
_eventAggregator.GetEvent<DBCUnloadedEvent>().Publish(new DBCUnloadedArgs
|
||||||
|
{
|
||||||
|
Channel = (uint)item.DBCChannel,
|
||||||
|
Scope = _scopeName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
info.IsConnected = ok;
|
||||||
|
|
||||||
|
if (ok)
|
||||||
|
LoggerHelper.Info($"CAN 设备 [{name}] 连接成功,已初始化 {CANFD.DBCParser.MaxChannels} 个通道。");
|
||||||
|
else
|
||||||
|
LoggerHelper.Warn($"CAN 设备 [{name}] 连接失败。");
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
info.IsConnected = false;
|
||||||
|
var inner = ex.InnerException?.Message ?? ex.Message;
|
||||||
|
LoggerHelper.ErrorWithNotify(_scopeName, $"CAN 设备 [{name}] 连接异常:{inner}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 关闭 CAN 卡
|
||||||
|
/// </summary>
|
||||||
|
private async Task CloseCanAsync(DeviceInfoVM info)
|
||||||
|
{
|
||||||
|
string name = info.DeviceName ?? "CAN";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (CANFD == null)
|
||||||
|
{
|
||||||
|
info.IsConnected = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info.IsConnected)
|
||||||
|
{
|
||||||
|
LoggerHelper.Info($"CAN 设备 [{name}] 本就处于断开状态。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Run(() => CANFD.关闭CAN卡设备());
|
||||||
|
LoggerHelper.Info($"CAN 设备 [{name}] 已成功关闭连接。");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
var inner = ex.InnerException?.Message ?? ex.Message;
|
||||||
|
LoggerHelper.Error($"CAN 设备 [{name}] 关闭连接时出现异常: {inner}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
info.IsConnected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static IReadOnlyDictionary<string, Type> BuildDeviceTypeMap()
|
private static IReadOnlyDictionary<string, Type> BuildDeviceTypeMap()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -179,6 +179,9 @@ namespace UIShare.GlobalVariable
|
|||||||
Value = value,
|
Value = value,
|
||||||
Time = now
|
Time = now
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 同步检查该作用域 ValueLimitList 是否超限
|
||||||
|
ValueLimitAlarmHelper.CheckAlarm(scope, entry.Fingerprint, entry.MethodName, value, _globalInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
|||||||
@@ -30,6 +30,15 @@ namespace UIShare.GlobalVariable
|
|||||||
public string DefaultDBCFilePath { get; set; } = "";
|
public string DefaultDBCFilePath { get; set; } = "";
|
||||||
public ObservableCollection<DeviceInfoVM> DeviceList = new();
|
public ObservableCollection<DeviceInfoVM> DeviceList = new();
|
||||||
public ObservableCollection<SharedParameter> SharedParameterList = new();
|
public ObservableCollection<SharedParameter> SharedParameterList = new();
|
||||||
|
public ObservableCollection<CANSignalConfig> ConfigurationList = new();
|
||||||
|
public ObservableCollection<AutoDBCLoadItem> DBCAutoLoadList = new();
|
||||||
|
public ObservableCollection<ValueLimitVM> ValueLimitList = new();
|
||||||
|
[JsonIgnore]
|
||||||
|
public ObservableCollection<MonitorChannelVM> Channels = new();
|
||||||
|
/// <summary>
|
||||||
|
/// 监测通道持久化配置列表(包含 Fingerprint、MethodName、IsDisplayed)
|
||||||
|
/// </summary>
|
||||||
|
public ObservableCollection<MonitorChannelConfig> MonitorChannels = new();
|
||||||
public ZLGCANFD CANFD = new();
|
public ZLGCANFD CANFD = new();
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public ObservableCollection<ParameterVM> ParameterList = new()
|
public ObservableCollection<ParameterVM> ParameterList = new()
|
||||||
|
|||||||
56
UIShare/GlobalVariable/ValueLimitAlarmHelper.cs
Normal file
56
UIShare/GlobalVariable/ValueLimitAlarmHelper.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using UIShare.UIViewModel;
|
||||||
|
|
||||||
|
namespace UIShare.GlobalVariable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 值限制报警检查辅助类:供各广播器在采样到信号值时统一判断是否超限。
|
||||||
|
/// </summary>
|
||||||
|
public static class ValueLimitAlarmHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 根据指定作用域的 ValueLimitList 检查当前值是否超限,并更新报警状态。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scope">作用域名称</param>
|
||||||
|
/// <param name="fingerprint">硬件指纹</param>
|
||||||
|
/// <param name="methodName">方法名/信号标识</param>
|
||||||
|
/// <param name="value">当前采样值</param>
|
||||||
|
/// <param name="globalInfo">全局信息</param>
|
||||||
|
public static void CheckAlarm(string scope, string fingerprint, string methodName, double value, GlobalInfo globalInfo)
|
||||||
|
{
|
||||||
|
if (globalInfo?.ConfigDic == null) return;
|
||||||
|
if (!globalInfo.ConfigDic.TryGetValue(scope, out var systemConfig)) return;
|
||||||
|
if (systemConfig.ValueLimitList == null) return;
|
||||||
|
|
||||||
|
var limit = systemConfig.ValueLimitList.FirstOrDefault(x =>
|
||||||
|
x.Fingerprint == fingerprint && x.MethodName == methodName);
|
||||||
|
if (limit == null) return;
|
||||||
|
if (value > limit.Upper)
|
||||||
|
{
|
||||||
|
limit.IsAlarm = true;
|
||||||
|
limit.AlarmSatus = AlarmStatus.超上限;
|
||||||
|
}
|
||||||
|
else if (value < limit.Lower)
|
||||||
|
{
|
||||||
|
limit.IsAlarm = true;
|
||||||
|
limit.AlarmSatus = AlarmStatus.超下限;
|
||||||
|
}
|
||||||
|
else if(value > limit.UpperExtreme)
|
||||||
|
{
|
||||||
|
limit.IsAlarm = true;
|
||||||
|
limit.AlarmSatus = AlarmStatus.超上极限;
|
||||||
|
}
|
||||||
|
else if (value < limit.LowerExtreme)
|
||||||
|
{
|
||||||
|
limit.IsAlarm = true;
|
||||||
|
limit.AlarmSatus = AlarmStatus.超下极限;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
limit.IsAlarm = false;
|
||||||
|
limit.AlarmSatus = AlarmStatus.未报警;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
UIShare/PubEvent/DBCLoadedEvent.cs
Normal file
17
UIShare/PubEvent/DBCLoadedEvent.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace UIShare.PubEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// DBC 加载完成事件:通知订阅者某个通道的 DBC 已加载,可以刷新信号列表。
|
||||||
|
/// </summary>
|
||||||
|
public class DBCLoadedEvent : PubSubEvent<DBCLoadedArgs>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DBCLoadedArgs
|
||||||
|
{
|
||||||
|
public uint Channel { get; set; }
|
||||||
|
public string Scope { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
18
UIShare/PubEvent/DBCUnloadedEvent.cs
Normal file
18
UIShare/PubEvent/DBCUnloadedEvent.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace UIShare.PubEvent
|
||||||
|
{
|
||||||
|
public class DBCUnloadedEvent : PubSubEvent<DBCUnloadedArgs>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DBCUnloadedArgs
|
||||||
|
{
|
||||||
|
public uint Channel { get; set; }
|
||||||
|
public string Scope { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
<PackageReference Include="MaterialDesignThemes" Version="5.3.0" />
|
<PackageReference Include="MaterialDesignThemes" Version="5.3.0" />
|
||||||
<PackageReference Include="MaterialDesignThemes.MahApps" Version="5.3.0" />
|
<PackageReference Include="MaterialDesignThemes.MahApps" Version="5.3.0" />
|
||||||
<PackageReference Include="Notifications.Wpf.Core" Version="2.0.1" />
|
<PackageReference Include="Notifications.Wpf.Core" Version="2.0.1" />
|
||||||
|
<PackageReference Include="OxyPlot.Wpf" Version="2.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Common\Common.csproj" />
|
<ProjectReference Include="..\Common\Common.csproj" />
|
||||||
|
|||||||
33
UIShare/UIViewModel/AutoDBCLoadItemVM.cs
Normal file
33
UIShare/UIViewModel/AutoDBCLoadItemVM.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
namespace UIShare.UIViewModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// CAN 通道枚举(对应 ZLGCANFD 通道选择)
|
||||||
|
/// </summary>
|
||||||
|
public enum APP_CHANNEL
|
||||||
|
{
|
||||||
|
CH0 = 0,
|
||||||
|
CH1 = 1,
|
||||||
|
CH2 = 2,
|
||||||
|
CH3 = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DBC 自动加载配置项
|
||||||
|
/// </summary>
|
||||||
|
public class AutoDBCLoadItem : BindableBase
|
||||||
|
{
|
||||||
|
private int _dbcChannel;
|
||||||
|
public int DBCChannel
|
||||||
|
{
|
||||||
|
get => _dbcChannel;
|
||||||
|
set => SetProperty(ref _dbcChannel, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _dbcFilePath;
|
||||||
|
public string DBCFilePath
|
||||||
|
{
|
||||||
|
get => _dbcFilePath;
|
||||||
|
set => SetProperty(ref _dbcFilePath, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
UIShare/UIViewModel/CANSignalConfigVM.cs
Normal file
14
UIShare/UIViewModel/CANSignalConfigVM.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace UIShare.UIViewModel
|
||||||
|
{
|
||||||
|
public class CANSignalConfig
|
||||||
|
{
|
||||||
|
public int Channel { get; set; }
|
||||||
|
public int MessageID { get; set; }
|
||||||
|
public string MessageName { get; set; }
|
||||||
|
public string SignalName { get; set; }
|
||||||
|
public int CollectionInterval { get; set; }
|
||||||
|
public Guid CollectionID { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,12 +5,9 @@
|
|||||||
// 字段声明
|
// 字段声明
|
||||||
private byte _通道;
|
private byte _通道;
|
||||||
private int _报文ID;
|
private int _报文ID;
|
||||||
private ulong _时间戳;
|
private double _时间戳;
|
||||||
private byte _长度;
|
private byte _长度;
|
||||||
private bool _fd;
|
|
||||||
private bool _isTx;
|
|
||||||
private byte[] _bytes = new byte[64];
|
|
||||||
private string _报文;
|
|
||||||
|
|
||||||
// 通道属性
|
// 通道属性
|
||||||
public byte 通道
|
public byte 通道
|
||||||
@@ -27,7 +24,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 时间戳属性
|
// 时间戳属性
|
||||||
public ulong 时间戳
|
public double 时间戳
|
||||||
{
|
{
|
||||||
get { return _时间戳; }
|
get { return _时间戳; }
|
||||||
set { SetProperty(ref _时间戳, value); }
|
set { SetProperty(ref _时间戳, value); }
|
||||||
@@ -40,32 +37,6 @@
|
|||||||
set { SetProperty(ref _长度, value); }
|
set { SetProperty(ref _长度, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// FD属性
|
|
||||||
public bool FD
|
|
||||||
{
|
|
||||||
get { return _fd; }
|
|
||||||
set { SetProperty(ref _fd, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsTx属性
|
|
||||||
public bool IsTx
|
|
||||||
{
|
|
||||||
get { return _isTx; }
|
|
||||||
set { SetProperty(ref _isTx, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes属性
|
|
||||||
public byte[] Bytes
|
|
||||||
{
|
|
||||||
get { return _bytes; }
|
|
||||||
set { SetProperty(ref _bytes, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
// 报文属性
|
|
||||||
public string 报文
|
|
||||||
{
|
|
||||||
get { return _报文; }
|
|
||||||
set { SetProperty(ref _报文, value); }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace UIShare.UIViewModel
|
namespace UIShare.UIViewModel
|
||||||
{
|
{
|
||||||
public class DeviceInfoVM : BindableBase
|
public class DeviceInfoVM : BindableBase
|
||||||
@@ -32,7 +34,8 @@ namespace UIShare.UIViewModel
|
|||||||
set => SetProperty(ref _isEnabled, value);
|
set => SetProperty(ref _isEnabled, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _isConnected;
|
private bool _isConnected=false;
|
||||||
|
[JsonIgnore]
|
||||||
public bool IsConnected
|
public bool IsConnected
|
||||||
{
|
{
|
||||||
get => _isConnected;
|
get => _isConnected;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using System.Collections.Concurrent;
|
|||||||
using Prism.Mvvm;
|
using Prism.Mvvm;
|
||||||
using NCalc;
|
using NCalc;
|
||||||
|
|
||||||
namespace MonitorModule.ViewModels
|
namespace UIShare.UIViewModel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 监测通道:一个设备方法对应一个通道。
|
/// 监测通道:一个设备方法对应一个通道。
|
||||||
@@ -12,7 +12,7 @@ namespace MonitorModule.ViewModels
|
|||||||
/// - IsMonitored=true 时始终记录 DataPoints
|
/// - IsMonitored=true 时始终记录 DataPoints
|
||||||
/// - IsDisplayed=true 时才创建 LineSeries 并绘制
|
/// - IsDisplayed=true 时才创建 LineSeries 并绘制
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MonitorChannel : BindableBase
|
public class MonitorChannelVM : BindableBase
|
||||||
{
|
{
|
||||||
// ===== 标识 =====
|
// ===== 标识 =====
|
||||||
public string DeviceName { get; init; } = string.Empty;
|
public string DeviceName { get; init; } = string.Empty;
|
||||||
17
UIShare/UIViewModel/MonitorChannelConfig.cs
Normal file
17
UIShare/UIViewModel/MonitorChannelConfig.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace UIShare.UIViewModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 监测通道持久化配置(用于 JSON 保存/恢复)。
|
||||||
|
/// </summary>
|
||||||
|
public class MonitorChannelConfig
|
||||||
|
{
|
||||||
|
/// <summary>硬件指纹(物理设备唯一标识)</summary>
|
||||||
|
public string Fingerprint { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>监测方法名</summary>
|
||||||
|
public string MethodName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>是否在图表上显示该通道的线图</summary>
|
||||||
|
public bool IsDisplayed { get; set; } = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
108
UIShare/UIViewModel/ValueLimitVM.cs
Normal file
108
UIShare/UIViewModel/ValueLimitVM.cs
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
using Prism.Mvvm;
|
||||||
|
|
||||||
|
namespace UIShare.UIViewModel
|
||||||
|
{
|
||||||
|
// 继承 BindableBase 使得属性变更时能实时通知 UI 刷新
|
||||||
|
public class ValueLimitVM : BindableBase
|
||||||
|
{
|
||||||
|
private string _signalName = string.Empty;
|
||||||
|
private string _fingerprint = string.Empty;
|
||||||
|
private string _methodName = string.Empty;
|
||||||
|
private double _upper;
|
||||||
|
private double _lower;
|
||||||
|
private double _upperExtreme;
|
||||||
|
private double _lowerExtreme;
|
||||||
|
private bool _isAlarm = false;
|
||||||
|
private AlarmStatus _alarmStatus = AlarmStatus.未报警;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 信号名(显示用)
|
||||||
|
/// </summary>
|
||||||
|
public string SignalName
|
||||||
|
{
|
||||||
|
get => _signalName;
|
||||||
|
set => SetProperty(ref _signalName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 硬件指纹(物理设备唯一标识,用于超限匹配)
|
||||||
|
/// </summary>
|
||||||
|
public string Fingerprint
|
||||||
|
{
|
||||||
|
get => _fingerprint;
|
||||||
|
set => SetProperty(ref _fingerprint, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 方法名(唯一标识,用于超限匹配)
|
||||||
|
/// </summary>
|
||||||
|
public string MethodName
|
||||||
|
{
|
||||||
|
get => _methodName;
|
||||||
|
set => SetProperty(ref _methodName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上限
|
||||||
|
/// </summary>
|
||||||
|
public double Upper
|
||||||
|
{
|
||||||
|
get => _upper;
|
||||||
|
set => SetProperty(ref _upper, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 下限
|
||||||
|
/// </summary>
|
||||||
|
public double Lower
|
||||||
|
{
|
||||||
|
get => _lower;
|
||||||
|
set => SetProperty(ref _lower, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上极限
|
||||||
|
/// </summary>
|
||||||
|
public double UpperExtreme
|
||||||
|
{
|
||||||
|
get => _upperExtreme;
|
||||||
|
set => SetProperty(ref _upperExtreme, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 下极限
|
||||||
|
/// </summary>
|
||||||
|
public double LowerExtreme
|
||||||
|
{
|
||||||
|
get => _lowerExtreme;
|
||||||
|
set => SetProperty(ref _lowerExtreme, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否报警
|
||||||
|
/// </summary>
|
||||||
|
public bool IsAlarm
|
||||||
|
{
|
||||||
|
get => _isAlarm;
|
||||||
|
set => SetProperty(ref _isAlarm, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 警报状态类型
|
||||||
|
/// </summary>
|
||||||
|
public AlarmStatus AlarmSatus
|
||||||
|
{
|
||||||
|
get => _alarmStatus;
|
||||||
|
set => SetProperty(ref _alarmStatus, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AlarmStatus
|
||||||
|
{
|
||||||
|
未报警 = 0, // 未报警
|
||||||
|
超上限 = 1, // 超上限
|
||||||
|
超下限 = 2, // 超下限
|
||||||
|
超上极限 = 3, // 超上极限
|
||||||
|
超下极限 = 4, // 超下极限
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,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 Prism.Dialogs;
|
||||||
|
|
||||||
namespace UIShare.ViewModelBase
|
namespace UIShare.ViewModelBase
|
||||||
{
|
{
|
||||||
@@ -11,12 +12,41 @@ namespace UIShare.ViewModelBase
|
|||||||
{
|
{
|
||||||
public DialogCloseListener RequestClose { get; set; }
|
public DialogCloseListener RequestClose { get; set; }
|
||||||
public IEventAggregator _eventAggregator;
|
public IEventAggregator _eventAggregator;
|
||||||
|
public IDialogService _dialogService;
|
||||||
private INotificationManager _notificationManager;
|
private INotificationManager _notificationManager;
|
||||||
public DialogViewModelBase(IContainerProvider containerProvider)
|
public DialogViewModelBase(IContainerProvider containerProvider)
|
||||||
{
|
{
|
||||||
_eventAggregator = containerProvider.Resolve<IEventAggregator>();
|
_eventAggregator = containerProvider.Resolve<IEventAggregator>();
|
||||||
|
_dialogService = containerProvider.Resolve<IDialogService>();
|
||||||
_notificationManager = containerProvider.Resolve<INotificationManager>();
|
_notificationManager = containerProvider.Resolve<INotificationManager>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void ShowInfoMessageBox(string Message, Action callback)
|
||||||
|
{
|
||||||
|
var dialogParams = new DialogParameters();
|
||||||
|
dialogParams.Add("Title", "提示");
|
||||||
|
dialogParams.Add("Message", Message);
|
||||||
|
dialogParams.Add("Icon", "info");
|
||||||
|
dialogParams.Add("ShowOk", true);
|
||||||
|
_dialogService.ShowDialog("MessageBoxView", dialogParams, result =>
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void ShowErrorMessageBox(string Message, Action callback)
|
||||||
|
{
|
||||||
|
var dialogParams = new DialogParameters();
|
||||||
|
dialogParams.Add("Title", "错误");
|
||||||
|
dialogParams.Add("Message", Message);
|
||||||
|
dialogParams.Add("Icon", "info");
|
||||||
|
dialogParams.Add("ShowOk", true);
|
||||||
|
_dialogService.ShowDialog("MessageBoxView", dialogParams, result =>
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#region Dialog
|
#region Dialog
|
||||||
|
|
||||||
public virtual bool CanCloseDialog() => true;
|
public virtual bool CanCloseDialog() => true;
|
||||||
|
|||||||
@@ -34,23 +34,38 @@ namespace ZLGUSBCANFD
|
|||||||
private readonly uint _deviceIndex; // 设备索引
|
private readonly uint _deviceIndex; // 设备索引
|
||||||
private readonly int _maxChannels; // 动态通道数
|
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>
|
/// <summary>
|
||||||
/// 事件:当接收到 CAN/CANFD 报文并且通过 DBC 成功解析后触发
|
/// 事件:当接收到 CAN/CANFD 报文并且通过 DBC 成功解析后触发
|
||||||
/// 参数:uint 通道号 (0,1,2,3), ZDBC.DBCMessage 解析后的DBC消息结构体
|
/// 参数:uint 通道号 (0,1,2,3), ZDBC.DBCMessage 解析后的DBC消息结构体
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<uint, ZDBC.DBCMessage>? OnDbcMessageDecoded;
|
public event Action<uint, ZDBC.DBCMessage>? OnDbcMessageDecoded;
|
||||||
|
|
||||||
public ZLGCANFD(uint deviceType = 76, uint deviceIndex = 0, int maxChannels = 4)
|
public ZLGCANFD(uint deviceType = 76, uint deviceIndex = 0, int maxChannels = 4,
|
||||||
|
string abitBaud = "500000", string dbitBaud = "2000000", bool enableTerminalResistance = true)
|
||||||
{
|
{
|
||||||
_deviceType = deviceType;
|
_deviceType = deviceType;
|
||||||
_deviceIndex = deviceIndex;
|
_deviceIndex = deviceIndex;
|
||||||
_maxChannels = maxChannels;
|
_maxChannels = maxChannels;
|
||||||
|
_abitBaud = abitBaud;
|
||||||
|
_dbitBaud = dbitBaud;
|
||||||
|
_enableTerminalResistance = enableTerminalResistance;
|
||||||
|
|
||||||
// 初始化通道相关状态数组
|
// 初始化通道相关状态数组
|
||||||
_channelHandles = new IntPtr[_maxChannels];
|
_channelHandles = new IntPtr[_maxChannels];
|
||||||
_dbcHandles = new uint[_maxChannels];
|
_dbcHandles = new uint[_maxChannels];
|
||||||
_isDbcLoadedArray = new bool[_maxChannels];
|
_isDbcLoadedArray = new bool[_maxChannels];
|
||||||
_channelLocks = new object[_maxChannels];
|
_channelLocks = new object[_maxChannels];
|
||||||
|
DBCParser = new ZLGDBCParser(_maxChannels);
|
||||||
|
|
||||||
for (int i = 0; i < _maxChannels; i++)
|
for (int i = 0; i < _maxChannels; i++)
|
||||||
{
|
{
|
||||||
@@ -74,9 +89,9 @@ namespace ZLGUSBCANFD
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 针对特定通道进行参数初始化并启动
|
/// 针对特定通道进行参数初始化并启动(使用构造时传入的波特率与终端电阻配置)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual bool 初始化并启动通道(uint 通道号, string abitBaud = "500000", string dbitBaud = "2000000", bool 开启终端电阻 = true)
|
public virtual bool 初始化并启动通道(uint 通道号)
|
||||||
{
|
{
|
||||||
if (_deviceHandle == IntPtr.Zero) throw new InvalidOperationException("请先调用 '打开设备()' 才能初始化通道。");
|
if (_deviceHandle == IntPtr.Zero) throw new InvalidOperationException("请先调用 '打开设备()' 才能初始化通道。");
|
||||||
if (通道号 >= _maxChannels) return false;
|
if (通道号 >= _maxChannels) return false;
|
||||||
@@ -90,11 +105,11 @@ namespace ZLGUSBCANFD
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 1. 设置该通道专属的仲裁域与数据域波特率
|
// 1. 设置该通道专属的仲裁域与数据域波特率
|
||||||
if (ZLGCAN.ZCAN_SetValue(_deviceHandle, $"{通道号}/canfd_abit_baud_rate", abitBaud) != 1) return false;
|
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;
|
if (ZLGCAN.ZCAN_SetValue(_deviceHandle, $"{通道号}/canfd_dbit_baud_rate", _dbitBaud) != 1) return false;
|
||||||
|
|
||||||
// 2. 设置该通道专属内部终端电阻状态
|
// 2. 设置该通道专属内部终端电阻状态
|
||||||
string resistanceStr = 开启终端电阻 ? "1" : "0";
|
string resistanceStr = _enableTerminalResistance ? "1" : "0";
|
||||||
if (ZLGCAN.ZCAN_SetValue(_deviceHandle, $"{通道号}/initenal_resistance", resistanceStr) != 1) return false;
|
if (ZLGCAN.ZCAN_SetValue(_deviceHandle, $"{通道号}/initenal_resistance", resistanceStr) != 1) return false;
|
||||||
|
|
||||||
// 3. 规范配置通道结构体
|
// 3. 规范配置通道结构体
|
||||||
@@ -173,7 +188,8 @@ namespace ZLGUSBCANFD
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_isDbcLoadedArray[通道号]) return true;
|
if (_isDbcLoadedArray[通道号]) return true;
|
||||||
|
//加载前先卸载
|
||||||
|
释放通道DBC(通道号);
|
||||||
uint chDbcHandle = ZDBC.ZDBC_Init();
|
uint chDbcHandle = ZDBC.ZDBC_Init();
|
||||||
if (chDbcHandle == 0) return false;
|
if (chDbcHandle == 0) return false;
|
||||||
|
|
||||||
@@ -185,6 +201,7 @@ namespace ZLGUSBCANFD
|
|||||||
{
|
{
|
||||||
_dbcHandles[通道号] = chDbcHandle;
|
_dbcHandles[通道号] = chDbcHandle;
|
||||||
_isDbcLoadedArray[通道号] = true;
|
_isDbcLoadedArray[通道号] = true;
|
||||||
|
DBCParser.ParseAndLoadToDatabase(chDbcHandle, (int)通道号);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -210,6 +227,7 @@ namespace ZLGUSBCANFD
|
|||||||
ZDBC.ZDBC_Release(_dbcHandles[通道号]);
|
ZDBC.ZDBC_Release(_dbcHandles[通道号]);
|
||||||
_dbcHandles[通道号] = 0;
|
_dbcHandles[通道号] = 0;
|
||||||
_isDbcLoadedArray[通道号] = false;
|
_isDbcLoadedArray[通道号] = false;
|
||||||
|
DBCParser.MsgDatabase[(int)通道号].Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -433,7 +451,7 @@ namespace ZLGUSBCANFD
|
|||||||
ZLGCAN.can_frame canFrame = (ZLGCAN.can_frame)Marshal.PtrToStructure(ptrCanFrame, typeof(ZLGCAN.can_frame));
|
ZLGCAN.can_frame canFrame = (ZLGCAN.can_frame)Marshal.PtrToStructure(ptrCanFrame, typeof(ZLGCAN.can_frame));
|
||||||
canFrame.__pad |= 0x20;
|
canFrame.__pad |= 0x20;
|
||||||
|
|
||||||
ZLGCAN.ZCAN_Transmit_Data txData = new ZLGCAN.ZCAN_Transmit_Data { frame = canFrame, transmit_type = 0 };
|
ZLGCAN.ZCAN_Transmit_Data txData = new ZLGCAN.ZCAN_Transmit_Data { frame = canFrame, transmit_type = 2 };
|
||||||
IntPtr pTx = Marshal.AllocHGlobal(Marshal.SizeOf(txData));
|
IntPtr pTx = Marshal.AllocHGlobal(Marshal.SizeOf(txData));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -460,7 +478,7 @@ namespace ZLGUSBCANFD
|
|||||||
ZLGCAN.canfd_frame canfdFrame = (ZLGCAN.canfd_frame)Marshal.PtrToStructure(ptrCanFDFrame, typeof(ZLGCAN.canfd_frame));
|
ZLGCAN.canfd_frame canfdFrame = (ZLGCAN.canfd_frame)Marshal.PtrToStructure(ptrCanFDFrame, typeof(ZLGCAN.canfd_frame));
|
||||||
canfdFrame.flags |= 0x20;
|
canfdFrame.flags |= 0x20;
|
||||||
|
|
||||||
ZLGCAN.ZCAN_TransmitFD_Data txFdData = new ZLGCAN.ZCAN_TransmitFD_Data { frame = canfdFrame, transmit_type = 0 };
|
ZLGCAN.ZCAN_TransmitFD_Data txFdData = new ZLGCAN.ZCAN_TransmitFD_Data { frame = canfdFrame, transmit_type = 2 };
|
||||||
IntPtr pTxFd = Marshal.AllocHGlobal(Marshal.SizeOf(txFdData));
|
IntPtr pTxFd = Marshal.AllocHGlobal(Marshal.SizeOf(txFdData));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -507,20 +525,25 @@ namespace ZLGUSBCANFD
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 4.1 仿同星高性能智能报文/信号控制流 (新增扩展)
|
#region 4.1 报文设置与发送 (支持智能进制转换)
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设置并发送指定信号值。
|
/// 设置并发送指定信号值(帧 ID 支持 string 兼容模式)。
|
||||||
/// 其余未指定信号使用 DBC 中定义的初始值填充,避免总线数据被意外清零。
|
/// 其余未指定信号使用 DBC 中定义的初始值填充,避免总线数据被意外清零。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="通道号">CAN/CANFD 通道索引</param>
|
/// <param name="通道号">CAN/CANFD 通道索引</param>
|
||||||
/// <param name="帧ID">对应报文的帧 ID</param>
|
/// <param name="帧IDStr">对应报文的帧 ID(可以是 "26"、"0x1A"、"1A")</param>
|
||||||
/// <param name="信号名称">DBC 中定义的英文信号名称(不区分大小写)</param>
|
/// <param name="信号名称">DBC 中定义的英文信号名称(不区分大小写)</param>
|
||||||
/// <param name="物理值">要写入的实际物理数值</param>
|
/// <param name="物理值">要写入的实际物理数值</param>
|
||||||
/// <param name="循环发送间隔毫秒">0 = 单次发送;>0 = 周期循环发送(单位毫秒)</param>
|
/// <param name="循环发送间隔毫秒">0 = 单次发送;>0 = 周期循环发送(单位毫秒)</param>
|
||||||
/// <returns>操作是否成功</returns>
|
/// <returns>操作是否成功</returns>
|
||||||
public virtual bool 设置报文(uint 通道号, uint 帧ID, string 信号名称, double 物理值, int 循环发送间隔毫秒 = 0)
|
public virtual bool 设置报文(uint 通道号, string 帧IDStr, string 信号名称, double 物理值, int 循环发送间隔毫秒 = 0)
|
||||||
{
|
{
|
||||||
|
if (!TryParseFrameId(帧IDStr, out uint 帧ID))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ZLGCANFD] 设置报文失败: 无法解析的帧 ID '{帧IDStr}'");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (通道号 >= _maxChannels || _channelHandles[通道号] == IntPtr.Zero) return false;
|
if (通道号 >= _maxChannels || _channelHandles[通道号] == IntPtr.Zero) return false;
|
||||||
if (!_isDbcLoadedArray[通道号]) return false;
|
if (!_isDbcLoadedArray[通道号]) return false;
|
||||||
if (string.IsNullOrWhiteSpace(信号名称)) return false;
|
if (string.IsNullOrWhiteSpace(信号名称)) return false;
|
||||||
@@ -534,15 +557,21 @@ namespace ZLGUSBCANFD
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 发送报文:以 DBC 中所有信号的初始值作为默认值发送。
|
/// 发送报文:以 DBC 中所有信号的初始值作为默认值发送(帧 ID 支持 string 兼容模式)。
|
||||||
/// 如果之前调用 设置报文 保存过覆盖值,则会优先使用覆盖值。
|
/// 如果之前调用 设置报文 保存过覆盖值,则会优先使用覆盖值。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="通道号">CAN/CANFD 通道索引</param>
|
/// <param name="通道号">CAN/CANFD 通道索引</param>
|
||||||
/// <param name="帧ID">DBC 中定义的帧 ID</param>
|
/// <param name="帧IDStr">DBC 中定义的帧 ID(可以是 "26"、"0x1A"、"1A")</param>
|
||||||
/// <param name="循环发送间隔毫秒">0 = 单次发送;>0 = 周期循环发送(毫秒)</param>
|
/// <param name="循环发送间隔毫秒">0 = 单次发送;>0 = 周期循环发送(毫秒)</param>
|
||||||
/// <param name="是否使用CANFD">1 = 以 CANFD 格式发送;0 = 经典 CAN 格式</param>
|
/// <param name="是否使用CANFD">1 = 以 CANFD 格式发送;0 = 经典 CAN 格式</param>
|
||||||
public virtual bool 发送报文(uint 通道号, uint 帧ID, int 循环发送间隔毫秒 = 0, int 是否使用CANFD = 1)
|
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 (通道号 >= _maxChannels || _channelHandles[通道号] == IntPtr.Zero) return false;
|
||||||
if (!_isDbcLoadedArray[通道号]) return false;
|
if (!_isDbcLoadedArray[通道号]) return false;
|
||||||
if (循环发送间隔毫秒 < 0) throw new ArgumentOutOfRangeException(nameof(循环发送间隔毫秒));
|
if (循环发送间隔毫秒 < 0) throw new ArgumentOutOfRangeException(nameof(循环发送间隔毫秒));
|
||||||
@@ -552,10 +581,10 @@ namespace ZLGUSBCANFD
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 发送自定义报文(原始字节)。
|
/// 发送自定义报文(原始字节,帧 ID 支持 string 兼容模式)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="通道号">CAN/CANFD 通道索引</param>
|
/// <param name="通道号">CAN/CANFD 通道索引</param>
|
||||||
/// <param name="帧ID">帧 ID;扩展帧会自动置位 0x80000000</param>
|
/// <param name="帧IDStr">帧 ID(可以是 "26"、"0x1A"、"1A");扩展帧会自动置位 0x80000000</param>
|
||||||
/// <param name="原始数据">原始帧数据</param>
|
/// <param name="原始数据">原始帧数据</param>
|
||||||
/// <param name="dlcLength">数据长度;CANFD 仅允许 0-8,12,16,20,24,32,48,64</param>
|
/// <param name="dlcLength">数据长度;CANFD 仅允许 0-8,12,16,20,24,32,48,64</param>
|
||||||
/// <param name="是否是扩展帧">是否为 29 位扩展帧</param>
|
/// <param name="是否是扩展帧">是否为 29 位扩展帧</param>
|
||||||
@@ -563,13 +592,19 @@ namespace ZLGUSBCANFD
|
|||||||
/// <param name="开启波特率加速BRS">CANFD 是否开启波特率加速</param>
|
/// <param name="开启波特率加速BRS">CANFD 是否开启波特率加速</param>
|
||||||
public virtual bool 发送自定义报文(
|
public virtual bool 发送自定义报文(
|
||||||
uint 通道号,
|
uint 通道号,
|
||||||
uint 帧ID,
|
string 帧IDStr,
|
||||||
byte[] 原始数据,
|
byte[] 原始数据,
|
||||||
byte dlcLength,
|
byte dlcLength,
|
||||||
bool 是否是扩展帧 = false,
|
bool 是否是扩展帧 = false,
|
||||||
bool 是否是CANFD = true,
|
bool 是否是CANFD = true,
|
||||||
bool 开启波特率加速BRS = 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 (通道号 >= _maxChannels || _channelHandles[通道号] == IntPtr.Zero) return false;
|
||||||
if (原始数据 == null) return false;
|
if (原始数据 == null) return false;
|
||||||
|
|
||||||
@@ -593,7 +628,7 @@ namespace ZLGUSBCANFD
|
|||||||
};
|
};
|
||||||
Array.Copy(原始数据, 0, fdFrame.data, 0, Math.Min(原始数据.Length, 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 = 0 };
|
ZLGCAN.ZCAN_TransmitFD_Data txFdData = new ZLGCAN.ZCAN_TransmitFD_Data { frame = fdFrame, transmit_type = 2 };
|
||||||
IntPtr pTxFd = Marshal.AllocHGlobal(Marshal.SizeOf(txFdData));
|
IntPtr pTxFd = Marshal.AllocHGlobal(Marshal.SizeOf(txFdData));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -613,7 +648,7 @@ namespace ZLGUSBCANFD
|
|||||||
};
|
};
|
||||||
Array.Copy(原始数据, 0, standardFrame.data, 0, Math.Min(原始数据.Length, 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 = 0 };
|
ZLGCAN.ZCAN_Transmit_Data txData = new ZLGCAN.ZCAN_Transmit_Data { frame = standardFrame, transmit_type = 2 };
|
||||||
IntPtr pTx = Marshal.AllocHGlobal(Marshal.SizeOf(txData));
|
IntPtr pTx = Marshal.AllocHGlobal(Marshal.SizeOf(txData));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -649,7 +684,6 @@ namespace ZLGUSBCANFD
|
|||||||
string signalName = Encoding.Default.GetString(signal.strName).TrimEnd('\0');
|
string signalName = Encoding.Default.GetString(signal.strName).TrimEnd('\0');
|
||||||
if (!string.IsNullOrEmpty(signalName) && signal.initialValueValid != 0)
|
if (!string.IsNullOrEmpty(signalName) && signal.initialValueValid != 0)
|
||||||
{
|
{
|
||||||
// ZDBC 文档标注 initialValue 为“原始值”,但实际按 DBC 规范这里存放的是物理值
|
|
||||||
dict[signalName] = signal.initialValue;
|
dict[signalName] = signal.initialValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -693,6 +727,49 @@ namespace ZLGUSBCANFD
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 计算物理值也需要传入对应通道,使用通道专属的DBC句柄进行计算
|
/// 计算物理值也需要传入对应通道,使用通道专属的DBC句柄进行计算
|
||||||
/// </summary>
|
/// </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)]
|
[Browsable(false)]
|
||||||
public ulong 物理值转原始寄存器值(uint 通道号, ZDBC.DBCSignal 信号定义, double 实际物理值)
|
public ulong 物理值转原始寄存器值(uint 通道号, ZDBC.DBCSignal 信号定义, double 实际物理值)
|
||||||
|
|||||||
125
ZLGUSBCANFD/ZLGDBCParser.cs
Normal file
125
ZLGUSBCANFD/ZLGDBCParser.cs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
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