فهرست منبع

fix: 修复CTS释放崩溃、图片保存失败及PLC回调异常静默丢弃

- ShowVisionRender: 新增GetSafeSaveToken()自愈模式,CTS释放后自动重建而非放弃存图
- ShowVisionRender: 新增[SAVE-IMAGE]诊断日志,便于追踪存图流程
- Management: PlcCommandTrigger改为async lambda+try-catch,防止异常静默丢弃
赵留洋 2 ماه پیش
والد
کامیت
9fb515db8d
2فایلهای تغییر یافته به همراه45 افزوده شده و 5 حذف شده
  1. 11 1
      TeamAAS-VM/Core/Management.cs
  2. 34 4
      TeamAAS-VM/Views/Home/ShowVisionRender.xaml.cs

+ 11 - 1
TeamAAS-VM/Core/Management.cs

@@ -1527,7 +1527,17 @@ namespace TeamAAS_VP.Core
         {
             // 立即释放OPC UA回调线程,避免阻塞Session导致后续WriteNodeAsync无法派发
             LogHelper.WriteLogInfo($"[PLC-TRACE] Trigger queued key={tuple.key}, node={tuple.nodeId}, value={tuple.value}, thread={Thread.CurrentThread.ManagedThreadId}, time={DateTime.Now:HH:mm:ss.fff}");
-            _ = Task.Run(() => PlcCommandTriggerAsync(tuple));
+            _ = Task.Run(async () =>
+            {
+                try
+                {
+                    await PlcCommandTriggerAsync(tuple).ConfigureAwait(false);
+                }
+                catch (Exception ex)
+                {
+                    LogHelper.WriteLogError($"PlcCommandTriggerAsync 执行异常, key={tuple.key}, node={tuple.nodeId}", ex);
+                }
+            });
         }
 
         /// <summary>

+ 34 - 4
TeamAAS-VM/Views/Home/ShowVisionRender.xaml.cs

@@ -46,9 +46,30 @@ namespace TeamAAS_VP.Views.Home
         private readonly Prism.Services.Dialogs.IDialogService _dialogService;
 
         private CancellationTokenSource _saveImageCts = new CancellationTokenSource();
+        private readonly object _saveImageCtsLock = new object();
         private readonly SemaphoreSlim _saveSemaphore = new SemaphoreSlim(5, 5);
         private List<Models.DelaySaveImage> delaySaveImages = new List<Models.DelaySaveImage>();
 
+        /// <summary>
+        /// 安全获取有效的 CancellationToken。如果当前 CTS 已释放则自动创建新的。
+        /// </summary>
+        private CancellationToken GetSafeSaveToken()
+        {
+            lock (_saveImageCtsLock)
+            {
+                try
+                {
+                    var token = _saveImageCts.Token;
+                    return token;
+                }
+                catch (ObjectDisposedException)
+                {
+                    _saveImageCts = new CancellationTokenSource();
+                    return _saveImageCts.Token;
+                }
+            }
+        }
+
         public ShowVisionRender()
         {
             InitializeComponent();
@@ -648,13 +669,19 @@ namespace TeamAAS_VP.Views.Home
         {
             if (render == null) return;
             if (disp == null) return;
-            if (!render.IsSaveImage) return;
+            if (!render.IsSaveImage)
+            {
+                LogHelper.WriteLogInfo($"[SAVE-IMAGE] 跳过保存: IsSaveImage=false, Id={render.Id}, DisplayName={render.DisplayName}");
+                return;
+            }
+
+            LogHelper.WriteLogInfo($"[SAVE-IMAGE] 开始保存流程: Id={render.Id}, SaveModel={render.SaveImageModel}, SavePath={render.SavePath}, IsDelay={render.IsDelaySaveImage}, ProductCode={_productService.CurrentProductCode}");
 
             bool needRecordedBitmap =
                 render.SaveImageModel == ProcedureSaveImageModel.Recorded ||
                 render.SaveImageModel == ProcedureSaveImageModel.OriginalAndRecorded;
 
-            var token = _saveImageCts.Token;
+            var token = GetSafeSaveToken();
 
             disp.BeginInvoke(new Action(() =>
             {
@@ -773,11 +800,14 @@ namespace TeamAAS_VP.Views.Home
         {
             if (string.IsNullOrEmpty(path) || string.IsNullOrWhiteSpace(path) || !Path.IsPathRooted(path))
             {
+                LogHelper.WriteLogInfo($"[SAVE-IMAGE] 保存失败:路径无效 path=\"{path}\"");
                 _eventAggregator.GetEvent<TaskMessageNotification>()
                     .Publish(new Models.MessageStruct() { Message = "保存图像路径为无效的路径!", level = MessageLevel.Alarm });
                 return;
             }
 
+            LogHelper.WriteLogInfo($"[SAVE-IMAGE] SaveImageAsync 开始: path={path}, model={saveImageModel}, pathModel={saveImagePathModel}, result={result}, is3D={is3Dmodel}, fileName={imageFileName}");
+
             try
             {
                 await Task.CompletedTask; // 消除 CS1998:调用者已在后台线程,下方所有 I/O 为同步执行
@@ -1000,7 +1030,7 @@ namespace TeamAAS_VP.Views.Home
             catch (OperationCanceledException) { }
             catch (Exception ex)
             {
-                LogHelper.WriteLogError("保存流程运行结果的图片时出错!", ex);
+                LogHelper.WriteLogError($"[SAVE-IMAGE] 保存流程运行结果的图片时出错!path={path}", ex);
             }
             finally
             {
@@ -1034,7 +1064,7 @@ namespace TeamAAS_VP.Views.Home
                 delaySaveImages.Clear();
             }
 
-            var token = _saveImageCts.Token;
+            var token = GetSafeSaveToken();
             foreach (var item in itemsToSave)
             {
                 try