Cladude code 更改

This commit is contained in:
2026-05-14 18:26:22 +08:00
parent 268f4baf99
commit df3da9d9cb
4 changed files with 107 additions and 48 deletions

View File

@@ -283,6 +283,24 @@ namespace CapMachine.Wpf
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
//非UI线程未捕获异常处理事件 (例如自己创建的一个子线程) //非UI线程未捕获异常处理事件 (例如自己创建的一个子线程)
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
//首次抛出(含被吞掉的)异常也落日志,便于排查闪退场景下原生互操作异常
AppDomain.CurrentDomain.FirstChanceException += (s, fce) =>
{
try
{
var ex = fce.Exception;
//只关心严重异常,避免日志被刷爆
if (ex is AccessViolationException
|| ex is System.Runtime.InteropServices.SEHException
|| ex is StackOverflowException
|| ex is OutOfMemoryException)
{
LogService?.Error($"[FirstChance-FATAL] {ex.GetType().Name}: {ex.Message}\r\n{ex.StackTrace}");
}
}
catch { /* 异常处理函数中绝不能再抛 */ }
};
} }

View File

@@ -962,10 +962,30 @@ namespace CapMachine.Wpf.CanDrive
// 执行发送CAN逻辑 // 执行发送CAN逻辑
{ {
//再次校验前置条件,运行中若设备/DBC被关闭则直接跳过本轮避免传 0 句柄给原生导致 AV
if (!OpenState || DBCHandle == 0 || CmdData == null || CmdData.Count == 0)
{
await Task.Delay(10, token);
continue;
}
var GroupMsg = CmdData.GroupBy(x => x.MsgName); //本轮要发送的指令快照(避免遍历时 UI 线程并发修改)
USB2CAN.CAN_MSG[] CanMsg = new USB2CAN.CAN_MSG[GroupMsg.Count()]; List<CanCmdData> roundSnapshot;
for (int i = 0; i < GroupMsg.Count(); i++) lock (_canSendLock)
{
roundSnapshot = CmdData
.Where(x => !string.IsNullOrWhiteSpace(x.MsgName) && !string.IsNullOrWhiteSpace(x.SignalName))
.ToList();
}
if (roundSnapshot.Count == 0)
{
await Task.Delay(10, token);
continue;
}
var GroupMsg = roundSnapshot.GroupBy(x => x.MsgName).ToList();
USB2CAN.CAN_MSG[] CanMsg = new USB2CAN.CAN_MSG[GroupMsg.Count];
for (int i = 0; i < GroupMsg.Count; i++)
{ {
CanMsg[i] = new USB2CAN.CAN_MSG(); CanMsg[i] = new USB2CAN.CAN_MSG();
CanMsg[i].Data = new Byte[64]; CanMsg[i].Data = new Byte[64];
@@ -973,38 +993,33 @@ namespace CapMachine.Wpf.CanDrive
// 发送构帧临时缓冲:每轮申请/释放,避免与其他线程共享同一指针导致并发问题 // 发送构帧临时缓冲:每轮申请/释放,避免与其他线程共享同一指针导致并发问题
IntPtr msgPtSend = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CAN.CAN_MSG))); IntPtr msgPtSend = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CAN.CAN_MSG)));
int Index = 0; try
//循环给MSG赋值数据
foreach (var itemMsg in GroupMsg)
{ {
foreach (var itemSignal in itemMsg) int Index = 0;
//循环给MSG赋值数据DBC 解析器在 .Net 侧不是线程安全的,必须串行化
lock (_dbcParserLock)
{ {
CAN_DBCParser.DBC_SetSignalValue(DBCHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue); foreach (var itemMsg in GroupMsg)
{
foreach (var itemSignal in itemMsg)
{
CAN_DBCParser.DBC_SetSignalValue(DBCHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue);
}
CAN_DBCParser.DBC_SyncValueToCANMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPtSend);
CanMsg[Index] = (USB2CAN.CAN_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CAN.CAN_MSG));
Index++;
}
} }
CAN_DBCParser.DBC_SyncValueToCANMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPtSend);
CanMsg[Index] = (USB2CAN.CAN_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CAN.CAN_MSG));
Index++;
} }
finally
//通过DBC写入数据后生成CanMsg {
//将信号值填入CAN消息里面 // 释放非托管缓冲区(务必释放,否则长时间运行会造成内存泄漏)
Marshal.FreeHGlobal(msgPtSend);
// 释放非托管缓冲区(务必释放,否则长时间运行会造成内存泄漏) }
Marshal.FreeHGlobal(msgPtSend);
//发送CAN数据 //发送CAN数据
int SendedNum = USB2CAN.CAN_SendMsg(DevHandle, WriteCANIndex, CanMsg, (uint)CanMsg.Length); int SendedNum = USB2CAN.CAN_SendMsg(DevHandle, WriteCANIndex, CanMsg, (uint)CanMsg.Length);
if (SendedNum >= 0) IsSendOk = SendedNum >= 0;
{
//Console.WriteLine("Success send frames:{0}", SendedNum);
IsSendOk = true;
}
else
{
//Console.WriteLine("Send CAN data failed! {0}", SendedNum);
IsSendOk = false;
}
} }

