CAN驱动修改优化
This commit is contained in:
@@ -18,6 +18,7 @@ using UIShare.GlobalVariable;
|
|||||||
using UIShare.PubEvent;
|
using UIShare.PubEvent;
|
||||||
using UIShare.UIViewModel;
|
using UIShare.UIViewModel;
|
||||||
using SqlSugar;
|
using SqlSugar;
|
||||||
|
using TSMasterCAN;
|
||||||
|
|
||||||
namespace ACP.ViewModels
|
namespace ACP.ViewModels
|
||||||
{
|
{
|
||||||
@@ -155,6 +156,7 @@ namespace ACP.ViewModels
|
|||||||
public ICommand MonitorValueSettingCommand { get; set; }
|
public ICommand MonitorValueSettingCommand { get; set; }
|
||||||
public ICommand GetFileStringCommand { get; set; }
|
public ICommand GetFileStringCommand { get; set; }
|
||||||
public ICommand SilenceBuzzerCommand { get; set; }
|
public ICommand SilenceBuzzerCommand { get; set; }
|
||||||
|
public ICommand SettingChannelCommand { get; set; }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public ShellViewModel(IContainerProvider containerProvider)
|
public ShellViewModel(IContainerProvider containerProvider)
|
||||||
@@ -204,7 +206,8 @@ namespace ACP.ViewModels
|
|||||||
MonitorValueSettingCommand = new DelegateCommand(MonitorValueSetting);
|
MonitorValueSettingCommand = new DelegateCommand(MonitorValueSetting);
|
||||||
GetFileStringCommand = new DelegateCommand(GetFileString);
|
GetFileStringCommand = new DelegateCommand(GetFileString);
|
||||||
SilenceBuzzerCommand = new AsyncDelegateCommand(SilenceBuzzer);
|
SilenceBuzzerCommand = new AsyncDelegateCommand(SilenceBuzzer);
|
||||||
|
SettingChannelCommand = new AsyncDelegateCommand(SilenceBuzzer);
|
||||||
|
SettingChannelCommand = new DelegateCommand(SettingChannel);
|
||||||
_globalInfo.ContextDic.Add("default", new ScopedContext());
|
_globalInfo.ContextDic.Add("default", new ScopedContext());
|
||||||
|
|
||||||
_eventAggregator.GetEvent<LoginSuccessEvent>().Subscribe(() =>
|
_eventAggregator.GetEvent<LoginSuccessEvent>().Subscribe(() =>
|
||||||
@@ -237,7 +240,16 @@ namespace ACP.ViewModels
|
|||||||
}
|
}
|
||||||
|
|
||||||
#region 命令处理与事件
|
#region 命令处理与事件
|
||||||
|
private void SettingChannel()
|
||||||
|
{
|
||||||
|
var re = CAN.ShowChannelMappingWindow(true);
|
||||||
|
if (re != 0)
|
||||||
|
{
|
||||||
|
var msg = CAN.GetErrorDescription(re);
|
||||||
|
if(CurrentConfig!=null)
|
||||||
|
LoggerHelper.ErrorWithNotify(CurrentConfig.Title,$"同星通道映射界面打开失败:{msg}");
|
||||||
|
}
|
||||||
|
}
|
||||||
private async Task SilenceBuzzer()
|
private async Task SilenceBuzzer()
|
||||||
{
|
{
|
||||||
_eventAggregator.GetEvent<SilenceBuzzerEvent>().Publish(_globalInfo.CurrentScope);
|
_eventAggregator.GetEvent<SilenceBuzzerEvent>().Publish(_globalInfo.CurrentScope);
|
||||||
|
|||||||
+12
-16
@@ -190,15 +190,19 @@
|
|||||||
Foreground="Black" />
|
Foreground="Black" />
|
||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<!-- CAN信号监控 -->
|
<MenuItem Header="同星CAN"
|
||||||
<MenuItem Header="CAN信号监控"
|
Foreground="Black">
|
||||||
Foreground="Black"
|
<MenuItem Header="通道映射"
|
||||||
Command="{Binding SelectCANSignalMonitorCommand}">
|
Command="{Binding SettingChannelCommand}"
|
||||||
<MenuItem.Icon>
|
Foreground="Black" />
|
||||||
<materialDesign:PackIcon Kind="Message"
|
<MenuItem Header="CAN信号采集设置"
|
||||||
Foreground="Black" />
|
Command="{Binding SelectCANSignalMonitorCommand}"
|
||||||
</MenuItem.Icon>
|
Foreground="Black" />
|
||||||
|
<MenuItem Header="CAN报文字符串获取"
|
||||||
|
Command="{Binding SelectCANMessageCommand}"
|
||||||
|
Foreground="Black" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
<!-- 监控设置 -->
|
<!-- 监控设置 -->
|
||||||
<MenuItem Header="监控设置"
|
<MenuItem Header="监控设置"
|
||||||
Foreground="Black"
|
Foreground="Black"
|
||||||
@@ -208,14 +212,6 @@ Command="{Binding MonitorValueSettingCommand}">
|
|||||||
Foreground="Black" />
|
Foreground="Black" />
|
||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem Header="CAN报文选择"
|
|
||||||
Foreground="Black"
|
|
||||||
Command="{Binding SelectCANMessageCommand}">
|
|
||||||
<MenuItem.Icon>
|
|
||||||
<materialDesign:PackIcon Kind="Message"
|
|
||||||
Foreground="Black" />
|
|
||||||
</MenuItem.Icon>
|
|
||||||
</MenuItem>
|
|
||||||
|
|
||||||
<!-- 获得文件路径字符串 -->
|
<!-- 获得文件路径字符串 -->
|
||||||
<MenuItem Header="获得文件路径字符串"
|
<MenuItem Header="获得文件路径字符串"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using CANModule.ViewModels;
|
||||||
using CANModule.Views;
|
using CANModule.Views;
|
||||||
|
|
||||||
namespace CANModule
|
namespace CANModule
|
||||||
@@ -12,7 +13,9 @@ namespace CANModule
|
|||||||
{
|
{
|
||||||
containerRegistry.RegisterDialog<SelectCanMessageView>("SelectCanMessageView");
|
containerRegistry.RegisterDialog<SelectCanMessageView>("SelectCanMessageView");
|
||||||
containerRegistry.RegisterDialog<CollectCANSignalSettingView>("CollectCANSignalSettingView");
|
containerRegistry.RegisterDialog<CollectCANSignalSettingView>("CollectCANSignalSettingView");
|
||||||
|
containerRegistry.RegisterDialog<DBCInfoView>("DBCInfo");
|
||||||
containerRegistry.RegisterForNavigation<CANView>("CANView");
|
containerRegistry.RegisterForNavigation<CANView>("CANView");
|
||||||
|
containerRegistry.Register<CANViewModel>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,11 @@ using System.Windows.Threading;
|
|||||||
using TSMasterCAN;
|
using TSMasterCAN;
|
||||||
using UIShare.GlobalVariable;
|
using UIShare.GlobalVariable;
|
||||||
using UIShare.UIViewModel;
|
using UIShare.UIViewModel;
|
||||||
|
using UIShare.ViewModelBase;
|
||||||
|
|
||||||
namespace CANModule.ViewModels
|
namespace CANModule.ViewModels
|
||||||
{
|
{
|
||||||
public class CANViewModel : BindableBase, IDialogAware
|
public class CANViewModel : NavigateViewModelBase
|
||||||
{
|
{
|
||||||
|
|
||||||
#region 属性
|
#region 属性
|
||||||
@@ -269,17 +270,15 @@ namespace CANModule.ViewModels
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
private IEventAggregator _eventAggregator { get; set; }
|
private IEventAggregator _eventAggregator { get; set; }
|
||||||
private CAN? _canfd;
|
|
||||||
private SystemConfig? _systemConfig;
|
private SystemConfig? _systemConfig;
|
||||||
private readonly DeviceManager _deviceManager;
|
private readonly DeviceManager _deviceManager;
|
||||||
private IDialogService _dialogService { get; set; }
|
private IDialogService _dialogService { get; set; }
|
||||||
CancellationTokenSource cts = new CancellationTokenSource();
|
CancellationTokenSource cts = new CancellationTokenSource();
|
||||||
public CANViewModel(IContainerProvider containerProvider)
|
public CANViewModel(IContainerProvider containerProvider) : base(containerProvider)
|
||||||
{
|
{
|
||||||
_eventAggregator = containerProvider.Resolve<IEventAggregator>();
|
_eventAggregator = containerProvider.Resolve<IEventAggregator>();
|
||||||
_systemConfig = containerProvider.Resolve<SystemConfig>();
|
_systemConfig = containerProvider.Resolve<SystemConfig>();
|
||||||
_deviceManager = containerProvider.Resolve<DeviceManager>();
|
_deviceManager = containerProvider.Resolve<DeviceManager>();
|
||||||
_canfd = _deviceManager.CANFD;
|
|
||||||
_dialogService = containerProvider.Resolve<IDialogService>();
|
_dialogService = containerProvider.Resolve<IDialogService>();
|
||||||
SelectBLFPathCommand = new DelegateCommand(SelectBLFPath);
|
SelectBLFPathCommand = new DelegateCommand(SelectBLFPath);
|
||||||
SaveBLFPathCommand = new DelegateCommand(SaveBLFPath);
|
SaveBLFPathCommand = new DelegateCommand(SaveBLFPath);
|
||||||
@@ -299,6 +298,7 @@ namespace CANModule.ViewModels
|
|||||||
LoadMessageCommand = new DelegateCommand(LoadMessage);
|
LoadMessageCommand = new DelegateCommand(LoadMessage);
|
||||||
DeleteDcAutoLoadCommand = new DelegateCommand(DeleteDcAutoLoad);
|
DeleteDcAutoLoadCommand = new DelegateCommand(DeleteDcAutoLoad);
|
||||||
SendCustomMessageCommand = new DelegateCommand(SendCustomMessage);
|
SendCustomMessageCommand = new DelegateCommand(SendCustomMessage);
|
||||||
|
Task.Run(() => Refresh(cts.Token), cts.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region 命令
|
#region 命令
|
||||||
@@ -496,7 +496,7 @@ namespace CANModule.ViewModels
|
|||||||
{
|
{
|
||||||
while (ct.IsCancellationRequested == false)
|
while (ct.IsCancellationRequested == false)
|
||||||
{
|
{
|
||||||
var temp = CAN.RealTimeMessages.Select(s => s.Value).ToArray();
|
var temp = _deviceManager._CANMonitoringService.RealTimeMessages.Select(s => s.Value).ToArray();
|
||||||
foreach (var item in temp)
|
foreach (var item in temp)
|
||||||
{
|
{
|
||||||
var find = CanMessageList.FirstOrDefault(s => s.报文ID == item.FIdentifier);
|
var find = CanMessageList.FirstOrDefault(s => s.报文ID == item.FIdentifier);
|
||||||
@@ -571,6 +571,10 @@ namespace CANModule.ViewModels
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void OnDialogOpened(IDialogParameters parameters)
|
public void OnDialogOpened(IDialogParameters parameters)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
public override void OnNavigatedTo(NavigationContext context)
|
||||||
{
|
{
|
||||||
Task.Run(() => Refresh(cts.Token), cts.Token);
|
Task.Run(() => Refresh(cts.Token), cts.Token);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Configuration;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using Model;
|
||||||
|
using Model.Models;
|
||||||
|
using TSMasterCAN;
|
||||||
|
using UIShare.GlobalVariable;
|
||||||
|
using UIShare.PubEvent;
|
||||||
|
using UIShare.UIViewModel;
|
||||||
|
|
||||||
|
namespace CANModule.ViewModels
|
||||||
|
{
|
||||||
|
public class DBCInfoViewModel :BindableBase, IDialogAware
|
||||||
|
{
|
||||||
|
private can_network _can_Network;
|
||||||
|
|
||||||
|
public required can_network can_Network
|
||||||
|
{
|
||||||
|
get { return _can_Network; }
|
||||||
|
set { SetProperty(ref _can_Network, value); }
|
||||||
|
}
|
||||||
|
private List<_Msg_> _MessageInfo;
|
||||||
|
|
||||||
|
public required List<_Msg_> MessageInfo
|
||||||
|
{
|
||||||
|
get { return _MessageInfo; }
|
||||||
|
set { SetProperty(ref _MessageInfo, value); }
|
||||||
|
}
|
||||||
|
private ObservableCollection<InstructionNode> _CanNetWorkNode = new();
|
||||||
|
public ObservableCollection<InstructionNode> CanNetWorkNode
|
||||||
|
{
|
||||||
|
get => _CanNetWorkNode;
|
||||||
|
set => SetProperty(ref _CanNetWorkNode, value);
|
||||||
|
}
|
||||||
|
private ObservableCollection<InstructionNode> _MessgaeNode = new();
|
||||||
|
public ObservableCollection<InstructionNode> MessgaeNode
|
||||||
|
{
|
||||||
|
get => _MessgaeNode;
|
||||||
|
set => SetProperty(ref _MessgaeNode, value);
|
||||||
|
}
|
||||||
|
public ICommand CloseCommand { get; set; }
|
||||||
|
public DialogCloseListener RequestClose { get; set; }
|
||||||
|
public DBCInfoViewModel()
|
||||||
|
{
|
||||||
|
CloseCommand = new DelegateCommand(Close);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Close()
|
||||||
|
{
|
||||||
|
RequestClose.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanCloseDialog()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDialogClosed()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDialogOpened(IDialogParameters parameters)
|
||||||
|
{
|
||||||
|
if (parameters.ContainsKey("can_Network"))
|
||||||
|
{
|
||||||
|
can_Network = parameters.GetValue<can_network>("can_Network");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters.ContainsKey("MsgDatabase"))
|
||||||
|
{
|
||||||
|
MessageInfo = parameters.GetValue<List<_Msg_>>("MsgDatabase");
|
||||||
|
}
|
||||||
|
InitTreeNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitTreeNode()
|
||||||
|
{
|
||||||
|
foreach(var item in can_Network.can_nodes)
|
||||||
|
{
|
||||||
|
var tx = new ObservableCollection<InstructionNode>();
|
||||||
|
var rx = new ObservableCollection<InstructionNode>();
|
||||||
|
foreach (var can_Message in item.rx_can_Messages)
|
||||||
|
{
|
||||||
|
rx.Add(new InstructionNode
|
||||||
|
{
|
||||||
|
Name = can_Message.message_name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
foreach (var can_Message in item.tx_can_Messages)
|
||||||
|
{
|
||||||
|
tx.Add(new InstructionNode
|
||||||
|
{
|
||||||
|
Name = can_Message.message_name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var children = new ObservableCollection<InstructionNode>();
|
||||||
|
children.Add(new InstructionNode
|
||||||
|
{
|
||||||
|
Name = "TX",
|
||||||
|
Children = tx
|
||||||
|
});
|
||||||
|
children.Add(new InstructionNode
|
||||||
|
{
|
||||||
|
Name = "RX",
|
||||||
|
Children = rx
|
||||||
|
});
|
||||||
|
CanNetWorkNode.Add(new InstructionNode
|
||||||
|
{
|
||||||
|
Name = item.node_name,
|
||||||
|
Children = children
|
||||||
|
});
|
||||||
|
}
|
||||||
|
foreach(var item in MessageInfo)
|
||||||
|
{
|
||||||
|
var children = new ObservableCollection<InstructionNode>();
|
||||||
|
foreach(var Signalname in item.signal_Name)
|
||||||
|
{
|
||||||
|
children.Add(new InstructionNode
|
||||||
|
{
|
||||||
|
Name = Signalname
|
||||||
|
});
|
||||||
|
}
|
||||||
|
string hexValue = "[0x" + item.msg_id.ToString("X") + "]";
|
||||||
|
MessgaeNode.Add(new InstructionNode
|
||||||
|
{
|
||||||
|
Name = hexValue+item.msg_name,
|
||||||
|
Children=children
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,12 +34,12 @@
|
|||||||
Foreground="White"
|
Foreground="White"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Margin="5,0,10,0" />
|
Margin="5,0,10,0" />
|
||||||
<Button Content="关闭"
|
<!--<Button Content="关闭"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="150 0 0 0"
|
Margin="150 0 0 0"
|
||||||
Width="60"
|
Width="60"
|
||||||
Command="{Binding CloseCommand}"
|
Command="{Binding CloseCommand}"
|
||||||
Height="25" />
|
Height="25" />-->
|
||||||
</Grid>
|
</Grid>
|
||||||
</GroupBox.Header>
|
</GroupBox.Header>
|
||||||
<Grid>
|
<Grid>
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<UserControl x:Class="CANModule.Views.DBCInfoView"
|
||||||
|
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: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"
|
||||||
|
xmlns:model="clr-namespace:Model.Models;assembly=Model"
|
||||||
|
prism:ViewModelLocator.AutoWireViewModel="True"
|
||||||
|
Height="400"
|
||||||
|
Width="800">
|
||||||
|
<prism:Dialog.WindowStyle>
|
||||||
|
<Style BasedOn="{StaticResource DialogUserManageStyle}"
|
||||||
|
TargetType="Window" />
|
||||||
|
</prism:Dialog.WindowStyle>
|
||||||
|
<GroupBox 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.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="2*" />
|
||||||
|
<ColumnDefinition Width="5" />
|
||||||
|
<!-- Splitter 列 -->
|
||||||
|
<ColumnDefinition Width="2*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TreeView ItemsSource="{Binding MessgaeNode}">
|
||||||
|
<TreeView.Resources>
|
||||||
|
<HierarchicalDataTemplate DataType="{x:Type model:InstructionNode}"
|
||||||
|
ItemsSource="{Binding Children}">
|
||||||
|
<TextBlock Text="{Binding Name}" />
|
||||||
|
</HierarchicalDataTemplate>
|
||||||
|
</TreeView.Resources>
|
||||||
|
</TreeView>
|
||||||
|
<GridSplitter Grid.Column="1"
|
||||||
|
Width="5"
|
||||||
|
Background="Gray"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
ResizeBehavior="PreviousAndNext"
|
||||||
|
ShowsPreview="True" />
|
||||||
|
<TreeView Grid.Column="2"
|
||||||
|
ItemsSource="{Binding CanNetWorkNode}">
|
||||||
|
<TreeView.Resources>
|
||||||
|
<HierarchicalDataTemplate DataType="{x:Type model:InstructionNode}"
|
||||||
|
ItemsSource="{Binding Children}">
|
||||||
|
<TextBlock Text="{Binding Name}" />
|
||||||
|
</HierarchicalDataTemplate>
|
||||||
|
</TreeView.Resources>
|
||||||
|
</TreeView>
|
||||||
|
</Grid>
|
||||||
|
</GroupBox>
|
||||||
|
</UserControl>
|
||||||
@@ -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>
|
||||||
|
/// DBCInfoView.xaml 的交互逻辑
|
||||||
|
/// </summary>
|
||||||
|
public partial class DBCInfoView : UserControl
|
||||||
|
{
|
||||||
|
public DBCInfoView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.LeftButton == MouseButtonState.Pressed)
|
||||||
|
{
|
||||||
|
Window.GetWindow(this)?.DragMove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -197,7 +197,6 @@ namespace MonitorModule.ViewModels.Dialogs
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private IEnumerable<(string DisplayName, string Fingerprint, string MethodName)> DiscoverCanSignals()
|
private IEnumerable<(string DisplayName, string Fingerprint, string MethodName)> DiscoverCanSignals()
|
||||||
{
|
{
|
||||||
if (_deviceManager?.CANFD == null) yield break;
|
|
||||||
if (_systemConfig?.ConfigurationList == null) yield break;
|
if (_systemConfig?.ConfigurationList == null) yield break;
|
||||||
|
|
||||||
var msgDb = DBCParse.MsgDatabase;
|
var msgDb = DBCParse.MsgDatabase;
|
||||||
@@ -331,7 +330,6 @@ namespace MonitorModule.ViewModels.Dialogs
|
|||||||
private void OnDbcLoaded(DBCLoadedArgs args)
|
private void OnDbcLoaded(DBCLoadedArgs args)
|
||||||
{
|
{
|
||||||
if (args.Scope != _systemConfig?.Title) return;
|
if (args.Scope != _systemConfig?.Title) return;
|
||||||
if (_deviceManager?.CANFD == null) return;
|
|
||||||
|
|
||||||
int channel = (int)args.Channel;
|
int channel = (int)args.Channel;
|
||||||
var msgDb = DBCParse.MsgDatabase;
|
var msgDb = DBCParse.MsgDatabase;
|
||||||
|
|||||||
@@ -535,7 +535,6 @@ namespace MonitorModule.ViewModels
|
|||||||
private void RefreshConfiguredCanSignals()
|
private void RefreshConfiguredCanSignals()
|
||||||
{
|
{
|
||||||
if (_systemConfig?.ConfigurationList == null || _systemConfig.ConfigurationList.Count == 0) return;
|
if (_systemConfig?.ConfigurationList == null || _systemConfig.ConfigurationList.Count == 0) return;
|
||||||
if (_deviceManager?.CANFD == null) return;
|
|
||||||
|
|
||||||
var msgDb = DBCParse.MsgDatabase;
|
var msgDb = DBCParse.MsgDatabase;
|
||||||
foreach (var channel in _systemConfig.ConfigurationList.Select(c => c.Channel).Distinct())
|
foreach (var channel in _systemConfig.ConfigurationList.Select(c => c.Channel).Distinct())
|
||||||
@@ -633,7 +632,6 @@ namespace MonitorModule.ViewModels
|
|||||||
private void OnDbcLoaded(DBCLoadedArgs args)
|
private void OnDbcLoaded(DBCLoadedArgs args)
|
||||||
{
|
{
|
||||||
if (args.Scope != TestStatus) return;
|
if (args.Scope != TestStatus) return;
|
||||||
if (_deviceManager?.CANFD == null) return;
|
|
||||||
int channel = (int)args.Channel;
|
int channel = (int)args.Channel;
|
||||||
var msgDb = DBCParse.MsgDatabase;
|
var msgDb = DBCParse.MsgDatabase;
|
||||||
if (channel < 0 || channel >= msgDb.Count) return;
|
if (channel < 0 || channel >= msgDb.Count) return;
|
||||||
|
|||||||
+6
-266
@@ -11,275 +11,15 @@ using TSMaster;
|
|||||||
namespace TSMasterCAN
|
namespace TSMasterCAN
|
||||||
{
|
{
|
||||||
[ACPCommand]
|
[ACPCommand]
|
||||||
public class CAN : IDisposable
|
public class CAN
|
||||||
{
|
{
|
||||||
#region 静态成员
|
|
||||||
|
|
||||||
public static event Action? ConnectEvent;
|
public static event Action ConnectEvent;
|
||||||
public static event Action? DisConnectEvent;
|
public static event Action DisConnectEvent;
|
||||||
public static bool ConnectFlag { get; set; } = false;
|
public static bool ConnectFlag { get; set; } = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 实时接收到的 CAN 报文缓存(按报文 ID 索引,保留最新帧)。
|
/// 初始化CAN驱动
|
||||||
/// 供 CANViewModel.Refresh 等界面轮询读取。
|
|
||||||
/// </summary>
|
|
||||||
public static ConcurrentDictionary<int, TLIBCANFD> RealTimeMessages { get; } = new();
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 实例字段与属性
|
|
||||||
|
|
||||||
/// <summary>设备类型号(43 = USBCANFD-400U 等,TSMaster 中作为配置参考保留)</summary>
|
|
||||||
public uint DeviceType { get; }
|
|
||||||
/// <summary>设备索引</summary>
|
|
||||||
public uint DeviceIndex { get; }
|
|
||||||
/// <summary>最大通道数</summary>
|
|
||||||
public int MaxChannels { get; }
|
|
||||||
/// <summary>仲裁域波特率</summary>
|
|
||||||
public string ABitBaud { get; }
|
|
||||||
/// <summary>数据域波特率</summary>
|
|
||||||
public string DBitBaud { get; }
|
|
||||||
/// <summary>是否开启终端电阻</summary>
|
|
||||||
public bool EnableTerminalResistance { get; }
|
|
||||||
|
|
||||||
/// <summary>DBC 解析器(提供 MsgDatabase、MaxChannels 等访问)</summary>
|
|
||||||
public DBCParse DBCParser => DBCParse.Instance;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// DBC 报文解码事件:当接收到 CAN 帧并通过 DBC 解码后触发。
|
|
||||||
/// 参数:(通道号, 解码后的报文信息)
|
|
||||||
/// </summary>
|
|
||||||
public event Action<uint, DBCMessage>? OnDbcMessageDecoded;
|
|
||||||
|
|
||||||
private bool _disposed;
|
|
||||||
private bool _isOpened;
|
|
||||||
private TCANFDQueueEvent_Win32? _instanceListener;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 构造函数
|
|
||||||
|
|
||||||
/// <summary>默认构造函数(兼容 SystemConfig.CANFD = new() 等场景)</summary>
|
|
||||||
public CAN() : this(43, 0, 4, "500000", "2000000", true) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 带配置参数的构造函数(兼容原 ZLG 设备管理流程)。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="deviceType">设备类型号</param>
|
|
||||||
/// <param name="deviceIndex">设备索引</param>
|
|
||||||
/// <param name="maxChannels">最大通道数</param>
|
|
||||||
/// <param name="aBitBaud">仲裁域波特率</param>
|
|
||||||
/// <param name="dBitBaud">数据域波特率</param>
|
|
||||||
/// <param name="enableTerminalResistance">是否开启终端电阻</param>
|
|
||||||
public CAN(uint deviceType, uint deviceIndex, int maxChannels, string aBitBaud, string dBitBaud, bool enableTerminalResistance)
|
|
||||||
{
|
|
||||||
DeviceType = deviceType;
|
|
||||||
DeviceIndex = deviceIndex;
|
|
||||||
MaxChannels = maxChannels;
|
|
||||||
ABitBaud = aBitBaud;
|
|
||||||
DBitBaud = dBitBaud;
|
|
||||||
EnableTerminalResistance = enableTerminalResistance;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 实例方法 — 设备管理(替代原 ZLG 实例方法)
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 打开设备:初始化 TSMaster 库 → 设置通道数 → 配置波特率 → 连接 → 启动 RBS → 注册监听。
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>true 表示成功</returns>
|
|
||||||
public bool 打开设备()
|
|
||||||
{
|
|
||||||
if (_isOpened) return true;
|
|
||||||
if (ConnectFlag) { _isOpened = true; return true; }
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 1. 初始化 TSMaster 库
|
|
||||||
var re = Init("ACP");
|
|
||||||
if (re != 0)
|
|
||||||
{
|
|
||||||
Debug.WriteLine($"TSMaster 初始化失败,错误代码:{re}");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 设置通道数
|
|
||||||
TsMasterApi.tsapp_set_can_channel_count(MaxChannels);
|
|
||||||
|
|
||||||
// 3. 配置各通道波特率
|
|
||||||
float arbKbps = float.Parse(ABitBaud) / 1000f;
|
|
||||||
float dataKbps = float.Parse(DBitBaud) / 1000f;
|
|
||||||
for (int ch = 0; ch < MaxChannels; ch++)
|
|
||||||
{
|
|
||||||
TsMasterApi.tsapp_configure_baudrate_canfd(
|
|
||||||
ch, arbKbps, dataKbps,
|
|
||||||
TLIBCANFDControllerType.lfdtISOCAN,
|
|
||||||
TLIBCANFDControllerMode.lfdmNormal,
|
|
||||||
EnableTerminalResistance);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. 连接硬件
|
|
||||||
re = Connect();
|
|
||||||
if (re != 0)
|
|
||||||
{
|
|
||||||
Debug.WriteLine($"CAN 连接失败,错误代码:{re}");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. 注册接收监听(用于 DBC 信号解码广播)
|
|
||||||
_instanceListener = new TCANFDQueueEvent_Win32(OnCanFdReceived);
|
|
||||||
RegisterListener(_instanceListener);
|
|
||||||
|
|
||||||
_isOpened = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.WriteLine($"打开设备异常:{ex.Message}");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 关闭 CAN 卡设备:注销监听 → 断开连接。
|
|
||||||
/// </summary>
|
|
||||||
public void 关闭CAN卡设备()
|
|
||||||
{
|
|
||||||
if (!_isOpened && !ConnectFlag) return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_instanceListener != null)
|
|
||||||
{
|
|
||||||
UnRegisterListener(_instanceListener);
|
|
||||||
_instanceListener = null;
|
|
||||||
}
|
|
||||||
DisConnect();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.WriteLine($"关闭CAN卡设备异常:{ex.Message}");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_isOpened = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化并启动指定通道(配置波特率,TSMaster 在 Connect 时已自动启动所有通道)。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="channel">通道号</param>
|
|
||||||
public void 初始化并启动通道(uint channel)
|
|
||||||
{
|
|
||||||
if (channel >= MaxChannels) return;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
float arbKbps = float.Parse(ABitBaud) / 1000f;
|
|
||||||
float dataKbps = float.Parse(DBitBaud) / 1000f;
|
|
||||||
TsMasterApi.tsapp_configure_baudrate_canfd(
|
|
||||||
(int)channel, arbKbps, dataKbps,
|
|
||||||
TLIBCANFDControllerType.lfdtISOCAN,
|
|
||||||
TLIBCANFDControllerMode.lfdmNormal,
|
|
||||||
EnableTerminalResistance);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.WriteLine($"初始化通道 {channel} 异常:{ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 加载通道 DBC 文件。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="channel">通道号</param>
|
|
||||||
/// <param name="filePath">DBC 文件路径</param>
|
|
||||||
/// <returns>true 表示加载成功</returns>
|
|
||||||
public bool 加载通道DBC文件(uint channel, string filePath)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var re = LoadDBC(filePath, [(int)channel], out _);
|
|
||||||
return re == 0;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.WriteLine($"加载通道 {channel} DBC 文件异常:{ex.Message}");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// CAN FD 接收回调:接收原始帧 → 查找 DBC → 解码信号 → 触发 OnDbcMessageDecoded 事件。
|
|
||||||
/// </summary>
|
|
||||||
private void OnCanFdReceived(ref int AObj, ref TLIBCANFD AData)
|
|
||||||
{
|
|
||||||
if (_disposed) return;
|
|
||||||
|
|
||||||
// 始终缓存最新报文,供界面轮询刷新
|
|
||||||
int identifier = AData.FIdentifier;
|
|
||||||
var canfdCopy = AData;
|
|
||||||
RealTimeMessages[identifier] = canfdCopy;
|
|
||||||
|
|
||||||
if (OnDbcMessageDecoded == null) return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
uint channel = AData.FIdxChn;
|
|
||||||
if (channel >= DBCParse.MsgDatabase.Count) return;
|
|
||||||
|
|
||||||
// 在 DBC 数据库中查找匹配的报文
|
|
||||||
var msgList = DBCParse.MsgDatabase[(int)channel];
|
|
||||||
var match = msgList.FirstOrDefault(m => m.msg_id == identifier);
|
|
||||||
if (match == null || match.signal_Name == null || match.signal_Name.Length == 0) return;
|
|
||||||
|
|
||||||
// 解码各信号值
|
|
||||||
var signals = new DBCSignal[match.signal_Name.Length];
|
|
||||||
for (int i = 0; i < match.signal_Name.Length; i++)
|
|
||||||
{
|
|
||||||
double value = 0;
|
|
||||||
TsMasterApi.tsdb_get_signal_value_canfd(ref canfdCopy, match.msg_name, match.signal_Name[i], ref value);
|
|
||||||
signals[i] = new DBCSignal
|
|
||||||
{
|
|
||||||
strName = Encoding.Default.GetBytes(match.signal_Name[i]),
|
|
||||||
nRawvalue = value,
|
|
||||||
nFactor = 1,
|
|
||||||
nOffset = 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var decoded = new DBCMessage
|
|
||||||
{
|
|
||||||
nID = (uint)identifier,
|
|
||||||
nSignalCount = signals.Length,
|
|
||||||
vSignals = signals
|
|
||||||
};
|
|
||||||
|
|
||||||
OnDbcMessageDecoded?.Invoke(channel, decoded);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.WriteLine($"DBC 信号解码异常:{ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region IDisposable
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_disposed) return;
|
|
||||||
_disposed = true;
|
|
||||||
关闭CAN卡设备();
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化TSMasterCAN
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ProjectName"></param>
|
/// <param name="ProjectName"></param>
|
||||||
/// <param name="filePath"></param>
|
/// <param name="filePath"></param>
|
||||||
@@ -298,7 +38,7 @@ namespace TSMasterCAN
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 释放TSMasterCAN
|
/// 释放CAN驱动
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Browsable(false)]
|
[Browsable(false)]
|
||||||
public static void Release()
|
public static void Release()
|
||||||
@@ -391,7 +131,7 @@ namespace TSMasterCAN
|
|||||||
public static int LoadDBC(string filePath, int[] channel, out uint databaseID)
|
public static int LoadDBC(string filePath, int[] channel, out uint databaseID)
|
||||||
{
|
{
|
||||||
databaseID = 0;
|
databaseID = 0;
|
||||||
|
if (!Path.Exists(filePath)) return -1;
|
||||||
var re = TsMasterApi.tsdb_load_can_db(Path.GetFullPath(filePath), string.Join(",", channel), ref databaseID);
|
var re = TsMasterApi.tsdb_load_can_db(Path.GetFullPath(filePath), string.Join(",", channel), ref databaseID);
|
||||||
foreach (var item in channel)
|
foreach (var item in channel)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -120,44 +120,8 @@ namespace TSMasterCAN
|
|||||||
NODE_RX_CANFD_MESSAGE_INDEX = 110,
|
NODE_RX_CANFD_MESSAGE_INDEX = 110,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// DBC 解码后的信号信息(替代原 ZLG ZDBC.DBCSignal)。
|
|
||||||
/// </summary>
|
|
||||||
public struct DBCSignal
|
|
||||||
{
|
|
||||||
/// <summary>信号名称(原始字节,兼容 Encoding.Default 解码)</summary>
|
|
||||||
public byte[] strName;
|
|
||||||
/// <summary>原始值(TSMaster tsdb_get_signal_value_canfd 返回的物理值)</summary>
|
|
||||||
public double nRawvalue;
|
|
||||||
/// <summary>因子(TSMaster 返回物理值,因子固定为 1)</summary>
|
|
||||||
public double nFactor;
|
|
||||||
/// <summary>偏移(TSMaster 返回物理值,偏移固定为 0)</summary>
|
|
||||||
public double nOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// DBC 解码后的报文信息(替代原 ZLG ZDBC.DBCMessage)。
|
|
||||||
/// 在 CAN 接收回调中对原始 TLIBCANFD 帧解码后构建此对象并触发 OnDbcMessageDecoded 事件。
|
|
||||||
/// </summary>
|
|
||||||
public class DBCMessage
|
|
||||||
{
|
|
||||||
/// <summary>报文 ID</summary>
|
|
||||||
public uint nID;
|
|
||||||
/// <summary>信号数量</summary>
|
|
||||||
public int nSignalCount;
|
|
||||||
/// <summary>信号数组</summary>
|
|
||||||
public DBCSignal[] vSignals;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DBCParse
|
public class DBCParse
|
||||||
{
|
{
|
||||||
private static DBCParse? _instance;
|
|
||||||
/// <summary>单例实例,供 CAN.DBCParser 属性使用(MsgDatabase 等成员为 static,实例仅用于属性访问兼容)。</summary>
|
|
||||||
public static DBCParse Instance => _instance ??= new DBCParse();
|
|
||||||
|
|
||||||
/// <summary>最大通道数(等于 MsgDatabase 的维度)</summary>
|
|
||||||
public int MaxChannels => MsgDatabase.Count;
|
|
||||||
|
|
||||||
public static List<List<_Msg_>> MsgDatabase { get; set; } =
|
public static List<List<_Msg_>> MsgDatabase { get; set; } =
|
||||||
[.. Enumerable.Range(0, 12).Select(_ => new List<_Msg_>())];
|
[.. Enumerable.Range(0, 12).Select(_ => new List<_Msg_>())];
|
||||||
public static can_network can_Network;
|
public static can_network can_Network;
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using TSMasterCAN;
|
||||||
|
using TSMaster;
|
||||||
|
namespace UIShare.GlobalVariable
|
||||||
|
{
|
||||||
|
public class CANMonitoringService
|
||||||
|
{
|
||||||
|
public bool IsStopped { get; set; } = false;
|
||||||
|
public bool IsInitialized { get; set; } = false;
|
||||||
|
public bool IsRegistered { get; set; } = false;
|
||||||
|
private SystemConfig _systemConfig { get; set; }
|
||||||
|
public CANMonitoringService(SystemConfig systemConfig)
|
||||||
|
{
|
||||||
|
_systemConfig = systemConfig;
|
||||||
|
CANSignalBroadcaster.RegisterService(this);
|
||||||
|
IsRegistered = true;
|
||||||
|
}
|
||||||
|
public int Init(CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
int re = 0;
|
||||||
|
if (IsInitialized == false)
|
||||||
|
{
|
||||||
|
re = CAN.Init(_systemConfig.TSMasterName);
|
||||||
|
}
|
||||||
|
if (IsInitialized == false)
|
||||||
|
{
|
||||||
|
re = CAN.RegisterListener(MonitorEvent);
|
||||||
|
if (re == 0) IsInitialized = true;
|
||||||
|
}
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
public ConcurrentDictionary<string, TLIBCANFD> RealTimeMessages { get; set; } = new();
|
||||||
|
public ConcurrentDictionary<string, double> RealTimeSignals { get; set; } = new();
|
||||||
|
public void MonitorEvent(ref int AObj, ref TLIBCANFD AData)
|
||||||
|
{
|
||||||
|
if (IsStopped) return;
|
||||||
|
byte[] oldbytes = null;
|
||||||
|
|
||||||
|
//记录至实时报文数据库
|
||||||
|
var cpAData = AData;
|
||||||
|
cpAData.FData = ArrayPool<byte>.Shared.Rent(64);
|
||||||
|
Array.Copy(AData.FData, 0, cpAData.FData, 0, cpAData.FData.Length);
|
||||||
|
var MessageName = $"{AData.FIdxChn}/{AData.FIdentifier}";
|
||||||
|
if (RealTimeMessages.TryGetValue(MessageName, out var v))
|
||||||
|
{
|
||||||
|
oldbytes = v.FData;
|
||||||
|
}
|
||||||
|
RealTimeMessages[MessageName] = cpAData;
|
||||||
|
|
||||||
|
//记录至DBC数据库
|
||||||
|
var dbcfind = DBCParse.MsgDatabase[AData.FIdxChn].FirstOrDefault(s => s.msg_id == cpAData.FIdentifier);
|
||||||
|
if (dbcfind != null)
|
||||||
|
{
|
||||||
|
Array.Copy(cpAData.FData, 0, dbcfind.ACANFD.FData, 0, cpAData.FData.Length);
|
||||||
|
dbcfind.ACANFD.FTimeUS = cpAData.FTimeUS;
|
||||||
|
dbcfind.ACANFD.FIsFD = cpAData.FIsFD;
|
||||||
|
dbcfind.ACANFD.FIsBRS = cpAData.FIsBRS;
|
||||||
|
dbcfind.ACANFD.FIsESI = cpAData.FIsESI;
|
||||||
|
dbcfind.ACANFD.FIsError = cpAData.FIsError;
|
||||||
|
dbcfind.ACANFD.FIsRemote = cpAData.FIsRemote;
|
||||||
|
dbcfind.ACANFD.FIsExt = cpAData.FIsExt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldbytes is not null) ArrayPool<byte>.Shared.Return(oldbytes);
|
||||||
|
|
||||||
|
var id = AData.FIdentifier;
|
||||||
|
var ch = AData.FIdxChn;
|
||||||
|
|
||||||
|
var canSignalList = _systemConfig.ConfigurationList.Where(s => s.MessageID == id && s.Channel == ch).ToArray();
|
||||||
|
|
||||||
|
if (canSignalList.Length > 0)
|
||||||
|
{
|
||||||
|
var find = DBCParse.MsgDatabase[ch].First(s => s.msg_id == id);
|
||||||
|
foreach (var item in canSignalList)
|
||||||
|
{
|
||||||
|
double Signalre = double.NaN;
|
||||||
|
var re = CAN.GetSignalValue(ref cpAData, item.MessageName, item.SignalName, ref Signalre);
|
||||||
|
//Debug.Assert(re == 0);
|
||||||
|
var SinalName = $"{AData.FIdxChn}/{item.MessageName}/{item.SignalName}";
|
||||||
|
RealTimeSignals[SinalName] = Signalre;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 停止监测:从广播器注销并清理实时数据。
|
||||||
|
/// </summary>
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
IsStopped = true;
|
||||||
|
if (IsRegistered)
|
||||||
|
{
|
||||||
|
CANSignalBroadcaster.UnregisterService(this);
|
||||||
|
IsRegistered = false;
|
||||||
|
}
|
||||||
|
RealTimeSignals.Clear();
|
||||||
|
RealTimeMessages.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,131 +1,191 @@
|
|||||||
using Model.Models;
|
using Model.Models;
|
||||||
using Prism.Events;
|
using Prism.Events;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Threading;
|
||||||
using TSMasterCAN;
|
using System.Threading.Tasks;
|
||||||
using UIShare.PubEvent;
|
using UIShare.PubEvent;
|
||||||
|
using UIShare.UIViewModel;
|
||||||
|
|
||||||
namespace UIShare.GlobalVariable
|
namespace UIShare.GlobalVariable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// CAN 信号广播器(全局单例):
|
/// CAN 信号广播器(全局单例):
|
||||||
/// 统一订阅 GlobalInfo.CanPool 中所有已实例化的 ZLGCANFD 设备,
|
/// 不再订阅 CAN 实体实例的事件,改为从所有已注册的 <see cref="CANMonitoringService"/>
|
||||||
/// 将 DBC 解码后的信号值广播给所有引用该 CAN 设备的作用域,
|
/// 的 RealTimeSignals 字典中轮询读取信号值,并通过 <see cref="HardwareDataReportedEvent"/> 广播。
|
||||||
/// 并同步检查各作用域 ValueLimitList 的超限报警。
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CANSignalBroadcaster : IDisposable
|
public class CANSignalBroadcaster
|
||||||
{
|
{
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
private readonly GlobalInfo _globalInfo;
|
private readonly SystemConfig _systemConfig;
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
/// <summary>已订阅解码事件的 CANFD 实例 → 委托 映射</summary>
|
/// <summary>全局已注册的 CANMonitoringService 实例列表(静态,跨作用域共享)</summary>
|
||||||
private readonly Dictionary<CAN, Action<uint, DBCMessage>> _handlers = new();
|
private static readonly ConcurrentDictionary<CANMonitoringService, byte> _registeredServices = new();
|
||||||
public CANSignalBroadcaster(GlobalInfo globalInfo, IEventAggregator eventAggregator)
|
|
||||||
|
private CancellationTokenSource? _cts;
|
||||||
|
private Task? _broadcastTask;
|
||||||
|
|
||||||
|
/// <summary>广播轮询间隔(毫秒)</summary>
|
||||||
|
public int PollingIntervalMs { get; set; } = 100;
|
||||||
|
|
||||||
|
public CANSignalBroadcaster(SystemConfig systemConfig, IEventAggregator eventAggregator)
|
||||||
{
|
{
|
||||||
_globalInfo = globalInfo;
|
_systemConfig = systemConfig;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
#region 服务注册(供 CANMonitoringService 调用)
|
||||||
/// 扫描 GlobalInfo.CanPool 中已创建的 CANFD 实例并订阅解码事件。
|
|
||||||
/// 幂等:已订阅的实例不会重复订阅。
|
/// <summary>注册一个 CANMonitoringService,使其信号纳入广播</summary>
|
||||||
/// </summary>
|
public static void RegisterService(CANMonitoringService service)
|
||||||
public void Discover()
|
|
||||||
{
|
{
|
||||||
if (_disposed) return;
|
_registeredServices.TryAdd(service, 0);
|
||||||
|
|
||||||
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, DBCMessage> handler = (channel, msg) => OnDbcMessageDecoded(fingerprint, channel, msg);
|
|
||||||
canfd.OnDbcMessageDecoded += handler;
|
|
||||||
_handlers[canfd] = handler;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>注销一个 CANMonitoringService</summary>
|
||||||
/// 启动广播器(目前 Discover 已完成订阅,此处保留以兼容 HardwareDataBroadcaster 的使用模式)。
|
public static void UnregisterService(CANMonitoringService service)
|
||||||
/// </summary>
|
{
|
||||||
|
_registeredServices.TryRemove(service, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 启动 / 停止
|
||||||
|
|
||||||
|
/// <summary>启动广播(幂等:多次调用只启动一次)</summary>
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
Discover();
|
if (_broadcastTask != null && !_broadcastTask.IsCompleted) return;
|
||||||
|
|
||||||
|
_cts = new CancellationTokenSource();
|
||||||
|
_broadcastTask = Task.Run(() => BroadcastLoop(_cts.Token));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>停止广播</summary>
|
||||||
/// DBC 解码回调:提取所有信号,向引用该 CAN 设备的作用域广播,并检查报警。
|
public void Stop()
|
||||||
/// </summary>
|
|
||||||
private void OnDbcMessageDecoded(string canFingerprint, uint channel, DBCMessage msg)
|
|
||||||
{
|
{
|
||||||
if (_disposed) return;
|
_cts?.Cancel();
|
||||||
|
_broadcastTask = null;
|
||||||
|
}
|
||||||
|
|
||||||
string signalFingerprint = BuildFingerprint(canFingerprint, channel);
|
/// <summary>兼容旧接口:Discover 已无需执行任何操作</summary>
|
||||||
var now = DateTime.Now;
|
public void Discover() { }
|
||||||
|
|
||||||
// 获取引用该 CAN 设备的所有作用域
|
#endregion
|
||||||
var scopes = GetScopesForFingerprint(canFingerprint);
|
|
||||||
if (scopes.Count == 0) return;
|
|
||||||
|
|
||||||
for (int i = 0; i < msg.nSignalCount; i++)
|
#region 广播核心逻辑
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 轮询所有已注册的 CANMonitoringService 的 RealTimeSignals,
|
||||||
|
/// 结合 ConfigurationList 映射出 MessageID/Channel,广播 HardwareDataReportedEvent。
|
||||||
|
/// </summary>
|
||||||
|
private async Task BroadcastLoop(CancellationToken ct)
|
||||||
|
{
|
||||||
|
while (!ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
var signal = msg.vSignals[i];
|
try
|
||||||
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
|
// 预构建信号名 → 配置 的映射(避免内层循环重复查找)
|
||||||
{
|
var configMap = BuildConfigMap();
|
||||||
Scope = scope,
|
|
||||||
HardwareFingerprint = signalFingerprint,
|
|
||||||
MethodName = methodName,
|
|
||||||
Value = physicalValue,
|
|
||||||
Time = now
|
|
||||||
});
|
|
||||||
|
|
||||||
string MonitorStatus = ValueLimitAlarmHelper.CheckAlarm(scope, signalFingerprint, methodName, physicalValue, _globalInfo);
|
string scope = _systemConfig.Title;
|
||||||
if (MonitorStatus != "" && MonitorStatus != "未报警")
|
|
||||||
|
foreach (var service in _registeredServices.Keys)
|
||||||
{
|
{
|
||||||
_eventAggregator.GetEvent<AlarmEvent>().Publish((scope,canFingerprint, MonitorStatus));
|
if (service.IsStopped) continue;
|
||||||
|
|
||||||
|
foreach (var kvp in service.RealTimeSignals)
|
||||||
|
{
|
||||||
|
if (ct.IsCancellationRequested) return;
|
||||||
|
|
||||||
|
// 信号 Key 格式: "{channel}/{MessageName}/{SignalName}"
|
||||||
|
if (!TryParseSignalKey(kvp.Key, out int channel, out string? messageName, out string? signalName))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// 从配置映射中查找对应的 MessageID
|
||||||
|
if (!configMap.TryGetValue($"{channel}/{messageName}/{signalName}", out var cfg))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
string canFingerprint = $"CAN:{channel}";
|
||||||
|
string fingerprint = BuildFingerprint(canFingerprint, (uint)channel);
|
||||||
|
string methodName = BuildMethodName((uint)cfg.MessageID, signalName);
|
||||||
|
|
||||||
|
// 广播信号值
|
||||||
|
_eventAggregator.GetEvent<HardwareDataReportedEvent>().Publish(new HardwareReportArgs
|
||||||
|
{
|
||||||
|
Scope = scope,
|
||||||
|
HardwareFingerprint = fingerprint,
|
||||||
|
MethodName = methodName,
|
||||||
|
Value = kvp.Value,
|
||||||
|
Time = DateTime.Now
|
||||||
|
});
|
||||||
|
|
||||||
|
// 报警检查
|
||||||
|
string alarmStatus = ValueLimitAlarmHelper.CheckAlarm(fingerprint, methodName, kvp.Value, _systemConfig);
|
||||||
|
if (!string.IsNullOrEmpty(alarmStatus) && alarmStatus != "未报警")
|
||||||
|
{
|
||||||
|
_eventAggregator.GetEvent<AlarmEvent>().Publish((scope, canFingerprint, alarmStatus));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await Task.Delay(PollingIntervalMs, ct);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) { break; }
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LoggerHelper.Error($"CANSignalBroadcaster 广播异常: {ex.Message}");
|
||||||
|
await Task.Delay(1000, ct); // 异常后等待一段时间再重试
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>获取指定硬件指纹当前被哪些作用域引用</summary>
|
/// <summary>
|
||||||
private List<string> GetScopesForFingerprint(string fingerprint)
|
/// 从 SystemConfig.ConfigurationList 构建 "channel/MessageName/SignalName" → CANSignalConfig 的映射
|
||||||
|
/// </summary>
|
||||||
|
private Dictionary<string, CANSignalConfig> BuildConfigMap()
|
||||||
{
|
{
|
||||||
if (_globalInfo.DeviceAndScopeDic.TryGetValue(fingerprint, out var lazy))
|
var map = new Dictionary<string, CANSignalConfig>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
if (_systemConfig?.ConfigurationList == null) return map;
|
||||||
|
|
||||||
|
foreach (var cfg in _systemConfig.ConfigurationList)
|
||||||
{
|
{
|
||||||
var scopeList = lazy.Value;
|
if (string.IsNullOrEmpty(cfg.SignalName) || string.IsNullOrEmpty(cfg.MessageName)) continue;
|
||||||
lock (scopeList) return scopeList.ToList();
|
string key = $"{cfg.Channel}/{cfg.MessageName}/{cfg.SignalName}";
|
||||||
|
map.TryAdd(key, cfg); // 第一个匹配的优先
|
||||||
}
|
}
|
||||||
return new List<string>();
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>解析信号 Key: "{channel}/{MessageName}/{SignalName}"</summary>
|
||||||
/// 生成 CAN 信号的 DisplayName 格式:"{MessageName}.{SignalName}"
|
private static bool TryParseSignalKey(string key, out int channel, out string? messageName, out string? signalName)
|
||||||
/// </summary>
|
{
|
||||||
|
channel = 0;
|
||||||
|
messageName = null;
|
||||||
|
signalName = null;
|
||||||
|
|
||||||
|
var parts = key.Split('/');
|
||||||
|
if (parts.Length < 3) return false;
|
||||||
|
|
||||||
|
if (!int.TryParse(parts[0], out channel)) return false;
|
||||||
|
messageName = parts[1];
|
||||||
|
signalName = parts[2];
|
||||||
|
return !string.IsNullOrEmpty(messageName) && !string.IsNullOrEmpty(signalName);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 静态工具方法(保持向后兼容)
|
||||||
|
|
||||||
|
/// <summary>生成 CAN 信号的 DisplayName 格式:"{MessageName}.{SignalName}"</summary>
|
||||||
public static string BuildDisplayName(string messageName, string signalName)
|
public static string BuildDisplayName(string messageName, string signalName)
|
||||||
{
|
{
|
||||||
return $"{messageName}.{signalName}";
|
return $"{messageName}.{signalName}";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>生成 CAN 信号的 MethodName 格式:"{MessageId:X}.{SignalName}"</summary>
|
||||||
/// 生成 CAN 信号的 MethodName 格式:"{MessageId:X}.{SignalName}"
|
|
||||||
/// </summary>
|
|
||||||
public static string BuildMethodName(uint messageId, string signalName)
|
public static string BuildMethodName(uint messageId, string signalName)
|
||||||
{
|
{
|
||||||
return $"{messageId:X}.{signalName}";
|
return $"{messageId:X}.{signalName}";
|
||||||
@@ -140,17 +200,6 @@ namespace UIShare.GlobalVariable
|
|||||||
return $"{canDeviceFingerprint}:{channel}";
|
return $"{canDeviceFingerprint}:{channel}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
#endregion
|
||||||
{
|
|
||||||
if (_disposed) return;
|
|
||||||
_disposed = true;
|
|
||||||
|
|
||||||
foreach (var kvp in _handlers)
|
|
||||||
{
|
|
||||||
if (kvp.Key != null)
|
|
||||||
kvp.Key.OnDbcMessageDecoded -= kvp.Value;
|
|
||||||
}
|
|
||||||
_handlers.Clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ using System.Reflection;
|
|||||||
using TSMasterCAN;
|
using TSMasterCAN;
|
||||||
using UIShare.PubEvent;
|
using UIShare.PubEvent;
|
||||||
using UIShare.UIViewModel;
|
using UIShare.UIViewModel;
|
||||||
using TSMasterCAN;
|
|
||||||
|
|
||||||
namespace UIShare.GlobalVariable
|
namespace UIShare.GlobalVariable
|
||||||
{
|
{
|
||||||
@@ -25,6 +24,7 @@ namespace UIShare.GlobalVariable
|
|||||||
{
|
{
|
||||||
private object _lockObj = new object();
|
private object _lockObj = new object();
|
||||||
public SystemConfig _systemConfig { get; set; }
|
public SystemConfig _systemConfig { get; set; }
|
||||||
|
public CANMonitoringService _CANMonitoringService { get; set; }
|
||||||
private readonly GlobalInfo _globalInfo;
|
private readonly GlobalInfo _globalInfo;
|
||||||
private readonly string _scopeName;
|
private readonly string _scopeName;
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
@@ -35,7 +35,6 @@ namespace UIShare.GlobalVariable
|
|||||||
|
|
||||||
/// <summary>类名 → Type 的反射缓存(仅扫描一次)。</summary>
|
/// <summary>类名 → Type 的反射缓存(仅扫描一次)。</summary>
|
||||||
private static readonly IReadOnlyDictionary<string, Type> _deviceTypeMap = BuildDeviceTypeMap();
|
private static readonly IReadOnlyDictionary<string, Type> _deviceTypeMap = BuildDeviceTypeMap();
|
||||||
public CAN CANFD { get; set; }
|
|
||||||
public IOBoardGroup IOGroup { get; set; }
|
public IOBoardGroup IOGroup { get; set; }
|
||||||
|
|
||||||
public DeviceManager(SystemConfig systemConfig, GlobalInfo globalInfo, IEventAggregator eventAggregator)
|
public DeviceManager(SystemConfig systemConfig, GlobalInfo globalInfo, IEventAggregator eventAggregator)
|
||||||
@@ -43,11 +42,17 @@ namespace UIShare.GlobalVariable
|
|||||||
_systemConfig = systemConfig;
|
_systemConfig = systemConfig;
|
||||||
_globalInfo = globalInfo;
|
_globalInfo = globalInfo;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
|
_CANMonitoringService=new CANMonitoringService(_systemConfig);
|
||||||
// 用 SystemConfig.Title 作为作用域唯一标识,无需反查 ConfigDic
|
// 用 SystemConfig.Title 作为作用域唯一标识,无需反查 ConfigDic
|
||||||
_scopeName = _systemConfig.Title;
|
_scopeName = _systemConfig.Title;
|
||||||
InitDevices();
|
InitDevices();
|
||||||
|
InitCAN();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void InitCAN()
|
||||||
|
{
|
||||||
|
var re1 = _CANMonitoringService.Init();
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 根据设备配置提取唯一的硬件指纹字符串。
|
/// 根据设备配置提取唯一的硬件指纹字符串。
|
||||||
/// <para>Tcp → "Tcp:IP:Port";Serial → "Serial:PortName";无法识别则返回空字符串。</para>
|
/// <para>Tcp → "Tcp:IP:Port";Serial → "Serial:PortName";无法识别则返回空字符串。</para>
|
||||||
@@ -98,55 +103,6 @@ namespace UIShare.GlobalVariable
|
|||||||
{
|
{
|
||||||
if (config == null || !config.IsEnabled) continue;
|
if (config == null || !config.IsEnabled) continue;
|
||||||
|
|
||||||
// CAN 设备:ZLGCANFD 不实现 IBaseInterface,通过 SystemConfig.CANFD 单独管理,
|
|
||||||
// 按指纹从全局 CanPool 创建/复用实例,并注册作用域引用计数。
|
|
||||||
if (string.Equals(config.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
var fingerprint = ExtractHardwareFingerprint(config);
|
|
||||||
if (string.IsNullOrEmpty(fingerprint))
|
|
||||||
{
|
|
||||||
LoggerHelper.Warn($"设备 [{config.DeviceName}] 无法提取硬件指纹(连接方式={config.ConnectionType}),已跳过。");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.CANConfig == null)
|
|
||||||
{
|
|
||||||
LoggerHelper.Warn($"设备 [{config.DeviceName}] 缺少 CAN 连接参数,已跳过。");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 按指纹全局唯一创建同星 CAN 实例(maxChannels 默认 4,对应 USBCANFD-400U)
|
|
||||||
// 波特率与终端电阻从 CANConfigVM 传入,初始化并启动通道 时直接使用
|
|
||||||
var canLazy = _globalInfo.CanPool.GetOrAdd(fingerprint, key => new Lazy<CAN>(() =>
|
|
||||||
new CAN(config.CANConfig.DeviceType, config.CANConfig.DeviceIndex, 4,
|
|
||||||
config.CANConfig.ABitBaud, config.CANConfig.DBitBaud, config.CANConfig.EnableTerminalResistance)));
|
|
||||||
|
|
||||||
_systemConfig.CANFD = canLazy.Value;
|
|
||||||
CANFD = canLazy.Value;
|
|
||||||
|
|
||||||
// 注册作用域引用计数
|
|
||||||
if (!string.IsNullOrEmpty(_scopeName))
|
|
||||||
{
|
|
||||||
var scopeList = _globalInfo.DeviceAndScopeDic.GetOrAdd(fingerprint,
|
|
||||||
_ => new Lazy<List<string>>(() => new List<string>())).Value;
|
|
||||||
lock (scopeList)
|
|
||||||
{
|
|
||||||
if (!scopeList.Contains(_scopeName))
|
|
||||||
scopeList.Add(_scopeName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LoggerHelper.Info($"已加载 CAN 设备 [{config.DeviceName}] 指纹={fingerprint}(通过 SystemConfig.CANFD 管理)");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.ErrorWithNotify(_scopeName, $"CAN 设备 [{config.DeviceName}] 实例化失败:{ex.Message}");
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(config.DeviceType) ||
|
if (string.IsNullOrWhiteSpace(config.DeviceType) ||
|
||||||
!_deviceTypeMap.TryGetValue(config.DeviceType, out var deviceType))
|
!_deviceTypeMap.TryGetValue(config.DeviceType, out var deviceType))
|
||||||
{
|
{
|
||||||
@@ -302,7 +258,7 @@ namespace UIShare.GlobalVariable
|
|||||||
// CAN 设备:直接关闭 CAN 卡
|
// CAN 设备:直接关闭 CAN 卡
|
||||||
if (info != null && string.Equals(info.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase))
|
if (info != null && string.Equals(info.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
await CloseCanAsync(info);
|
CloseCan();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,7 +281,7 @@ namespace UIShare.GlobalVariable
|
|||||||
|
|
||||||
lock (_lockObj)
|
lock (_lockObj)
|
||||||
{
|
{
|
||||||
if (DeviceMap.Count == 0 && (CANFD == null)) return;
|
if (DeviceMap.Count == 0 ) return;
|
||||||
|
|
||||||
foreach (var kvp in DeviceMap)
|
foreach (var kvp in DeviceMap)
|
||||||
{
|
{
|
||||||
@@ -338,17 +294,7 @@ namespace UIShare.GlobalVariable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CAN 设备:直接关闭 CAN 卡
|
CAN.DisConnect();
|
||||||
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("所有设备已执行关闭操作。");
|
||||||
}
|
}
|
||||||
@@ -456,26 +402,11 @@ namespace UIShare.GlobalVariable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task<bool> ConnectCanAsync(DeviceInfoVM info)
|
private async Task<bool> ConnectCanAsync(DeviceInfoVM info)
|
||||||
{
|
{
|
||||||
string name = info.DeviceName ?? "CAN";
|
|
||||||
|
|
||||||
try
|
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(() =>
|
bool ok = await Task.Run(() =>
|
||||||
{
|
{
|
||||||
if (!CANFD.打开设备()) return false;
|
|
||||||
// 自动加载 DBC 文件
|
// 自动加载 DBC 文件
|
||||||
if (_systemConfig?.DBCAutoLoadList != null)
|
if (_systemConfig?.DBCAutoLoadList != null)
|
||||||
{
|
{
|
||||||
@@ -495,8 +426,7 @@ namespace UIShare.GlobalVariable
|
|||||||
LoggerHelper.Warn($"CAN 通道 {item.DBCChannel} 自动加载 DBC 失败:文件不存在 [{item.DBCFilePath}]");
|
LoggerHelper.Warn($"CAN 通道 {item.DBCChannel} 自动加载 DBC 失败:文件不存在 [{item.DBCFilePath}]");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
CANFD.初始化并启动通道((uint)item.DBCChannel);
|
bool loadOk = CAN.LoadDBC(item.DBCFilePath,new int[] { item.DBCChannel },out _ )==0;
|
||||||
bool loadOk = CANFD.加载通道DBC文件((uint)item.DBCChannel, item.DBCFilePath);
|
|
||||||
if (loadOk)
|
if (loadOk)
|
||||||
{
|
{
|
||||||
LoggerHelper.Info($"CAN 通道 {item.DBCChannel} 已自动加载 DBC:{item.DBCFilePath}");
|
LoggerHelper.Info($"CAN 通道 {item.DBCChannel} 已自动加载 DBC:{item.DBCFilePath}");
|
||||||
@@ -518,16 +448,16 @@ namespace UIShare.GlobalVariable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (CAN.Connect() != 0) return false;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
info.IsConnected = ok;
|
info.IsConnected = ok;
|
||||||
|
|
||||||
if (ok)
|
if (ok)
|
||||||
LoggerHelper.Info($"CAN 设备 [{name}] 连接成功,已初始化 {CANFD.DBCParser.MaxChannels} 个通道。");
|
LoggerHelper.Info($"CAN 设备 连接成功");
|
||||||
else
|
else
|
||||||
LoggerHelper.Warn($"CAN 设备 [{name}] 连接失败。");
|
LoggerHelper.Warn($"CAN 设备连接失败。");
|
||||||
|
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
@@ -535,7 +465,7 @@ namespace UIShare.GlobalVariable
|
|||||||
{
|
{
|
||||||
info.IsConnected = false;
|
info.IsConnected = false;
|
||||||
var inner = ex.InnerException?.Message ?? ex.Message;
|
var inner = ex.InnerException?.Message ?? ex.Message;
|
||||||
LoggerHelper.ErrorWithNotify(_scopeName, $"CAN 设备 [{name}] 连接异常:{inner}");
|
LoggerHelper.ErrorWithNotify(_scopeName, $"CAN 设备 连接异常:{inner}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -543,36 +473,9 @@ namespace UIShare.GlobalVariable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 关闭 CAN 卡
|
/// 关闭 CAN 卡
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task CloseCanAsync(DeviceInfoVM info)
|
private void CloseCan()
|
||||||
{
|
{
|
||||||
string name = info.DeviceName ?? "CAN";
|
CAN.DisConnect();
|
||||||
|
|
||||||
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()
|
||||||
@@ -620,6 +523,9 @@ namespace UIShare.GlobalVariable
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
// 停止 CAN 信号监测服务
|
||||||
|
_CANMonitoringService?.Stop();
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(_scopeName)) return;
|
if (string.IsNullOrEmpty(_scopeName)) return;
|
||||||
|
|
||||||
// 遍历当前作用域用到的所有指纹,逐一移除本作用域的引用
|
// 遍历当前作用域用到的所有指纹,逐一移除本作用域的引用
|
||||||
@@ -655,16 +561,6 @@ namespace UIShare.GlobalVariable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 尝试从 CanPool 取出并销毁
|
|
||||||
if (_globalInfo.CanPool.TryRemove(fingerprint, out var canLazy))
|
|
||||||
{
|
|
||||||
if (canLazy.IsValueCreated)
|
|
||||||
{
|
|
||||||
try { canLazy.Value.Dispose(); }
|
|
||||||
catch { /* 销毁时忽略异常 */ }
|
|
||||||
LoggerHelper.Info($"指纹 [{fingerprint}] 无作用域引用,已销毁 CAN 设备实例。");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 同步清除 DeviceAndScopeDic 中的空条目
|
// 同步清除 DeviceAndScopeDic 中的空条目
|
||||||
_globalInfo.DeviceAndScopeDic.TryRemove(fingerprint, out _);
|
_globalInfo.DeviceAndScopeDic.TryRemove(fingerprint, out _);
|
||||||
|
|||||||
@@ -578,11 +578,7 @@ namespace UIShare
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if(targetType.Name== "ZLGCANFD")
|
if (targetType.Name == "IOBoardGroup")
|
||||||
{
|
|
||||||
instance = _deviceManager.CANFD;
|
|
||||||
}
|
|
||||||
else if (targetType.Name == "IOBoardGroup")
|
|
||||||
{
|
{
|
||||||
instance = _deviceManager.IOGroup;
|
instance = _deviceManager.IOGroup;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,12 +20,22 @@ namespace UIShare.GlobalVariable
|
|||||||
{
|
{
|
||||||
if (globalInfo?.ConfigDic == null) return "";
|
if (globalInfo?.ConfigDic == null) return "";
|
||||||
if (!globalInfo.ConfigDic.TryGetValue(scope, out var systemConfig)) return "";
|
if (!globalInfo.ConfigDic.TryGetValue(scope, out var systemConfig)) return "";
|
||||||
if (systemConfig.ValueLimitList == null) return "";
|
return CheckAlarm(fingerprint, methodName, value, systemConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 直接根据 SystemConfig 的 ValueLimitList 检查当前值是否超限。
|
||||||
|
/// 适用于不持有 GlobalInfo 引用的场景(如 CANSignalBroadcaster)。
|
||||||
|
/// </summary>
|
||||||
|
public static string CheckAlarm(string fingerprint, string methodName, double value, SystemConfig systemConfig)
|
||||||
|
{
|
||||||
|
if (systemConfig?.ValueLimitList == null) return "";
|
||||||
|
|
||||||
var limit = systemConfig.ValueLimitList.FirstOrDefault(x =>
|
var limit = systemConfig.ValueLimitList.FirstOrDefault(x =>
|
||||||
x.Fingerprint == fingerprint && x.MethodName == methodName);
|
x.Fingerprint == fingerprint && x.MethodName == methodName);
|
||||||
if (limit == null) return "";
|
if (limit == null) return "";
|
||||||
if (value > limit.UpperExtreme)
|
|
||||||
|
if (value > limit.UpperExtreme)
|
||||||
{
|
{
|
||||||
limit.IsAlarm = true;
|
limit.IsAlarm = true;
|
||||||
limit.AlarmSatus = AlarmStatus.超上极限;
|
limit.AlarmSatus = AlarmStatus.超上极限;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0-windows</TargetFramework>
|
<TargetFramework>net8.0-windows</TargetFramework>
|
||||||
@@ -25,7 +25,10 @@
|
|||||||
<ProjectReference Include="..\ORM\ORM.csproj" />
|
<ProjectReference Include="..\ORM\ORM.csproj" />
|
||||||
<ProjectReference Include="..\Service\Service.csproj" />
|
<ProjectReference Include="..\Service\Service.csproj" />
|
||||||
<ProjectReference Include="..\TSMasterCAN\TSMasterCAN.csproj" />
|
<ProjectReference Include="..\TSMasterCAN\TSMasterCAN.csproj" />
|
||||||
<ProjectReference Include="..\TSMasterFlexRay\TSMasterFlexRay.csproj" />
|
</ItemGroup>
|
||||||
<ProjectReference Include="..\TSMasterLIN\TSMasterLIN.csproj" />
|
<ItemGroup>
|
||||||
|
<Reference Include="Interop.TSMasterAPI">
|
||||||
|
<HintPath>..\TSMasterCAN\Interop.TSMasterAPI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user