فهرست منبع

针对图片保存的优化,当拍照时,还没有获得流程码;将图片缓存,等待SN到位,再存储

844184169 6 ماه پیش
والد
کامیت
76564699be

+ 4 - 0
TeamAAS-VM/Core/Management.cs

@@ -2273,6 +2273,7 @@ namespace TeamAAS_VP.Core
                                                         ProductMainSN[i] = item[4].Replace("sn=", "");
                                                         SendTaskMessage($"产品主sn:{ProductMainSN[i]}", MessageLevel.Info);
                                                         _productService.CurrentProductCode = ProductMainSN[i];
+                                                        _eventAggregator.GetEvent<DelaySaveImageNotification>().Publish(true);//通知界面延迟保存图片,等待MES查询结果返回,避免图片和数据不同步
                                                     }
                                                     if (item[5].Contains("wo="))
                                                     {
@@ -2361,6 +2362,7 @@ namespace TeamAAS_VP.Core
                                                     ProductMainSN[i] = item[4].Replace("sn=", "");
                                                     SendTaskMessage($"产品主sn:{ProductMainSN[i]}", MessageLevel.Info);
                                                     _productService.CurrentProductCode = ProductMainSN[i];
+                                                    _eventAggregator.GetEvent<DelaySaveImageNotification>().Publish(true);//通知界面延迟保存图片,等待MES查询结果返回,避免图片和数据不同步
                                                 }
                                                 if (item[5].Contains("wo="))
                                                 {
@@ -2418,6 +2420,7 @@ namespace TeamAAS_VP.Core
                                         if (i == 0)
                                         {
                                             _productService.CurrentProductCode = ProductMainSN[i];
+                                            _eventAggregator.GetEvent<DelaySaveImageNotification>().Publish(true);//通知界面延迟保存图片,等待MES查询结果返回,避免图片和数据不同步
                                         }
 
                                         LogHelper.WriteLogMes($"【未启用mes】");
@@ -2446,6 +2449,7 @@ namespace TeamAAS_VP.Core
                                         if (i == 0)
                                         {
                                             CTtime = serverTimeResult.Now.Subtract(_LastMesCheckTime[i]).TotalSeconds;
+                                            _eventAggregator.GetEvent<DelaySaveImageNotification>().Publish(false);//通知界面延迟保存图片,等待MES查询结果返回,避免图片和数据不同步
                                         }
 
                                         //读取产品结果

+ 8 - 0
TeamAAS-VM/Events/DelaySaveImageNotification.cs

@@ -0,0 +1,8 @@
+using Prism.Events;
+
+namespace TeamAAS_VP.Events
+{
+    public class DelaySaveImageNotification : PubSubEvent<bool>
+    {
+    }
+}

+ 89 - 0
TeamAAS-VM/Models/DelaySaveImage.cs

@@ -0,0 +1,89 @@
+using Prism.Mvvm;
+using System.Drawing;
+using TeamAAS_VP.Enums;
+
+namespace TeamAAS_VP.Models
+{
+    /// <summary>
+    /// 延迟保存的图像
+    /// </summary>
+    public class DelaySaveImage : BindableBase
+    {
+        private ProcedureSaveImageModel _SaveImageModel;
+        /// <summary>
+        /// 存图模式
+        /// </summary>
+        public ProcedureSaveImageModel SaveImageModel
+        {
+            get { return _SaveImageModel; }
+            set { SetProperty(ref _SaveImageModel, value); }
+        }
+
+        private ProcedureSaveImagePathModel _SaveImagePathModel;
+        /// <summary>
+        /// 存图路径模式
+        /// </summary>
+        public ProcedureSaveImagePathModel SaveImagePathModel
+        {
+            get { return _SaveImagePathModel; }
+            set { SetProperty(ref _SaveImagePathModel, value); }
+        }
+
+        private string _SavePath;
+        /// <summary>
+        /// 存图路径
+        /// </summary>
+        public string SavePath
+        {
+            get { return _SavePath; }
+            set { SetProperty(ref _SavePath, value); }
+        }
+
+        private Cognex.VisionPro.ICogImage _Image;
+
+        public Cognex.VisionPro.ICogImage Image
+        {
+            get { return _Image; }
+            set { SetProperty(ref _Image, value); }
+        }
+
+        private Bitmap _clonedBitmap;
+        /// <summary>
+        /// 图片克隆,保存图像时使用,避免图像被修改导致保存失败
+        /// </summary>
+        public Bitmap clonedBitmap
+        {
+            get { return _clonedBitmap; }
+            set { SetProperty(ref _clonedBitmap, value); }
+        }
+
+        private bool _result;
+
+        public bool Result
+        {
+            get { return _result; }
+            set { SetProperty(ref _result, value); }
+        }
+
+        private bool _IsCompress;
+        /// <summary>
+        /// 是否压缩图片
+        /// </summary>
+        public bool IsCompress
+        {
+            get { return _IsCompress; }
+            set { SetProperty(ref _IsCompress, value); }
+        }
+
+        //用户定义的图片文件名
+        private string _ImageFileName;
+        /// <summary>
+        /// 用户定义的图片文件名
+        /// </summary>
+        public string ImageFileName
+        {
+            get { return _ImageFileName; }
+            set { SetProperty(ref _ImageFileName, value); }
+        }
+    }
+}

+ 29 - 18
TeamAAS-VM/Models/Product/ProcedureModel.cs

@@ -38,7 +38,7 @@ namespace TeamAAS_VP.Models
         /// <summary>
         /// 触发相机命令
         /// </summary>
-        public string TriggerCommand {  get; set; }
+        public string TriggerCommand { get; set; }
 
         /// <summary>
         /// 机器人
@@ -68,7 +68,7 @@ namespace TeamAAS_VP.Models
         public ObservableCollection<ProcedureOutput> ProcedureOutputs
         {
             get { return _ProcedureOutputs; }
-            set { SetProperty(ref _ProcedureOutputs,value); }
+            set { SetProperty(ref _ProcedureOutputs, value); }
         }
 
         private string _OutputSeparator = ";";
@@ -89,7 +89,7 @@ namespace TeamAAS_VP.Models
         public FeederWorks FeederWorks
         {
             get { return _FeederWorks; }
-            set { SetProperty(ref _FeederWorks,value); }
+            set { SetProperty(ref _FeederWorks, value); }
         }
 
         private FeederStockWorks _FeederStockWorks;
@@ -130,7 +130,7 @@ namespace TeamAAS_VP.Models
         public float ExposureTime
         {
             get { return _ExposureTime; }
-            set { SetProperty(ref _ExposureTime, value);}
+            set { SetProperty(ref _ExposureTime, value); }
         }
 
         private float _Gain;
@@ -151,7 +151,7 @@ namespace TeamAAS_VP.Models
         public CogToolBlock ToolBlock
         {
             get { return _ToolBlock; }
-            set { SetProperty(ref _ToolBlock,value); }
+            set { SetProperty(ref _ToolBlock, value); }
         }
 
         private ObservableCollection<ToolInfo> _ToolInfo = new ObservableCollection<ToolInfo>();
@@ -214,7 +214,7 @@ namespace TeamAAS_VP.Models
             set { SetProperty(ref _OutputPointMode, value); }
         }
 