View File

@@ -7,6 +7,7 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<LegacyCorruptedStateExceptionsPolicy>true</LegacyCorruptedStateExceptionsPolicy>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -210,31 +210,56 @@ namespace CapMachine.Wpf.Services
/// <summary> /// <summary>
/// 循环发送数据到CAN /// 循环发送数据到CAN
/// </summary> /// </summary>
public void CycleSendMsg() public void CycleSendMsg()
{ {
if (ToomossCanDrive.OpenState) if (!ToomossCanDrive.OpenState)
{ {
if (ToomossCanDrive.IsCycleSend == false) System.Windows.MessageBox.Show("CAN未打开请先连接CAN", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand);
{ return;
if (CmdData.Count > 0)
{
ToomossCanDrive.IsCycleSend = true;
ToomossCanDrive.CmdData = CmdData;
ToomossCanDrive.StartPrecisionCycleSendMsg();
}
else
{
System.Windows.MessageBox.Show("未发现配置的数据内容", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand);
}
}
else
{
ToomossCanDrive.StopCycleSendMsg();
}
} }
//停止已在运行的循环
if (ToomossCanDrive.IsCycleSend == true)
{
ToomossCanDrive.StopCycleSendMsg();
return;
}
//必要的前置校验DBC 必须已解析成功,否则原生互操作直接 AccessViolation 闪退
if (!ToomossCanDrive.DbcParserState || ToomossCanDrive.DBCHandle == 0)
{
System.Windows.MessageBox.Show("DBC尚未解析成功无法循环发送", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand);
return;
}
//过滤掉 MsgName / SignalName 为空的行(空名传给原生 DBC 解析器是 AV 常见诱因)
List<CanCmdData> cmdDataSnapshot;
lock (_cmdDataLock)
{
cmdDataSnapshot = CmdData
.Where(x => !string.IsNullOrWhiteSpace(x.MsgName) && !string.IsNullOrWhiteSpace(x.SignalName))
.Select(x => new CanCmdData
{
ConfigName = x.ConfigName,
MsgName = x.MsgName,
SignalName = x.SignalName,
SignalCmdValue = x.SignalCmdValue
})
.ToList();
}
if (cmdDataSnapshot.Count == 0)
{
System.Windows.MessageBox.Show("未发现可发送的数据(请检查写入配置的【消息名称】/【信号名称】是否为空)", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand);
return;
}
ToomossCanDrive.IsCycleSend = true;
//传入的是已经过滤的快照副本,避免与 UI 线程共享同一引用
ToomossCanDrive.CmdData = cmdDataSnapshot;
ToomossCanDrive.StartPrecisionCycleSendMsg();
} }