canbug修复
This commit is contained in:
@@ -117,13 +117,23 @@ namespace MonitorModule.ViewModels.Dialogs
|
|||||||
EnsureValueLimit(fingerprint, displayName, displayName, methodName);
|
EnsureValueLimit(fingerprint, displayName, displayName, methodName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 发现 CAN 信号并加入 ValueLimitList / DeviceSingleList
|
// 3. 发现 CAN 信号并加入 ValueLimitList,同时收集有效信号名
|
||||||
|
var validCanSignalNames = new HashSet<string>();
|
||||||
foreach (var (displayName, fingerprint, methodName) in DiscoverCanSignals())
|
foreach (var (displayName, fingerprint, methodName) in DiscoverCanSignals())
|
||||||
{
|
{
|
||||||
//DeviceSingleList.Add(displayName);
|
|
||||||
_canSignalMap[displayName] = (fingerprint, methodName);
|
_canSignalMap[displayName] = (fingerprint, methodName);
|
||||||
EnsureValueLimit(fingerprint, displayName, displayName, methodName);
|
EnsureValueLimit(fingerprint, displayName, displayName, methodName);
|
||||||
|
validCanSignalNames.Add(displayName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. 清理已不存在于 DBC 中的 CAN 信号(Fingerprint 以 "CAN" 开头且不在有效集合中)
|
||||||
|
var staleCanSignals = ValueLimitList
|
||||||
|
.Where(x => !string.IsNullOrEmpty(x.Fingerprint) &&
|
||||||
|
x.Fingerprint.StartsWith("CAN") &&
|
||||||
|
!validCanSignalNames.Contains(x.SignalName))
|
||||||
|
.ToList();
|
||||||
|
foreach (var item in staleCanSignals)
|
||||||
|
ValueLimitList.Remove(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ namespace UIShare.UIViewModel
|
|||||||
if (_series != null)
|
if (_series != null)
|
||||||
{
|
{
|
||||||
_series.Points.Add(new DataPoint(time, displayValue));
|
_series.Points.Add(new DataPoint(time, displayValue));
|
||||||
while (_series.Points.Count > 200)
|
while (_series.Points.Count > 10000)
|
||||||
{
|
{
|
||||||
_series.Points.RemoveAt(0);
|
_series.Points.RemoveAt(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,10 +25,11 @@ namespace ZLGUSBCANFD
|
|||||||
|
|
||||||
// 异步高性能接收线程控制
|
// 异步高性能接收线程控制
|
||||||
private volatile bool _isRunning = false;
|
private volatile bool _isRunning = false;
|
||||||
|
private volatile bool _isClosing = false; // 关闭流程标志:通知循环发送任务尽快退出
|
||||||
private readonly List<Thread> _receiveThreads = new List<Thread>();
|
private readonly List<Thread> _receiveThreads = new List<Thread>();
|
||||||
|
|
||||||
// DBC 循环发送任务管理:Key = (通道号, 帧ID)
|
// DBC 循环发送任务管理:Key = (通道号, 帧ID),Value = (CTS, Task)
|
||||||
private readonly ConcurrentDictionary<(uint 通道号, uint 帧ID), CancellationTokenSource> _cyclicSenders = new ConcurrentDictionary<(uint, uint), CancellationTokenSource>();
|
private readonly ConcurrentDictionary<(uint 通道号, uint 帧ID), (CancellationTokenSource Cts, Task SendTask)> _cyclicSenders = new ConcurrentDictionary<(uint, uint), (CancellationTokenSource, Task)>();
|
||||||
|
|
||||||
// 信号值持久化覆盖表:Key = (通道号, 帧ID),Value = 信号名 → 物理值
|
// 信号值持久化覆盖表:Key = (通道号, 帧ID),Value = 信号名 → 物理值
|
||||||
// 每次 设置报文 会累积写入此表,发送时以 DBC 初始值为底再叠加此表覆盖值
|
// 每次 设置报文 会累积写入此表,发送时以 DBC 初始值为底再叠加此表覆盖值
|
||||||
@@ -98,15 +99,17 @@ namespace ZLGUSBCANFD
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual bool 初始化并启动通道(uint 通道号)
|
public virtual bool 初始化并启动通道(uint 通道号)
|
||||||
{
|
{
|
||||||
|
_isClosing = false; // 重置关闭标志,允许循环发送
|
||||||
if (_deviceHandle == IntPtr.Zero) throw new InvalidOperationException("请先调用 '打开设备()' 才能初始化通道。");
|
if (_deviceHandle == IntPtr.Zero) throw new InvalidOperationException("请先调用 '打开设备()' 才能初始化通道。");
|
||||||
if (通道号 >= _maxChannels) return false;
|
if (通道号 >= _maxChannels) return false;
|
||||||
|
|
||||||
lock (_channelLocks[通道号])
|
lock (_channelLocks[通道号])
|
||||||
{
|
{
|
||||||
// 如果已经启动过,先复位
|
// 如果已经启动过,直接退出
|
||||||
if (_channelHandles[通道号] != IntPtr.Zero)
|
if (_channelHandles[通道号] != IntPtr.Zero)
|
||||||
{
|
{
|
||||||
ZLGCAN.ZCAN_ResetCAN(_channelHandles[通道号]);
|
return true;
|
||||||
|
//ZLGCAN.ZCAN_ResetCAN(_channelHandles[通道号]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 设置该通道专属的仲裁域与数据域波特率
|
// 1. 设置该通道专属的仲裁域与数据域波特率
|
||||||
@@ -150,15 +153,24 @@ namespace ZLGUSBCANFD
|
|||||||
|
|
||||||
public virtual void 关闭CAN卡设备()
|
public virtual void 关闭CAN卡设备()
|
||||||
{
|
{
|
||||||
_isRunning = false;
|
_isClosing = true; // 通知循环发送任务尽快退出
|
||||||
停止所有循环发送();
|
_isRunning = false; // 通知接收轮询线程退出
|
||||||
Thread.Sleep(50); // 确保轮询线程安全退出
|
停止所有循环发送(); // 内部会等待所有循环发送 Task 退出
|
||||||
|
|
||||||
|
// 等待所有接收轮询线程真正退出(每个最多 1 秒)
|
||||||
|
foreach (var thread in _receiveThreads)
|
||||||
|
{
|
||||||
|
if (thread.IsAlive)
|
||||||
|
thread.Join(1000);
|
||||||
|
}
|
||||||
_receiveThreads.Clear();
|
_receiveThreads.Clear();
|
||||||
|
|
||||||
//动态复位所有通道并释放DBC
|
// 动态复位所有通道并释放 DBC(使用 TryEnter 防止死锁)
|
||||||
for (uint i = 0; i < _maxChannels; i++)
|
for (uint i = 0; i < _maxChannels; i++)
|
||||||
{
|
{
|
||||||
lock (_channelLocks[i])
|
if (Monitor.TryEnter(_channelLocks[i], TimeSpan.FromSeconds(2)))
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (_channelHandles[i] != IntPtr.Zero)
|
if (_channelHandles[i] != IntPtr.Zero)
|
||||||
{
|
{
|
||||||
@@ -167,6 +179,22 @@ namespace ZLGUSBCANFD
|
|||||||
}
|
}
|
||||||
释放通道DBC(i);
|
释放通道DBC(i);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Monitor.Exit(_channelLocks[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 锁超时:循环发送 Task 可能仍阻塞在原生 API 调用中,强制清理
|
||||||
|
LoggerHelper.Info($"[ZLGCANFD] 关闭通道 {i} 时获取锁超时,强制清理");
|
||||||
|
if (_channelHandles[i] != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
try { ZLGCAN.ZCAN_ResetCAN(_channelHandles[i]); } catch { }
|
||||||
|
_channelHandles[i] = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
释放通道DBC(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭设备主句柄
|
// 关闭设备主句柄
|
||||||
@@ -175,6 +203,8 @@ namespace ZLGUSBCANFD
|
|||||||
ZLGCAN.ZCAN_CloseDevice(_deviceHandle);
|
ZLGCAN.ZCAN_CloseDevice(_deviceHandle);
|
||||||
_deviceHandle = IntPtr.Zero;
|
_deviceHandle = IntPtr.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_isClosing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -354,17 +384,16 @@ namespace ZLGUSBCANFD
|
|||||||
停止循环发送(通道号, 帧ID);
|
停止循环发送(通道号, 帧ID);
|
||||||
|
|
||||||
var cts = new CancellationTokenSource();
|
var cts = new CancellationTokenSource();
|
||||||
_cyclicSenders[(通道号, 帧ID)] = cts;
|
var sendTask = Task.Run(async () =>
|
||||||
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
while (!cts.Token.IsCancellationRequested)
|
while (!cts.Token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
|
if (_isClosing) break; // 关闭流程中立即退出
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
lock (_channelLocks[通道号])
|
lock (_channelLocks[通道号])
|
||||||
{
|
{
|
||||||
if (_channelHandles[通道号] == IntPtr.Zero) break;
|
if (_channelHandles[通道号] == IntPtr.Zero || _isClosing) break;
|
||||||
发送DBC定义报文单次(通道号, 帧ID, 信号物理值字典, 是否使用CANFD);
|
发送DBC定义报文单次(通道号, 帧ID, 信号物理值字典, 是否使用CANFD);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,13 +411,15 @@ namespace ZLGUSBCANFD
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 清理:仅当字典中存的仍然是本任务创建的 CTS 时才移除,避免误删新任务的 CTS
|
// 清理:仅当字典中存的仍然是本任务创建的 CTS 时才移除,避免误删新任务的 CTS
|
||||||
if (_cyclicSenders.TryGetValue((通道号, 帧ID), out var current) && ReferenceEquals(current, cts))
|
if (_cyclicSenders.TryGetValue((通道号, 帧ID), out var current) && ReferenceEquals(current.Cts, cts))
|
||||||
{
|
{
|
||||||
_cyclicSenders.TryRemove((通道号, 帧ID), out _);
|
_cyclicSenders.TryRemove((通道号, 帧ID), out _);
|
||||||
}
|
}
|
||||||
cts.Dispose();
|
cts.Dispose();
|
||||||
}, cts.Token);
|
}, cts.Token);
|
||||||
|
|
||||||
|
_cyclicSenders[(通道号, 帧ID)] = (cts, sendTask);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,9 +428,9 @@ namespace ZLGUSBCANFD
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void 停止循环发送(uint 通道号, uint 帧ID)
|
public virtual void 停止循环发送(uint 通道号, uint 帧ID)
|
||||||
{
|
{
|
||||||
if (_cyclicSenders.TryRemove((通道号, 帧ID), out var cts))
|
if (_cyclicSenders.TryRemove((通道号, 帧ID), out var entry))
|
||||||
{
|
{
|
||||||
cts.Cancel();
|
entry.Cts.Cancel();
|
||||||
// 不在这里 Dispose,由 Task 的清理代码负责释放,避免 Task 仍在使用已释放的 Token
|
// 不在这里 Dispose,由 Task 的清理代码负责释放,避免 Task 仍在使用已释放的 Token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -409,11 +440,30 @@ namespace ZLGUSBCANFD
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void 停止所有循环发送()
|
public virtual void 停止所有循环发送()
|
||||||
{
|
{
|
||||||
foreach (var kvp in _cyclicSenders)
|
var entries = _cyclicSenders.ToArray();
|
||||||
|
|
||||||
|
// 1. 取消所有 CTS(不 Dispose,由 Task 清理代码负责)
|
||||||
|
foreach (var kvp in entries)
|
||||||
{
|
{
|
||||||
kvp.Value.Cancel();
|
kvp.Value.Cts.Cancel();
|
||||||
kvp.Value.Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. 等待所有循环发送 Task 退出(最多 3 秒)
|
||||||
|
var tasks = new List<Task>();
|
||||||
|
foreach (var kvp in entries)
|
||||||
|
{
|
||||||
|
if (!kvp.Value.SendTask.IsCompleted)
|
||||||
|
tasks.Add(kvp.Value.SendTask);
|
||||||
|
}
|
||||||
|
if (tasks.Count > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Task.WaitAll(tasks.ToArray(), TimeSpan.FromSeconds(3));
|
||||||
|
}
|
||||||
|
catch { /* 忽略等待异常 */ }
|
||||||
|
}
|
||||||
|
|
||||||
_cyclicSenders.Clear();
|
_cyclicSenders.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user