-        private int _OutputPointCountMax=100;
+        private int _OutputPointCountMax = 100;
         /// <summary>
         /// 每次发送给机器人的点数最大值
         /// </summary>
@@ -229,12 +229,12 @@ namespace TeamAAS_VP.Models
         /// 是否剔除本次识别到的物料与上一次识别到的物料位置过近的点
         /// </summary>
         public bool IsExcludeNearPoint
-            {
+        {
             get { return _IsExcludeNearPoint; }
             set { SetProperty(ref _IsExcludeNearPoint, value); }
         }
 
-        private double _ExcludeNearPointThreshol=1;
+        private double _ExcludeNearPointThreshol = 1;
         /// <summary>
         /// 物料位置过近阈值
         /// </summary>
@@ -305,6 +305,16 @@ namespace TeamAAS_VP.Models
             set { SetProperty(ref _IsCompress, value); }
         }
 
+        private bool _IsDelaySaveImage;
+        /// <summary>
+        /// 是否延迟保存图片
+        /// </summary>
+        public bool IsDelaySaveImage
+        {
+            get { return _IsDelaySaveImage; }
+            set { SetProperty(ref _IsDelaySaveImage, value); }
+        }
+
         private FeederFrequencyModel _FeederFindFrequency;
         /// <summary>
         /// Feeder自动寻频配置
