CAN优化

This commit is contained in:
hsc
2026-07-15 10:11:17 +08:00
parent aebbd88e23
commit 8e44be4894
10 changed files with 117 additions and 30 deletions

View File

@@ -204,6 +204,7 @@ namespace CANModule.ViewModels
{ {
CollectionID = Guid.NewGuid(), CollectionID = Guid.NewGuid(),
Channel = MessageChannel, Channel = MessageChannel,
MethodName = CANSignalBroadcaster.BuildMethodName((uint)decimalValue, SelectedSignal),
MessageName = ExtractMessageName(SelectedMessage), MessageName = ExtractMessageName(SelectedMessage),
SignalName = SelectedSignal, SignalName = SelectedSignal,
MessageID = decimalValue, MessageID = decimalValue,

View File

@@ -452,7 +452,7 @@ namespace CANModule.ViewModels
if (targetMsg != null) if (targetMsg != null)
{ {
_canfd.((uint)MessageSendChannel, targetMsg.msg_id.ToString(), SelectedSignal, SValue, sendPeriod); _canfd.((uint)MessageSendChannel, targetMsg.msg_id.ToString(), SelectedSignal, SValue, sendPeriod, IsDirectlySend);
} }
} }
} }
@@ -603,7 +603,7 @@ namespace CANModule.ViewModels
private void OnDbcMessageDecoded(uint channel, ZDBC.DBCMessage msg) private void OnDbcMessageDecoded(uint channel, ZDBC.DBCMessage msg)
{ {
// DBC 解码后的报文回调 // DBC 解码后的报文回调
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.BeginInvoke(() =>
{ {
var msgName = System.Text.Encoding.Default.GetString(msg.strName).TrimEnd('\0'); var msgName = System.Text.Encoding.Default.GetString(msg.strName).TrimEnd('\0');
var find = CanMessageList.FirstOrDefault(s => s.ID == (int)msg.nID&& s.==channel); var find = CanMessageList.FirstOrDefault(s => s.ID == (int)msg.nID&& s.==channel);

View File

@@ -111,10 +111,10 @@ namespace MonitorModule.ViewModels.Dialogs
} }
// 2. 发现设备方法信号并加入 ValueLimitList / DeviceSingleList // 2. 发现设备方法信号并加入 ValueLimitList / DeviceSingleList
foreach (var signal in DiscoverDeviceSignals()) foreach (var (displayName, fingerprint, methodName) in DiscoverDeviceSignals())
{ {
//DeviceSingleList.Add(signal); //DeviceSingleList.Add(displayName);
EnsureValueLimit(signal); EnsureValueLimit(fingerprint, displayName, displayName, methodName);
} }
// 3. 发现 CAN 信号并加入 ValueLimitList / DeviceSingleList // 3. 发现 CAN 信号并加入 ValueLimitList / DeviceSingleList
@@ -122,15 +122,15 @@ namespace MonitorModule.ViewModels.Dialogs
{ {
//DeviceSingleList.Add(displayName); //DeviceSingleList.Add(displayName);
_canSignalMap[displayName] = (fingerprint, methodName); _canSignalMap[displayName] = (fingerprint, methodName);
EnsureValueLimit(displayName); EnsureValueLimit(fingerprint, displayName, displayName, methodName);
} }
} }
/// <summary> /// <summary>
/// 发现当前作用域所有可监测的设备方法信号(与 MonitorViewModel 逻辑一致)。 /// 发现当前作用域所有可监测的设备方法信号(与 MonitorViewModel 逻辑一致)。
/// 返回 DisplayName 列表。 /// 返回 (DisplayName, Fingerprint, MethodName) 列表。
/// </summary> /// </summary>
private IEnumerable<string> DiscoverDeviceSignals() private IEnumerable<(string DisplayName, string Fingerprint, string MethodName)> DiscoverDeviceSignals()
{ {
if (_deviceManager?.DeviceMap == null) yield break; if (_deviceManager?.DeviceMap == null) yield break;
@@ -159,7 +159,7 @@ namespace MonitorModule.ViewModels.Dialogs
string displayName = !string.IsNullOrEmpty(attr?.Description) string displayName = !string.IsNullOrEmpty(attr?.Description)
? $"{deviceName}.{attr.Description}" ? $"{deviceName}.{attr.Description}"
: $"{deviceName}.{method.Name}"; : $"{deviceName}.{method.Name}";
yield return displayName; yield return (displayName, fingerprint, method.Name);
} }
} }
} }
@@ -191,12 +191,13 @@ namespace MonitorModule.ViewModels.Dialogs
if (_systemConfig?.ConfigurationList == null) yield break; if (_systemConfig?.ConfigurationList == null) yield break;
var msgDb = _deviceManager.CANFD.DBCParser.MsgDatabase; var msgDb = _deviceManager.CANFD.DBCParser.MsgDatabase;
string canDeviceFingerprint = _deviceManager.GetCanDeviceFingerprint();
foreach (var cfg in _systemConfig.ConfigurationList) foreach (var cfg in _systemConfig.ConfigurationList)
{ {
if (string.IsNullOrEmpty(cfg.SignalName)) continue; if (string.IsNullOrEmpty(cfg.SignalName)) continue;
if (cfg.Channel < 0 || cfg.Channel >= msgDb.Count) continue; if (cfg.Channel < 0 || cfg.Channel >= msgDb.Count) continue;
string fingerprint = CANSignalBroadcaster.BuildFingerprint((uint)cfg.Channel); string fingerprint = CANSignalBroadcaster.BuildFingerprint(canDeviceFingerprint, (uint)cfg.Channel);
string methodName = CANSignalBroadcaster.BuildMethodName((uint)cfg.MessageID, cfg.SignalName); string methodName = CANSignalBroadcaster.BuildMethodName((uint)cfg.MessageID, cfg.SignalName);
string displayName = CANSignalBroadcaster.BuildDisplayName(cfg.MessageName, cfg.SignalName); string displayName = CANSignalBroadcaster.BuildDisplayName(cfg.MessageName, cfg.SignalName);
@@ -224,13 +225,16 @@ namespace MonitorModule.ViewModels.Dialogs
/// <summary> /// <summary>
/// 确保 ValueLimitList 中存在指定信号;不存在则添加默认值。 /// 确保 ValueLimitList 中存在指定信号;不存在则添加默认值。
/// </summary> /// </summary>
private void EnsureValueLimit(string signalName) private void EnsureValueLimit(string fingerprint, string displayName, string signalName, string methodName = "")
{ {
if (ValueLimitList.Any(x => x.SignalName == signalName)) return; if (ValueLimitList.Any(x => x.SignalName == signalName)) return;
ValueLimitList.Add(new ValueLimitVM ValueLimitList.Add(new ValueLimitVM
{ {
DisplayName = displayName,
Fingerprint = fingerprint,
SignalName = signalName, SignalName = signalName,
MethodName = methodName,
Upper = 9999, Upper = 9999,
Lower = -9999, Lower = -9999,
UpperExtreme = 9999, UpperExtreme = 9999,
@@ -323,7 +327,8 @@ namespace MonitorModule.ViewModels.Dialogs
var msgDb = _deviceManager.CANFD.DBCParser.MsgDatabase; var msgDb = _deviceManager.CANFD.DBCParser.MsgDatabase;
if (channel < 0 || channel >= msgDb.Count) return; if (channel < 0 || channel >= msgDb.Count) return;
string fingerprint = CANSignalBroadcaster.BuildFingerprint(args.Channel); string canDeviceFingerprint = _deviceManager.GetCanDeviceFingerprint();
string fingerprint = CANSignalBroadcaster.BuildFingerprint(canDeviceFingerprint, args.Channel);
// 1. 移除该通道中已失效的 CAN 信号限制项 // 1. 移除该通道中已失效的 CAN 信号限制项
var staleSignals = _canSignalMap var staleSignals = _canSignalMap
@@ -353,7 +358,7 @@ namespace MonitorModule.ViewModels.Dialogs
_canSignalMap[displayName] = (fingerprint, methodName); _canSignalMap[displayName] = (fingerprint, methodName);
//if (!DeviceSingleList.Contains(displayName)) //if (!DeviceSingleList.Contains(displayName))
// DeviceSingleList.Add(displayName); // DeviceSingleList.Add(displayName);
EnsureValueLimit(displayName); EnsureValueLimit(fingerprint, displayName, displayName, methodName);
} }
} }
} }
@@ -366,7 +371,8 @@ namespace MonitorModule.ViewModels.Dialogs
{ {
if (args.Scope != _systemConfig?.Title) return; if (args.Scope != _systemConfig?.Title) return;
string fingerprint = CANSignalBroadcaster.BuildFingerprint(args.Channel); string canDeviceFingerprint = _deviceManager.GetCanDeviceFingerprint();
string fingerprint = CANSignalBroadcaster.BuildFingerprint(canDeviceFingerprint, args.Channel);
var toRemove = _canSignalMap var toRemove = _canSignalMap
.Where(kvp => kvp.Value.Fingerprint == fingerprint) .Where(kvp => kvp.Value.Fingerprint == fingerprint)
.Select(kvp => kvp.Key) .Select(kvp => kvp.Key)

View File

@@ -556,7 +556,8 @@ namespace MonitorModule.ViewModels
{ {
if (_systemConfig?.ConfigurationList == null) return; if (_systemConfig?.ConfigurationList == null) return;
string fingerprint = CANSignalBroadcaster.BuildFingerprint(channel); string canDeviceFingerprint = _deviceManager?.GetCanDeviceFingerprint() ?? string.Empty;
string fingerprint = CANSignalBroadcaster.BuildFingerprint(canDeviceFingerprint, channel);
var configs = _systemConfig.ConfigurationList.Where(c => c.Channel == (int)channel).ToList(); var configs = _systemConfig.ConfigurationList.Where(c => c.Channel == (int)channel).ToList();
bool changed = false; bool changed = false;
@@ -650,7 +651,8 @@ namespace MonitorModule.ViewModels
{ {
if (args.Scope != TestStatus) return; if (args.Scope != TestStatus) return;
string fingerprint = CANSignalBroadcaster.BuildFingerprint(args.Channel); string canDeviceFingerprint = _deviceManager?.GetCanDeviceFingerprint() ?? string.Empty;
string fingerprint = CANSignalBroadcaster.BuildFingerprint(canDeviceFingerprint, args.Channel);
// 从 AvailableMethods 中移除 // 从 AvailableMethods 中移除
var toRemoveMethods = AvailableMethods.Where(m => m.Fingerprint == fingerprint).ToList(); var toRemoveMethods = AvailableMethods.Where(m => m.Fingerprint == fingerprint).ToList();

View File

@@ -69,7 +69,7 @@ namespace UIShare.GlobalVariable
{ {
if (_disposed) return; if (_disposed) return;
string signalFingerprint = BuildFingerprint(channel); string signalFingerprint = BuildFingerprint(canFingerprint, channel);
var now = DateTime.Now; var now = DateTime.Now;
// 获取引用该 CAN 设备的所有作用域 // 获取引用该 CAN 设备的所有作用域
@@ -133,11 +133,12 @@ namespace UIShare.GlobalVariable
} }
/// <summary> /// <summary>
/// 生成 CAN 信号的 Fingerprint 格式:"CAN:{Channel}" /// 生成 CAN 信号的 Fingerprint 格式:"{canDeviceFingerprint}:{channel}"
/// canDeviceFingerprint 来自 DeviceManager.ExtractHardwareFingerprint如 "CAN:0"
/// </summary> /// </summary>
public static string BuildFingerprint(uint channel) public static string BuildFingerprint(string canDeviceFingerprint, uint channel)
{ {
return $"CAN:{channel}"; return $"{canDeviceFingerprint}:{channel}";
} }
public void Dispose() public void Dispose()

View File

@@ -71,6 +71,20 @@ namespace UIShare.GlobalVariable
return string.Empty; return string.Empty;
} }
/// <summary>
/// 获取当前作用域配置的 CAN 设备硬件指纹(与 GlobalInfo.CanPool 的 Key 一致)。
/// 如 "CAN:0",其中 0 是 CANConfig.DeviceIndex。
/// </summary>
public string GetCanDeviceFingerprint()
{
if (_systemConfig?.DeviceList == null) return string.Empty;
var canConfig = _systemConfig.DeviceList.FirstOrDefault(d =>
d != null && d.IsEnabled &&
string.Equals(d.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase));
return canConfig != null ? ExtractHardwareFingerprint(canConfig) : string.Empty;
}
private void InitDevices() private void InitDevices()
{ {
DeviceMap = new Dictionary<string, IBaseInterface>(StringComparer.OrdinalIgnoreCase); DeviceMap = new Dictionary<string, IBaseInterface>(StringComparer.OrdinalIgnoreCase);

View File

@@ -10,5 +10,6 @@ namespace UIShare.UIViewModel
public string SignalName { get; set; } public string SignalName { get; set; }
public int CollectionInterval { get; set; } public int CollectionInterval { get; set; }
public Guid CollectionID { get; set; } public Guid CollectionID { get; set; }
public string MethodName {get;set;}
} }
} }

View File

@@ -8,6 +8,7 @@ namespace UIShare.UIViewModel
private string _signalName = string.Empty; private string _signalName = string.Empty;
private string _fingerprint = string.Empty; private string _fingerprint = string.Empty;
private string _methodName = string.Empty; private string _methodName = string.Empty;
private string _displayName = string.Empty;
private double _upper; private double _upper;
private double _lower; private double _lower;
private double _upperExtreme; private double _upperExtreme;
@@ -23,6 +24,14 @@ namespace UIShare.UIViewModel
get => _signalName; get => _signalName;
set => SetProperty(ref _signalName, value); set => SetProperty(ref _signalName, value);
} }
/// <summary>
/// 信号名(显示用)
/// </summary>
public string DisplayName
{
get => _displayName;
set => SetProperty(ref _displayName, value);
}
/// <summary> /// <summary>
/// 硬件指纹(物理设备唯一标识,用于超限匹配) /// 硬件指纹(物理设备唯一标识,用于超限匹配)

View File

@@ -1,4 +1,5 @@
using Common.Attributes; using Common.Attributes;
using Logger;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
@@ -29,6 +30,10 @@ namespace ZLGUSBCANFD
// DBC 循环发送任务管理Key = (通道号, 帧ID) // DBC 循环发送任务管理Key = (通道号, 帧ID)
private readonly ConcurrentDictionary<(uint , uint ID), CancellationTokenSource> _cyclicSenders = new ConcurrentDictionary<(uint, uint), CancellationTokenSource>(); private readonly ConcurrentDictionary<(uint , uint ID), CancellationTokenSource> _cyclicSenders = new ConcurrentDictionary<(uint, uint), CancellationTokenSource>();
// 信号值持久化覆盖表Key = (通道号, 帧ID)Value = 信号名 → 物理值
// 每次 设置报文 会累积写入此表,发送时以 DBC 初始值为底再叠加此表覆盖值
private readonly ConcurrentDictionary<(uint , uint ID), Dictionary<string, double>> _signalOverrides = new ConcurrentDictionary<(uint, uint), Dictionary<string, double>>();
// 动态硬件参数 // 动态硬件参数
private readonly uint _deviceType; // 76: USBCANFD-400U private readonly uint _deviceType; // 76: USBCANFD-400U
private readonly uint _deviceIndex; // 设备索引 private readonly uint _deviceIndex; // 设备索引
@@ -371,14 +376,17 @@ namespace ZLGUSBCANFD
} }
catch (Exception ex) catch (Exception ex)
{ {
// 循环发送出错时退出,避免刷屏 Logger.LoggerHelper.Error($"[ZLGCANFD] 循环发送 DBC 报文失败: {ex.Message}");
Console.WriteLine($"[ZLGCANFD] 循环发送 DBC 报文失败: {ex.Message}");
break; break;
} }
} }
_cyclicSenders.TryRemove((, ID), out var removedCts); // 清理:仅当字典中存的仍然是本任务创建的 CTS 时才移除,避免误删新任务的 CTS
removedCts?.Dispose(); if (_cyclicSenders.TryGetValue((, ID), out var current) && ReferenceEquals(current, cts))
{
_cyclicSenders.TryRemove((, ID), out _);
}
cts.Dispose();
}, cts.Token); }, cts.Token);
return true; return true;
@@ -392,7 +400,7 @@ namespace ZLGUSBCANFD
if (_cyclicSenders.TryRemove((, ID), out var cts)) if (_cyclicSenders.TryRemove((, ID), out var cts))
{ {
cts.Cancel(); cts.Cancel();
cts.Dispose(); // 不在这里 Dispose由 Task 的清理代码负责释放,避免 Task 仍在使用已释放的 Token
} }
} }
@@ -409,6 +417,31 @@ namespace ZLGUSBCANFD
_cyclicSenders.Clear(); _cyclicSenders.Clear();
} }
/// <summary>
/// 清除指定通道、指定帧 ID 的信号覆盖值,恢复为 DBC 初始值。
/// </summary>
public virtual void (uint , uint ID)
{
if (_signalOverrides.TryRemove((, ID), out var dict))
{
lock (dict) dict.Clear();
}
}
/// <summary>
/// 清除指定通道所有帧的信号覆盖值。
/// </summary>
public virtual void (uint )
{
foreach (var key in _signalOverrides.Keys)
{
if (key. == && _signalOverrides.TryRemove(key, out var dict))
{
lock (dict) dict.Clear();
}
}
}
/// <summary> /// <summary>
@@ -536,7 +569,7 @@ namespace ZLGUSBCANFD
/// <param name="物理值">要写入的实际物理数值</param> /// <param name="物理值">要写入的实际物理数值</param>
/// <param name="循环发送间隔毫秒">0 = 单次发送;>0 = 周期循环发送(单位毫秒)</param> /// <param name="循环发送间隔毫秒">0 = 单次发送;>0 = 周期循环发送(单位毫秒)</param>
/// <returns>操作是否成功</returns> /// <returns>操作是否成功</returns>
public virtual bool (uint , string IDStr, string , double , int = 0) public virtual bool (uint , string IDStr, string , double , int = 0, bool =false)
{ {
if (!TryParseFrameId(IDStr, out uint ID)) if (!TryParseFrameId(IDStr, out uint ID))
{ {
@@ -548,12 +581,18 @@ namespace ZLGUSBCANFD
if (!_isDbcLoadedArray[]) return false; if (!_isDbcLoadedArray[]) return false;
if (string.IsNullOrWhiteSpace()) return false; if (string.IsNullOrWhiteSpace()) return false;
var merged = (, ID, new Dictionary<string, double>(StringComparer.OrdinalIgnoreCase) // 将本次设置的信号值写入持久化覆盖表(累积,不会丢失之前设置的值)
var overrides = _signalOverrides.GetOrAdd((, ID), _ => new Dictionary<string, double>(StringComparer.OrdinalIgnoreCase));
lock (overrides)
{ {
{ , } overrides[] = ;
}); }
return DBC定义报文(, ID, merged, ); // 构建发送字典DBC 初始值 + 持久化覆盖表(已包含本次设置)
var merged = (, ID);
if () return DBC定义报文(, ID, merged, );
else return true;
} }
/// <summary> /// <summary>
@@ -753,6 +792,7 @@ namespace ZLGUSBCANFD
var msg = (ZDBC.DBCMessage)Marshal.PtrToStructure(ptrDbcMsg, typeof(ZDBC.DBCMessage)); var msg = (ZDBC.DBCMessage)Marshal.PtrToStructure(ptrDbcMsg, typeof(ZDBC.DBCMessage));
var dict = new Dictionary<string, double>(StringComparer.OrdinalIgnoreCase); var dict = new Dictionary<string, double>(StringComparer.OrdinalIgnoreCase);
// 1. DBC 初始值打底
for (int i = 0; i < msg.nSignalCount; i++) for (int i = 0; i < msg.nSignalCount; i++)
{ {
var signal = msg.vSignals[i]; var signal = msg.vSignals[i];
@@ -763,6 +803,17 @@ namespace ZLGUSBCANFD
} }
} }
// 2. 叠加持久化覆盖表(之前 设置报文 累积的值)
if (_signalOverrides.TryGetValue((, ID), out var persisted))
{
lock (persisted)
{
foreach (var kvp in persisted)
dict[kvp.Key] = kvp.Value;
}
}
// 3. 叠加本次调用传入的额外覆盖(优先级最高)
if ( != null) if ( != null)
{ {
foreach (var kvp in ) foreach (var kvp in )
@@ -776,6 +827,7 @@ namespace ZLGUSBCANFD
} }
finally finally
{ {
Marshal.FreeHGlobal(ptrDbcMsg); Marshal.FreeHGlobal(ptrDbcMsg);
} }
} }

View File

@@ -8,6 +8,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Common\Common.csproj" /> <ProjectReference Include="..\Common\Common.csproj" />
<ProjectReference Include="..\Logger\Logger.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>