@@ -365,11 +375,11 @@ namespace TeamAAS_VP.Models
         {
             Id = Guid.NewGuid();
             ProcedureOutputs = new ObservableCollection<ProcedureOutput>();
-            ToolInfo= new ObservableCollection<ToolInfo>();
-            FeederFindFrequency= new FeederFrequencyModel();
-            UserDefineParameters= new ObservableCollection<ProcedureUserDefineParam>();
+            ToolInfo = new ObservableCollection<ToolInfo>();
+            FeederFindFrequency = new FeederFrequencyModel();
+            UserDefineParameters = new ObservableCollection<ProcedureUserDefineParam>();
             FeederWorks = new FeederWorks();
-            FeederStockWorks= new FeederStockWorks();
+            FeederStockWorks = new FeederStockWorks();
             LightChannels = new ObservableCollection<ChannelConfig>();
         }
 
@@ -394,9 +404,9 @@ namespace TeamAAS_VP.Models
             {
                 Id = this.Id,
                 Name = this.Name,
-                CameraName=this.CameraName,
+                CameraName = this.CameraName,
                 VisionModel = this.VisionModel,
-                TriggerCommand=this.TriggerCommand,
+                TriggerCommand = this.TriggerCommand,
                 RobotId = this.RobotId,
                 FeederId = this.FeederId,
                 CameraId = this.CameraId,
@@ -419,13 +429,14 @@ namespace TeamAAS_VP.Models
                 SaveImageModel = this.SaveImageModel,
                 SaveImagePathModel = this.SaveImagePathModel,
                 SavePath = this.SavePath,
-                ImageFileNameFormat=this.ImageFileNameFormat,
+                ImageFileNameFormat = this.ImageFileNameFormat,
                 IsCompress = this.IsCompress,
+                IsDelaySaveImage = this.IsDelaySaveImage,
                 FeederFindFrequency = this.FeederFindFrequency?.Copy(),
-                IsExcludeNearPoint=this.IsExcludeNearPoint,
+                IsExcludeNearPoint = this.IsExcludeNearPoint,
                 ExcludeNearPointThreshol = this.ExcludeNearPointThreshol,
-                OutputPointMode=this.OutputPointMode,
-                OutputSeparator=this.OutputSeparator,
+                OutputPointMode = this.OutputPointMode,
+                OutputSeparator = this.OutputSeparator,
                 UserDefineParameters = new ObservableCollection<ProcedureUserDefineParam>(this.UserDefineParameters.Select(udp => udp.Copy())),
                 LightChannels = this.LightChannels == null ? new ObservableCollection<ChannelConfig>() : new ObservableCollection<ChannelConfig>(this.LightChannels.Select(ch => new ChannelConfig
                 {

+ 10 - 0
TeamAAS-VM/Models/ShowRender.cs

@@ -122,6 +122,16 @@ namespace TeamAAS_VP.Models
             set { SetProperty(ref _IsCompress, value); }
         }
 
+        private bool _IsDelaySaveImage;
+        /// <summary>
+        /// 是否延迟保存图片
+        /// </summary>
+        public bool IsDelaySaveImage
+        {
+            get { return _IsDelaySaveImage; }
+            set { SetProperty(ref _IsDelaySaveImage, value); }
+        }
+
         private ImageParameters _ImgPara;
         /// <summary>
         /// 图片保存附加参数

+ 18 - 0
TeamAAS-VM/Resources/Languages/Lang.Designer.cs

@@ -4704,6 +4704,15 @@ namespace TeamAAS_VP.Resources.Languages {
             }
         }
         
+        /// <summary>
+        ///   查找类似 延迟保存图片 的本地化字符串。
+        /// </summary>
+        public static string 延迟保存图片 {
+            get {
+                return ResourceManager.GetString("延迟保存图片", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   查找类似 开启后台Modbus TCP通讯时出错! 的本地化字符串。
         /// </summary>
@@ -6297,6 +6306,15 @@ namespace TeamAAS_VP.Resources.Languages {
             }
         }
         
+        /// <summary>
+        ///   查找类似 是否延迟保存图片 的本地化字符串。
+        /// </summary>
+        public static string 是否延迟保存图片 {
+            get {
+                return ResourceManager.GetString("是否延迟保存图片", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   查找类似 是否开始测试? 的本地化字符串。
         /// </summary>

+ 15 - 9
TeamAAS-VM/Resources/Languages/Lang.en.resx

@@ -2514,6 +2514,9 @@
   <data name="AfagFeeder写入参数时出错" xml:space="preserve">
     <value>Error occurred when writing parameters to Afag Feeder!</value>
   </data>
+  <data name="为PLC写入系统参数和订阅变量时出错" xml:space="preserve">
+    <value>An error occurred while writing system parameters to the PLC and subscribing to variables.</value>
+  </data>
   <data name="选中Feeder序列时出错" xml:space="preserve">
     <value>Error occurred when selecting Feeder sequence!</value>
   </data>
@@ -2634,15 +2637,15 @@
   <data name="轴停滞超时整体判定目标位置X0Y1Z2U3超时阈值ms4" xml:space="preserve">
     <value>Axis stagnation timeout (overall judgment), Target position: X={0},Y={1},Z={2},U={3}, Timeout threshold(ms)={4}</value>
   </data>
-  <data name="后台Modbustcp读取0" xml:space="preserve">
-    <value>Background Modbus TCP read {0}</value>
-  </data>
   <data name="机器人执行移动超时位置X0Y1Z2U3" xml:space="preserve">
     <value>Robot movement execution timed out, Position: X={0},Y={1},Z={2},U={3}</value>
   </data>
   <data name="执行XYZU平台阻塞式等待运动完成时出错" xml:space="preserve">
     <value>Error occurred when executing XYZU platform and waiting for motion completion in blocking mode!</value>
   </data>
+  <data name="后台Modbustcp读取0" xml:space="preserve">
+    <value>Background Modbus TCP read {0}</value>
+  </data>
   <data name="获取输入位状态时出错" xml:space="preserve">
     <value>Error occurred while acquiring input bit status</value>
   </data>
@@ -3945,12 +3948,12 @@
   <data name="扭力测量仪值" xml:space="preserve">
     <value>Torque meter reading</value>
   </data>
-  <data name="差值" xml:space="preserve">
-    <value>Difference</value>
-  </data>
   <data name="电批测量值" xml:space="preserve">
     <value>Electric screwdriver torque reading</value>
   </data>
+  <data name="差值" xml:space="preserve">
+    <value>Difference</value>
+  </data>
   <data name="批头吸嘴计数清零" xml:space="preserve">
     <value>Bit/Nozzle Counter Reset</value>
   </data>
@@ -3963,10 +3966,13 @@
   <data name="设置吸嘴报警值" xml:space="preserve">
     <value>Set Nozzle Alarm Value</value>
   </data>
-  <data name="为PLC写入系统参数和订阅变量时出错" xml:space="preserve">
-    <value>An error occurred while writing system parameters to the PLC and subscribing to variables.</value>
-  </data>
   <data name="二维码拍照点位" xml:space="preserve">
     <value>QR Code Shooting Position</value>
   </data>
+  <data name="延迟保存图片" xml:space="preserve">
+    <value>Delay saving the image</value>
+  </data>
+  <data name="是否延迟保存图片" xml:space="preserve">
+    <value>Do you want to delay saving the picture</value>
+  </data>
 </root>

+ 15 - 9
TeamAAS-VM/Resources/Languages/Lang.ja-JP.resx

@@ -2514,6 +2514,9 @@
   <data name="AfagFeeder写入参数时出错" xml:space="preserve">
     <value>Afag Feeder にパラメータを書き込む際にエラーが発生しました!</value>
   </data>
+  <data name="为PLC写入系统参数和订阅变量时出错" xml:space="preserve">
+    <value>PLC にシステムパラメータを書き込み、変数を購読する際にエラーが発生しました。</value>
+  </data>
   <data name="选中Feeder序列时出错" xml:space="preserve">
     <value>Feeder シーケンス選択時にエラーが発生しました!</value>
   </data>
@@ -2634,18 +2637,18 @@
   <data name="轴停滞超时整体判定目标位置X0Y1Z2U3超时阈值ms4" xml:space="preserve">
     <value>軸停止タイムアウト(全体判定)、目標位置:X={0},Y={1},Z={2},U={3}、タイムアウト閾値 (ms)={4}</value>
   </data>
-  <data name="后台Modbustcp读取0" xml:space="preserve">
-    <value>バックグラウンド Modbus TCP 読み取り {0}</value>
-  </data>
-  <data name="获取输入位状态时出错" xml:space="preserve">
-    <value>入力ビットステータス取得時エラー発生</value>
-  </data>
   <data name="机器人执行移动超时位置X0Y1Z2U3" xml:space="preserve">
     <value>ロボット移動実行タイムアウト、位置:X={0},Y={1},Z={2},U={3}</value>
   </data>
   <data name="执行XYZU平台阻塞式等待运动完成时出错" xml:space="preserve">
     <value>XYZU プラットフォーム実行中、ブロッキングモードで動作完了待機時にエラー発生しました!</value>
   </data>
+  <data name="后台Modbustcp读取0" xml:space="preserve">
+    <value>バックグラウンド Modbus TCP 読み取り {0}</value>
+  </data>
+  <data name="获取输入位状态时出错" xml:space="preserve">
+    <value>入力ビットステータス取得時エラー発生</value>
+  </data>
   <data name="获取输出位状态时出错" xml:space="preserve">
     <value>出力ビットステータス取得時エラー発生</value>
   </data>
@@ -3963,10 +3966,13 @@
   <data name="设置吸嘴报警值" xml:space="preserve">
     <value>ノズルのアラーム値設定</value>
   </data>
-  <data name="为PLC写入系统参数和订阅变量时出错" xml:space="preserve">
-    <value>PLC にシステムパラメータを書き込み、変数を購読する際にエラーが発生しました。</value>
-  </data>
   <data name="二维码拍照点位" xml:space="preserve">
     <value>QR コード撮影ポジション</value>
   </data>
+  <data name="延迟保存图片" xml:space="preserve">
+    <value>画像の保存を遅延</value>
+  </data>
+  <data name="是否延迟保存图片" xml:space="preserve">
+    <value>画像の保存を遅延しますか?</value>
+  </data>
 </root>

+ 8 - 0
TeamAAS-VM/Resources/Languages/Lang.resx

@@ -4080,4 +4080,12 @@
   <data name="二维码拍照点位参数" xml:space="preserve">
     <value />
   </data>
+  <data name="延迟保存图片" xml:space="preserve">
+    <value>延迟保存图片</value>
+    <comment>延迟保存图片</comment>
+  </data>
+  <data name="是否延迟保存图片" xml:space="preserve">
+    <value>是否延迟保存图片</value>
+    <comment>是否延迟保存图片</comment>
+  </data>
 </root>

+ 12 - 6
TeamAAS-VM/Resources/Languages/Lang.zh-TW.resx

@@ -2514,6 +2514,9 @@
   <data name="AfagFeeder写入参数时出错" xml:space="preserve">
     <value>Afag Feeder 寫入參數時發生錯誤!</value>
   </data>
+  <data name="为PLC写入系统参数和订阅变量时出错" xml:space="preserve">
+    <value>寫入系統參數至 PLC 及訂閱變數時發生錯誤。</value>
+  </data>
   <data name="选中Feeder序列时出错" xml:space="preserve">
     <value>選取 Feeder 序列時發生錯誤!</value>
   </data>
@@ -2634,15 +2637,15 @@
   <data name="轴停滞超时整体判定目标位置X0Y1Z2U3超时阈值ms4" xml:space="preserve">
     <value>軸停滯逾時(整體判定),目標位置:X={0},Y={1},Z={2},U={3},逾時閾值 (ms)={4}</value>
   </data>
-  <data name="后台Modbustcp读取0" xml:space="preserve">
-    <value>後台 Modbus tcp 讀取 {0}</value>
-  </data>
   <data name="机器人执行移动超时位置X0Y1Z2U3" xml:space="preserve">
     <value>機器人執行移動逾時,位置:X={0},Y={1},Z={2},U={3}</value>
   </data>
   <data name="执行XYZU平台阻塞式等待运动完成时出错" xml:space="preserve">
     <value>執行 XYZU 平台,阻塞式等待運動完成時發生錯誤!</value>
   </data>
+  <data name="后台Modbustcp读取0" xml:space="preserve">
+    <value>後台 Modbus tcp 讀取 {0}</value>
+  </data>
   <data name="获取输入位状态时出错" xml:space="preserve">
     <value>取得輸入位狀態時發生錯誤</value>
   </data>
@@ -3963,10 +3966,13 @@
   <data name="设置吸嘴报警值" xml:space="preserve">
     <value>設定吸嘴警報值</value>
   </data>
-  <data name="为PLC写入系统参数和订阅变量时出错" xml:space="preserve">
-    <value>寫入系統參數至 PLC 及訂閱變數時發生錯誤。</value>
-  </data>
   <data name="二维码拍照点位" xml:space="preserve">
     <value>QRCODE 拍攝定位點</value>
   </data>
+  <data name="延迟保存图片" xml:space="preserve">
+    <value>延遲保存圖片</value>
+  </data>
+  <data name="是否延迟保存图片" xml:space="preserve">
+    <value>是否延遲保存圖片</value>
+  </data>
 </root>

+ 6 - 0
TeamAAS-VM/Services/RemoteCommandService.cs

@@ -1551,6 +1551,7 @@ namespace TeamAAS_VP.Services
                         SaveImagePathModel = procedure.SaveImagePathModel,
                         SavePath = procedure.SavePath,
                         IsCompress = procedure.IsCompress,
+                        IsDelaySaveImage = procedure.IsDelaySaveImage,
                         ImgPara = imgPara,
                         ImageFileName = imgName,
                         ProceductName = procedure.Name,
@@ -1592,6 +1593,8 @@ namespace TeamAAS_VP.Services
                         SaveImageModel = procedure.SaveImageModel,
                         SaveImagePathModel = procedure.SaveImagePathModel,
                         SavePath = procedure.SavePath,
+                        IsCompress = procedure.IsCompress,
+                        IsDelaySaveImage = procedure.IsDelaySaveImage,
                         ProceductName = procedure.Name,
                         ImageFileName = imgName
                     });
@@ -1689,6 +1692,7 @@ namespace TeamAAS_VP.Services
                         SaveImagePathModel = procedure.SaveImagePathModel,
                         SavePath = procedure.SavePath,
                         IsCompress = procedure.IsCompress,
+                        IsDelaySaveImage = procedure.IsDelaySaveImage,
                         ProceductName = procedure.Name,
                         ImageFileName = imgName,
                     });
@@ -1729,6 +1733,8 @@ namespace TeamAAS_VP.Services
                         SaveImageModel = procedure.SaveImageModel,
                         SaveImagePathModel = procedure.SaveImagePathModel,
                         SavePath = procedure.SavePath,
+                        IsCompress = procedure.IsCompress,
+                        IsDelaySaveImage = procedure.IsDelaySaveImage,
                         ProceductName = procedure.Name,
                         ImageFileName = imgName
                     });

+ 2 - 0
TeamAAS-VM/TeamAAS-VP.csproj

@@ -540,6 +540,7 @@
     <Compile Include="Core\StabilityAnalyzer.cs" />
     <Compile Include="Core\TxtFile.cs" />
     <Compile Include="Core\VisionProFileHelper.cs" />
+    <Compile Include="Events\DelaySaveImageNotification.cs" />
     <Compile Include="Events\MainTabSwitchNotification.cs" />
     <Compile Include="Events\WorkShiftChangedNotification.cs" />
     <Compile Include="Events\WorkShiftUpdateNotification.cs" />
@@ -547,6 +548,7 @@
     <Compile Include="Models\ConfigFtp.cs" />
     <Compile Include="Models\CTQ_Data.cs" />
     <Compile Include="Models\DataFormCtq.cs" />
+    <Compile Include="Models\DelaySaveImage.cs" />
     <Compile Include="Models\DeviceInfo.cs" />
     <Compile Include="Models\Feeder\ScrewFeederInfo.cs" />
     <Compile Include="Models\HeadChangeRecord.cs" />

+ 75 - 13
TeamAAS-VM/Views/Home/ShowVisionRender.xaml.cs

@@ -36,6 +36,8 @@ namespace TeamAAS_VP.Views.Home
         private readonly ISystemDatabaseService _systemDatabaseService;
         private readonly IProductService _productService;
 
+        private List<DelaySaveImage> delaySaveImages = new List<DelaySaveImage>();
+
         // ======================= 【新增】保存任务控制:防闪退/防资源泄露 =======================
         private CancellationTokenSource _saveImageCts = new CancellationTokenSource();
         private readonly SemaphoreSlim _saveSemaphore = new SemaphoreSlim(2, 2);
@@ -56,6 +58,7 @@ namespace TeamAAS_VP.Views.Home
             viewModel.UpdateLayout += ViewModel_UpdateLayout;
             _eventAggregator.GetEvent<RenderUpdateNotification>().Subscribe(UpdateRenderModuleSource);
             _eventAggregator.GetEvent<ProductChangedNotification>().Subscribe(ProductChanged);
+            _eventAggregator.GetEvent<DelaySaveImageNotification>().Subscribe(UpdateDelaySaveImage);
 
             // 【可选】控件卸载时取消所有保存任务,避免页面关闭后仍在截图/保存导致闪退
             this.Unloaded += ShowVisionRender_Unloaded;
@@ -308,16 +311,35 @@ namespace TeamAAS_VP.Views.Home
                         {
                             token.ThrowIfCancellationRequested();
 
-                            SaveImage(
-                                render.SaveImageModel,
-                                render.SaveImagePathModel,
-                                render.SavePath,
-                                render.Image,
-                                clonedBitmap,
-                                render.Result,
-                                render.IsCompress,
-                                render.ImageFileName
-                            );
+                            if (render.IsDelaySaveImage)// 延迟保存(适合你那边后续有处理的场景)
+                            {
+                                // 创建延迟保存项,转移资源所有权
+                                var delayItem = new DelaySaveImage()
+                                {
+                                    SaveImageModel = render.SaveImageModel,
+                                    SaveImagePathModel = render.SaveImagePathModel,
+                                    SavePath = render.SavePath,
+                                    Image = render.Image?.CopyBase(CogImageCopyModeConstants.CopyPixels), // 深拷贝ICogImage
+                                    clonedBitmap = (Bitmap)clonedBitmap.Clone(),      // 转移所有权
+                                    Result = render.Result,
+                                    IsCompress = render.IsCompress,
+                                    ImageFileName = render.ImageFileName
+                                };
+                                delaySaveImages.Add(delayItem);
+                            }
+                            else
+                            {
+                                SaveImage(
+                                    render.SaveImageModel,
+                                    render.SaveImagePathModel,
+                                    render.SavePath,
+                                    render.Image,
+                                    clonedBitmap,
+                                    render.Result,
+                                    render.IsCompress,
+                                    render.ImageFileName
+                                );
+                            }
                         }
                         catch (OperationCanceledException)
                         {
@@ -347,6 +369,43 @@ namespace TeamAAS_VP.Views.Home
             }));
         }
 
+        private void UpdateDelaySaveImage(bool isSave)
+        {
+            if (!isSave)
+            {
+                delaySaveImages.Clear();
+                return;
+            }
+
+            // 这里简单遍历保存,实际可以根据需要优化(如批量处理/分队列等)
+            foreach (var item in delaySaveImages)
+            {
+                try
+                {
+                    SaveImage(
+                        item.SaveImageModel,
+                        item.SaveImagePathModel,
+                        item.SavePath,
+                        item.Image,
+                        item.clonedBitmap,
+                        item.Result,
+                        item.IsCompress,
+                        item.ImageFileName
+                    );
+                }
+                catch (Exception ex)
+                {
+                    LogHelper.WriteLogError("UpdateDelaySaveImage 保存图像时出错!", ex);
+                }
+                finally
+                {
+                    // 保存后���放资源
+                    try { item.clonedBitmap?.Dispose(); } catch { }
+                }
+            }
+            delaySaveImages.Clear();
+        }
+
         /// <summary>
         /// 保存图像(保持你原有逻辑不变,补充 Dispose 兜底,防止压缩分支不释放导致闪退)
         /// </summary>
@@ -404,8 +463,11 @@ namespace TeamAAS_VP.Views.Home
 
                             // 产品SN(过滤非法字符)
                             string productSN = _productService.CurrentProductCode;
-                            foreach (char c in Path.GetInvalidFileNameChars())
-                                productSN = productSN.Replace(c, '_');
+                            if (!string.IsNullOrEmpty(productSN))
+                            {
+                                foreach (char c in Path.GetInvalidFileNameChars())
+                                    productSN = productSN.Replace(c, '_');
+                            }
 
                             string resultStr = result ? "OK" : "NG";
                             Originalpath = $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\{formulaName}\\{productSN}\\{resultStr}\\{imgName}.bmp";
@@ -524,7 +586,7 @@ namespace TeamAAS_VP.Views.Home
                             {
                                 foreach (char c in Path.GetInvalidFileNameChars())
                                     productSN = productSN.Replace(c, '_');
-                            }                          
+                            }
 
                             string resultStr = result ? "OK" : "NG";
                             Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\{formulaName}\\{productSN}\\{resultStr}\\{imgName}.bmp";

+ 17 - 6
TeamAAS-VM/Views/Product/ProcedureParams.xaml

@@ -431,19 +431,30 @@
                               Visibility="{Binding ElementName=chbSaveImage,Path=IsChecked, Converter={StaticResource BooleanToVisibilityConverter}}"
                               IsChecked="{Binding SelectProcedure.IsCompress,Mode=TwoWay}"
                               Style="{StaticResource MaterialDesignLightCheckBox}" />
-                    <!--图片名称格式-->
+                    <!--是否延迟保存图片-->
                     <TextBlock Grid.Row="14"
+                               Grid.Column="0"
+                               Visibility="{Binding ElementName=chbSaveImage,Path=IsChecked, Converter={StaticResource BooleanToVisibilityConverter}}"
+                               Text="{lex:Loc 延迟保存图片,Converter={StaticResource StringFormatConverter},ConverterParameter='{}{0}: '}" />
+                    <CheckBox Grid.Row="14"
+                              Grid.Column="1"
+                              Content="{lex:Loc 是否延迟保存图片}"
+                              Visibility="{Binding ElementName=chbSaveImage,Path=IsChecked, Converter={StaticResource BooleanToVisibilityConverter}}"
+                              IsChecked="{Binding SelectProcedure.IsDelaySaveImage,Mode=TwoWay}"
+                              Style="{StaticResource MaterialDesignLightCheckBox}" />
+                    <!--图片名称格式-->
+                    <TextBlock Grid.Row="15"
                                Grid.Column="0"
                                Visibility="{Binding ElementName=chbSaveImage,Path=IsChecked, Converter={StaticResource BooleanToVisibilityConverter}}"
                                Text="图片名称格式:" />
-                    <TextBox Grid.Row="14"
+                    <TextBox Grid.Row="15"
                              Grid.Column="1"
                              HorizontalAlignment="Stretch"
                              Visibility="{Binding ElementName=chbSaveImage,Path=IsChecked, Converter={StaticResource BooleanToVisibilityConverter}}"
                              Text="{Binding SelectProcedure.ImageFileNameFormat,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
                     </TextBox>
                     <!--示例-->
-                    <StackPanel Grid.Row="15"
+                    <StackPanel Grid.Row="16"
                                 Grid.Column="0"
                                 Grid.ColumnSpan="2"
                                 Orientation="Vertical"
@@ -466,7 +477,7 @@
                     </StackPanel>
 
                     <!--自定义参数-->
-                    <GroupBox Grid.Row="16"
+                    <GroupBox Grid.Row="17"
                               Grid.Column="0"
                               Grid.ColumnSpan="2"
                               HorizontalAlignment="Stretch"
@@ -536,10 +547,10 @@
                             </DataGrid>
                         </DockPanel>
                     </GroupBox>
-                    <TextBlock Grid.Row="17"
+                    <TextBlock Grid.Row="18"
                                Grid.Column="0"
                                Text="{lex:Loc 重写视觉处理逻辑}" />
-                    <Button Grid.Row="17"
+                    <Button Grid.Row="18"
                             Grid.Column="1"
                             Margin="0,2,0,0"
                             Command="{Binding OverrideVisionTaskCommand}">