Переглянути джерело

优化log,产品切换,NG数据单独保存csv

zly 1 місяць тому
батько
коміт
dca47852e4

+ 7 - 29
TeamAAS-VM/Core/Cameras/OptCamera.cs

@@ -97,7 +97,7 @@ namespace TeamAAS_VP.Core.Cameras
         #endregion
 
         #region 事件
-        public event Action<ICogImage, TimeSpan, string,Guid> ImageCallbackEvent;
+        public event Action<ICogImage, TimeSpan, string, Guid> ImageCallbackEvent;
         public event Action<Guid, bool> CameraConnectChangedEvent;
         #endregion
 
@@ -124,7 +124,7 @@ namespace TeamAAS_VP.Core.Cameras
 
                 if (devTlType == SciCam.SciCamTLType.SciCam_TLType_Gige)
                 {
-                    
+
                     SciCam.SCI_DEVICE_GIGE_INFO gigeDevInfo = (SciCam.SCI_DEVICE_GIGE_INFO)SciCam.ByteToStruct(device.info.gigeInfo, typeof(SciCam.SCI_DEVICE_GIGE_INFO));
                     uint ip1 = gigeDevInfo.ip;
                     int nIp1 = (int)(ip1 & 0x000000ff);
@@ -340,7 +340,7 @@ namespace TeamAAS_VP.Core.Cameras
                         throw new Exception("没有发现相机");
                     }
                 }
-                SciCam.SCI_DEVICE_INFO sCI_DEVICE_INFO= CameraInfo;
+                SciCam.SCI_DEVICE_INFO sCI_DEVICE_INFO = CameraInfo;
                 nReVal = mvCameraAcq.CreateDevice(ref sCI_DEVICE_INFO);
                 if (nReVal != SciCam.SCI_CAMERA_OK)
                 {
@@ -390,13 +390,13 @@ namespace TeamAAS_VP.Core.Cameras
             //_operationSemaphore.Wait();
             try
             {
-                if (mvCameraAcq!=null)
+                if (mvCameraAcq != null)
                 {
                     mvCameraAcq.StopGrabbing();
                     mvCameraAcq.CloseDevice();
                 }
-                
-                
+
+
             }
             finally
             {
@@ -419,30 +419,14 @@ namespace TeamAAS_VP.Core.Cameras
                 {
                     try
                     {
-                        var clearWatch = Stopwatch.StartNew();
                         mvCameraAcq.ClearPayloadBuffer();
-                        clearWatch.Stop();
-
-                        var triggerWatch = Stopwatch.StartNew();
                         nReVal = mvCameraAcq.SetCommandValueEx(SciCam.SciCamDeviceXmlType.SciCam_DeviceXml_Camera, "TriggerSoftware");
-                        triggerWatch.Stop();
-
-                        var grabWatch = Stopwatch.StartNew();
                         nReVal = mvCameraAcq.Grab(ref payload);
-                        grabWatch.Stop();
-
                         if (nReVal == SciCam.SCI_CAMERA_OK)
                         {
-                            var convertWatch = Stopwatch.StartNew();
                             Image = GetConvertedInfo(payload);
-                            convertWatch.Stop();
-
-                            var freeWatch = Stopwatch.StartNew();
                             mvCameraAcq.FreePayload(payload);
-                            freeWatch.Stop();
-
                             TotalTime = sw.Elapsed;
-                            LogHelper.WriteLogInfo($"[PLC-TRACE] OptCamera Grab done, name={Name}, attempt={i + 1}, clearMs={clearWatch.ElapsedMilliseconds}, triggerMs={triggerWatch.ElapsedMilliseconds}, grabMs={grabWatch.ElapsedMilliseconds}, convertMs={convertWatch.ElapsedMilliseconds}, freeMs={freeWatch.ElapsedMilliseconds}, totalMs={sw.ElapsedMilliseconds}");
                             return Image;
                         }
                         //else
@@ -585,7 +569,6 @@ namespace TeamAAS_VP.Core.Cameras
         /// <returns></returns>
         public bool SetExposureTime(float ExposureTime)
         {
-            var sw = Stopwatch.StartNew();
             try
             {
                 if (!mvCameraAcq.IsDeviceOpen())
@@ -596,8 +579,6 @@ namespace TeamAAS_VP.Core.Cameras
                 {
                     mvCameraAcq.SetFloatValue("ExposureTime", ExposureTime);
                 }
-                sw.Stop();
-                LogHelper.WriteLogInfo($"[PLC-TRACE] OptCamera SetExposureTime done, name={Name}, value={ExposureTime}, elapsedMs={sw.ElapsedMilliseconds}");
                 return true;
             }
             finally
@@ -652,7 +633,6 @@ namespace TeamAAS_VP.Core.Cameras
         /// <returns></returns>
         public bool SetGain(float Gain)
         {
-            var sw = Stopwatch.StartNew();
             try
             {
                 if (!mvCameraAcq.IsDeviceOpen())
@@ -663,8 +643,6 @@ namespace TeamAAS_VP.Core.Cameras
                 {
                     mvCameraAcq.SetFloatValue("Gain", Gain);
                 }
-                sw.Stop();
-                LogHelper.WriteLogInfo($"[PLC-TRACE] OptCamera SetGain done, name={Name}, value={Gain}, elapsedMs={sw.ElapsedMilliseconds}");
                 return true;
             }
             finally
@@ -718,7 +696,7 @@ namespace TeamAAS_VP.Core.Cameras
                 try
                 {
                     Grab();
-                    ImageCallbackEvent?.Invoke(Image, TotalTime, ErrorMessage,ID);
+                    ImageCallbackEvent?.Invoke(Image, TotalTime, ErrorMessage, ID);
                 }
                 catch (Exception)
                 {

+ 277 - 126
TeamAAS-VM/Core/Management.cs

@@ -12,6 +12,7 @@ using Prism.Events;
 using Prism.Ioc;
 using Prism.Mvvm;
 using Prism.Regions;
+using Prism.Services.Dialogs;
 using SqlSugar;
 using SqlSugar.SplitTableExtensions;
 using System;
@@ -23,12 +24,14 @@ using System.Drawing;
 using System.IO;
 using System.IO.Compression;
 using System.IO.MemoryMappedFiles;
+using System.IO.MemoryMappedFiles;
 using System.Linq;
 using System.Text;
 using System.Text.Json;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Web.UI.WebControls;
+using System.Windows;
 using System.Windows.Media;
 using Team.FFFeederService.Interfaces;
 using TeamAAS_VP.Controls;
@@ -44,7 +47,6 @@ using TeamAAS_VP.ViewModels.Home;
 using TouchSocket.Core;
 using TouchSocket.SerialPorts;
 using TouchSocket.Sockets;
-using System.IO.MemoryMappedFiles;
 
 namespace TeamAAS_VP.Core
 {
@@ -66,6 +68,7 @@ namespace TeamAAS_VP.Core
         IRemoteCommandService _remoteCommandService;
         ISystemDatabaseService _systemDatabaseService;
         IMesService _mesService;
+        IDialogService _dialogService;
 
         Timer yieldtimer;
         /// <summary>
@@ -292,7 +295,7 @@ namespace TeamAAS_VP.Core
 
         #endregion
 
-        public Management(IRegionManager regionManager, IEventAggregator ea, IContainerProvider container, IConfigService configService,
+        public Management(IRegionManager regionManager, IEventAggregator ea, IContainerProvider container, IConfigService configService, IDialogService dialogService,
             IRobotService robotService, ICameraService cameraService, IFeederService feederService, IPlcService plcService, IProductService productService,
             ICalibrationService calibrationService, IRemoteCommandService remoteCommandService, ISystemDatabaseService systemDatabaseService, ILightManagerService lightManagerService, IMesService mesService, IScannerService scannerService)
         {
@@ -306,6 +309,7 @@ namespace TeamAAS_VP.Core
             _cameraService = cameraService;
             _feederService = feederService;
             _plcService = plcService;
+            _dialogService = dialogService;
             _calibrationService = calibrationService;
             yieldtimer = new Timer(DoYieldTime, null, 10000, 1000);
             _messageFlushTimer = new Timer(_ => FlushMessageQueue(), null, 200, 200);
@@ -542,8 +546,8 @@ namespace TeamAAS_VP.Core
         {
 
             var camera = _configService.GetCamera(cameraId);
-            SendTaskMessage($"相机[{camera.CameraName}]图像接收完成!", MessageLevel.Info);
-
+            //SendTaskMessage($"相机[{camera.CameraName}]图像接收完成!", MessageLevel.Info);
+            LogHelper.WriteLogInfo($"相机[{camera.CameraName}]图像接收完成!");
             //if (camera.CameraNo == 1)
             //{
             //    ImageData[1] = 1;
@@ -1526,7 +1530,7 @@ namespace TeamAAS_VP.Core
         private void PlcCommandTrigger((string key, string nodeId, object value) tuple)
         {
             // 立即释放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}");
+            //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(async () =>
             {
                 try
@@ -1578,13 +1582,12 @@ namespace TeamAAS_VP.Core
 
                         if (state)
                         {
-                            SendTaskMessage($"写入3D相机长度...", MessageLevel.Debug);
                             //相机的编号
                             //Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_FixedCameracheckNum.Address);
                             // 读取相机编号
 
                             Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_FixedCameracheckNum.Address);
-                            SendTaskMessage($"编号:{cameraIndex}", MessageLevel.Info);
+                            SendTaskMessage($"写入3D相机长度...编号:{cameraIndex}", MessageLevel.Debug);
 
                             // 找到对应编号的 PlcPoint
                             var targetPoint = currentProduct.AoiPoints?.FirstOrDefault(p => p.Number == cameraIndex);
@@ -1653,10 +1656,9 @@ namespace TeamAAS_VP.Core
                         {
                             LockResult lockResult = new LockResult();
                             lockResult.TimeStart = DateTime.Now;
-                            SendTaskMessage($"触发固定相机检测拍照...", MessageLevel.Debug);
                             // 读取相机编号
                             Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_FixedCameracheckNum.Address);
-                            SendTaskMessage($"编号:{cameraIndex}", MessageLevel.Info);
+                            SendTaskMessage($"触发固定相机检测拍照...编号:{cameraIndex}", MessageLevel.Debug);
 
                             // 找到对应编号的 PlcPoint
                             var targetPoint = currentProduct.AoiPoints?.FirstOrDefault(p => p.Number == cameraIndex);
@@ -1689,14 +1691,10 @@ namespace TeamAAS_VP.Core
                             }
                             catch { }
                             var sysConfig = _configService.GetSystemConfiguration();
-                            string fixedCameraTraceId = $"FC-{cameraIndex}-{DateTime.Now:HHmmss.fff}";
-                            LogHelper.WriteLogInfo($"[PLC-TRACE] {fixedCameraTraceId} fixed camera trigger start, node={tuple.nodeId}, value={tuple.value}, procCount={procIds.Count}, PhotosUse={sysConfig.PhotosUse}, cameraIndex={cameraIndex}, PhotoCountStop={currentProduct.PhotoCountStop}, thread={Thread.CurrentThread.ManagedThreadId}");
-
+                          
                             #region 扫码枪扫码
                             if (sysConfig.ScannerUse == true && cameraIndex == 1 && sysConfig.CodeUse2 == true)
                             {
-                                FinalResultStatus = "检测中";
-                                FinalResultText = "检测中";
                                 ScanValue = "";
                                 _scanDoneEvent.Reset(); // 重要:先关门
 
@@ -1779,6 +1777,8 @@ namespace TeamAAS_VP.Core
                                             kb_pn[0] = kbpnsnitem.Replace("KB_PN=", "").Trim();
                                             SendTaskMessage($"KB_PN:{kb_pn[0]}", MessageLevel.Info);
                                         }
+                                        FinalResultStatus = "检测中";
+                                        FinalResultText = "检测中";
                                         var item1 = await _systemDatabaseService.QueryProductWithMesALLAsync();
                                         foreach (var q in item1)
                                         {
@@ -1800,10 +1800,20 @@ namespace TeamAAS_VP.Core
                                                     if (currentProduct.ID != productId && q.ProductLoad.Contains(kb_pn[0]))
                                                     {
                                                         //切换产品
-                                                        SendTaskMessage($"开始切换产品{q.ProductKBSN}", MessageLevel.Error);
-                                                        _productService.LoadProduct(productId);
+                                                        SendTaskMessage($"开始切换产品{q.ProductName}", MessageLevel.Error);
+                                                        var _ = Application.Current.Dispatcher.BeginInvoke(new Action(() => { _dialogService.ShowDialog("UProgressBar", rst => { }); }));
+                                                        await Task.Delay(50);
+                                                        _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                                                        {
+                                                            _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"切换{q.ProductName}产品中...", Value = 0 });
+                                                        }));
+                                                        await _productService.LoadProductAsync(productId);
                                                         _productService.SetCurrentProduct(productId);
                                                         await WritePositionToPlcAsync();
+                                                        _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                                                        {
+                                                            _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"{Lang.完成}", Value = -1 });
+                                                        }));
                                                         SendTaskMessage($"切换产品成功", MessageLevel.Debug);
                                                         break;
                                                     }
@@ -1814,9 +1824,19 @@ namespace TeamAAS_VP.Core
                                                     {
                                                         //切换产品
                                                         SendTaskMessage($"开始切换产品{q.ProductCode}", MessageLevel.Error);
-                                                        _productService.LoadProduct(productId);
+                                                        var _ = Application.Current.Dispatcher.BeginInvoke(new Action(() => { _dialogService.ShowDialog("UProgressBar", rst => { }); }));
+                                                        await Task.Delay(50);
+                                                        _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                                                        {
+                                                            _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"切换{q.ProductName}产品中...", Value = 0 });
+                                                        }));
+                                                        await _productService.LoadProductAsync(productId);
                                                         _productService.SetCurrentProduct(productId);
                                                         await WritePositionToPlcAsync();
+                                                        _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                                                        {
+                                                            _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"{Lang.完成}", Value = -1 });
+                                                        }));
                                                         SendTaskMessage($"切换产品成功", MessageLevel.Debug);
                                                         break;
                                                     }
@@ -1846,8 +1866,6 @@ namespace TeamAAS_VP.Core
 
                             if (sysConfig.ScannerUse == true && cameraIndex == 1 && sysConfig.CodeUse == true)
                             {
-                                FinalResultStatus = "检测中";
-                                FinalResultText = "检测中";
                                 ScanValue = "";
                                 _scanDoneEvent.Reset(); // 重要:先关门
 
@@ -1954,7 +1972,8 @@ namespace TeamAAS_VP.Core
                                                 kb_pn[0] = kbpnsnitem.Replace("KB_PN=", "").Trim();
                                                 SendTaskMessage($"KB_PN:{kb_pn[0]}", MessageLevel.Info);
                                             }
-
+                                            FinalResultStatus = "检测中";
+                                            FinalResultText = "检测中";
                                             var item1 = await _systemDatabaseService.QueryProductWithMesALLAsync();
                                             foreach (var q in item1)
                                             {
@@ -1976,10 +1995,20 @@ namespace TeamAAS_VP.Core
                                                         if (currentProduct.ID != productId && q.ProductLoad.Contains(kb_pn[0]))
                                                         {
                                                             //切换产品
-                                                            SendTaskMessage($"开始切换产品{q.ProductKBSN}", MessageLevel.Error);
-                                                            _productService.LoadProduct(productId);
+                                                            SendTaskMessage($"开始切换产品{q.ProductName}", MessageLevel.Error);
+                                                            var _ = Application.Current.Dispatcher.BeginInvoke(new Action(() => { _dialogService.ShowDialog("UProgressBar", rst => { }); }));
+                                                            await Task.Delay(50);
+                                                            _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                                                            {
+                                                                _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"切换{q.ProductName}产品中...", Value = 0 });
+                                                            }));
+                                                            await _productService.LoadProductAsync(productId);
                                                             _productService.SetCurrentProduct(productId);
                                                             await WritePositionToPlcAsync();
+                                                            _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                                                            {
+                                                                _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"{Lang.完成}", Value = -1 });
+                                                            }));
                                                             SendTaskMessage($"切换产品成功", MessageLevel.Debug);
                                                             break;
                                                         }
@@ -1990,9 +2019,19 @@ namespace TeamAAS_VP.Core
                                                         {
                                                             //切换产品
                                                             SendTaskMessage($"开始切换产品{q.ProductCode}", MessageLevel.Error);
-                                                            _productService.LoadProduct(productId);
+                                                            var _ = Application.Current.Dispatcher.BeginInvoke(new Action(() => { _dialogService.ShowDialog("UProgressBar", rst => { }); }));
+                                                            await Task.Delay(50);
+                                                            _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                                                            {
+                                                                _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"切换{q.ProductName}产品中...", Value = 0 });
+                                                            }));
+                                                            await _productService.LoadProductAsync(productId);
                                                             _productService.SetCurrentProduct(productId);
                                                             await WritePositionToPlcAsync();
+                                                            _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                                                            {
+                                                                _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"{Lang.完成}", Value = -1 });
+                                                            }));
                                                             SendTaskMessage($"切换产品成功", MessageLevel.Debug);
                                                             break;
                                                         }
@@ -2085,7 +2124,8 @@ namespace TeamAAS_VP.Core
                                 }
                                 count = AllProcId.Count + isscan;
                                 countdown = new CountdownEvent(count);
-                                SendTaskMessage($"需要执行模板数:{count}", MessageLevel.Alarm);
+                                //SendTaskMessage($"需要执行模板数:{count}", MessageLevel.Alarm);
+                                LogHelper.WriteLogInfo($"需要执行模板数:{count}");
                                 _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
                                 {
                                     _eventAggregator.GetEvent<LockResetNotification>().Publish();
@@ -2095,8 +2135,6 @@ namespace TeamAAS_VP.Core
                             #region 相机扫码
                             if (sysConfig.CodeUse == true && cameraIndex == 1)
                             {
-                                FinalResultStatus = "检测中";
-                                FinalResultText = "检测中";
                                 var cts = new CancellationTokenSource();
                                 // CameraProcedureId 存储的是具体的视觉流程 Id(ProcedureModel.Id),
                                 // 需要在所有 CameraProcedures 的 ProcedureModels 中查找匹配的流程。
@@ -2230,6 +2268,8 @@ namespace TeamAAS_VP.Core
                                                     kb_pn[0] = kbpnsnitem.Replace("KB_PN=", "").Trim();
                                                     SendTaskMessage($"KB_PN:{kb_pn[0]}", MessageLevel.Info);
                                                 }
+                                                FinalResultStatus = "检测中";
+                                                FinalResultText = "检测中";
                                                 var item1 = await _systemDatabaseService.QueryProductWithMesALLAsync();
                                                 foreach (var q in item1)
                                                 {
@@ -2251,10 +2291,20 @@ namespace TeamAAS_VP.Core
                                                             if (currentProduct.ID != productId && q.ProductLoad.Contains(kb_pn[0]))
                                                             {
                                                                 //切换产品
-                                                                SendTaskMessage($"开始切换产品{q.ProductKBSN}", MessageLevel.Error);
-                                                                _productService.LoadProduct(productId);
+                                                                SendTaskMessage($"开始切换产品{q.ProductName}", MessageLevel.Error);
+                                                                var _ = Application.Current.Dispatcher.BeginInvoke(new Action(() => { _dialogService.ShowDialog("UProgressBar", rst => { }); }));
+                                                                await Task.Delay(50);
+                                                                _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                                                                {
+                                                                    _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"切换{q.ProductName}产品中...", Value = 0 });
+                                                                }));
+                                                                await _productService.LoadProductAsync(productId);
                                                                 _productService.SetCurrentProduct(productId);
                                                                 await WritePositionToPlcAsync();
+                                                                _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                                                                {
+                                                                    _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"{Lang.完成}", Value = -1 });
+                                                                }));
                                                                 SendTaskMessage($"切换产品成功", MessageLevel.Debug);
                                                                 break;
                                                             }
@@ -2265,9 +2315,19 @@ namespace TeamAAS_VP.Core
                                                             {
                                                                 //切换产品
                                                                 SendTaskMessage($"开始切换产品{q.ProductCode}", MessageLevel.Error);
-                                                                _productService.LoadProduct(productId);
+                                                                var _ = Application.Current.Dispatcher.BeginInvoke(new Action(() => { _dialogService.ShowDialog("UProgressBar", rst => { }); }));
+                                                                await Task.Delay(50);
+                                                                _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                                                                {
+                                                                    _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"切换{q.ProductName}产品中...", Value = 0 });
+                                                                }));
+                                                                await _productService.LoadProductAsync(productId);
                                                                 _productService.SetCurrentProduct(productId);
                                                                 await WritePositionToPlcAsync();
+                                                                _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                                                                {
+                                                                    _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"{Lang.完成}", Value = -1 });
+                                                                }));
                                                                 SendTaskMessage($"切换产品成功", MessageLevel.Debug);
                                                                 break;
                                                             }
@@ -2325,8 +2385,6 @@ namespace TeamAAS_VP.Core
 
                             if (sysConfig.CodeUse2 == true && cameraIndex == 1)
                             {
-                                FinalResultStatus = "检测中";
-                                FinalResultText = "检测中";
                                 var cts = new CancellationTokenSource();
                                 // CameraProcedureId 存储的是具体的视觉流程 Id(ProcedureModel.Id),
                                 // 需要在所有 CameraProcedures 的 ProcedureModels 中查找匹配的流程。
@@ -2436,6 +2494,8 @@ namespace TeamAAS_VP.Core
                                                 kb_pn[0] = kbpnsnitem.Replace("KB_PN=", "").Trim();
                                                 SendTaskMessage($"KB_PN:{kb_pn[0]}", MessageLevel.Info);
                                             }
+                                            FinalResultStatus = "检测中";
+                                            FinalResultText = "检测中";
                                             var item1 = await _systemDatabaseService.QueryProductWithMesALLAsync();
                                             foreach (var q in item1)
                                             {
@@ -2457,10 +2517,20 @@ namespace TeamAAS_VP.Core
                                                         if (currentProduct.ID != productId && q.ProductLoad.Contains(kb_pn[0]))
                                                         {
                                                             //切换产品
-                                                            SendTaskMessage($"开始切换产品{q.ProductKBSN}", MessageLevel.Error);
-                                                            _productService.LoadProduct(productId);
+                                                            SendTaskMessage($"开始切换产品{q.ProductName}", MessageLevel.Error);
+                                                            var _ = Application.Current.Dispatcher.BeginInvoke(new Action(() => { _dialogService.ShowDialog("UProgressBar", rst => { }); }));
+                                                            await Task.Delay(50);
+                                                            _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                                                            {
+                                                                _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"切换{q.ProductName}产品中...", Value = 0 });
+                                                            }));
+                                                            await _productService.LoadProductAsync(productId);
                                                             _productService.SetCurrentProduct(productId);
                                                             await WritePositionToPlcAsync();
+                                                            _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                                                            {
+                                                                _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"{Lang.完成}", Value = -1 });
+                                                            }));
                                                             SendTaskMessage($"切换产品成功", MessageLevel.Debug);
                                                             break;
                                                         }
@@ -2471,9 +2541,19 @@ namespace TeamAAS_VP.Core
                                                         {
                                                             //切换产品
                                                             SendTaskMessage($"开始切换产品{q.ProductCode}", MessageLevel.Error);
-                                                            _productService.LoadProduct(productId);
+                                                            var _ = Application.Current.Dispatcher.BeginInvoke(new Action(() => { _dialogService.ShowDialog("UProgressBar", rst => { }); }));
+                                                            await Task.Delay(50);
+                                                            _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                                                            {
+                                                                _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"切换{q.ProductName}产品中...", Value = 0 });
+                                                            }));
+                                                            await _productService.LoadProductAsync(productId);
                                                             _productService.SetCurrentProduct(productId);
                                                             await WritePositionToPlcAsync();
+                                                            _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                                                            {
+                                                                _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"{Lang.完成}", Value = -1 });
+                                                            }));
                                                             SendTaskMessage($"切换产品成功", MessageLevel.Debug);
                                                             break;
                                                         }
@@ -2518,7 +2598,6 @@ namespace TeamAAS_VP.Core
                             #endregion
 
 
-
                             //不能使用全局变量,不然并发的时候会被清除掉,所以局部变量
                             var _ImageTaskList = new List<Task<(bool IsSucceed, ICogImage[] Images, string Message, int cameraIndex, Guid procedureId)>>();
 
@@ -2548,36 +2627,23 @@ namespace TeamAAS_VP.Core
                                 if (sysConfig.PhotosUse == true)
                                 {
                                     bool Is3dPhoto = Is3D(prcId);
-                                    var grabWatch = Stopwatch.StartNew();
-                                    LogHelper.WriteLogInfo($"[PLC-TRACE] {fixedCameraTraceId} GrabImageEx start, cameraIndex={cameraIndex}, procedureId={prcId}, is3D={Is3dPhoto}, thread={Thread.CurrentThread.ManagedThreadId}");
                                     var tt = await GrabImageEx(cameraIndex, prcId, Is3dPhoto, currentProduct, sysConfig);
-                                    grabWatch.Stop();
-                                    LogHelper.WriteLogInfo($"[PLC-TRACE] {fixedCameraTraceId} GrabImageEx done, cameraIndex={cameraIndex}, procedureId={prcId}, success={tt.IsSucceed}, elapsedMs={grabWatch.ElapsedMilliseconds}, msg={tt.Message}");
+
                                     if (tt.IsSucceed == false)
                                     {
                                         SendTaskMessage($"图像采集失败!{cameraIndex}: {prcId}", MessageLevel.Alarm);
-                                        LogHelper.WriteLogInfo($"[PLC-TRACE] {fixedCameraTraceId} return before status write because GrabImageEx failed, cameraIndex={cameraIndex}, procedureId={prcId}");
                                         return;
                                     }
-                                    var executeWatch = Stopwatch.StartNew();
-                                    LogHelper.WriteLogInfo($"[PLC-TRACE] {fixedCameraTraceId} ExecuteProcedureAsync start, cameraIndex={cameraIndex}, procedureId={prcId}, imageCount={(tt.imges == null ? 0 : tt.imges.Length)}");
                                     await ExecuteProcedureAsync(cameraIndex, prcId, tt.imges, Is3dPhoto, currentProduct);
-                                    executeWatch.Stop();
-                                    LogHelper.WriteLogInfo($"[PLC-TRACE] {fixedCameraTraceId} ExecuteProcedureAsync done, cameraIndex={cameraIndex}, procedureId={prcId}, elapsedMs={executeWatch.ElapsedMilliseconds}");
                                     if (targetPoint.AiVision == 1)
                                     {
                                         var targetPoint2 = currentProduct.AoiPoints?.FirstOrDefault(p => p.Number == targetPoint.AiVision);
-                                        var aiExecuteWatch = Stopwatch.StartNew();
-                                        LogHelper.WriteLogInfo($"[PLC-TRACE] {fixedCameraTraceId} AiVision ExecuteProcedureAsync start, aiPoint={targetPoint.AiVision}, procedureId={targetPoint2?.CameraProcedureId1}");
                                         await ExecuteProcedureAsync(targetPoint.AiVision, targetPoint2.CameraProcedureId1, tt.imges, Is3dPhoto, currentProduct);
-                                        aiExecuteWatch.Stop();
-                                        LogHelper.WriteLogInfo($"[PLC-TRACE] {fixedCameraTraceId} AiVision ExecuteProcedureAsync done, aiPoint={targetPoint.AiVision}, elapsedMs={aiExecuteWatch.ElapsedMilliseconds}");
                                     }
                                 }
                                 else
                                 {
                                     bool Is3dPhoto = Is3D(prcId);
-                                    LogHelper.WriteLogInfo($"[PLC-TRACE] {fixedCameraTraceId} enqueue GrabImageEx task, cameraIndex={cameraIndex}, procedureId={prcId}, is3D={Is3dPhoto}");
                                     _ImageTaskList.Add(GrabImageEx(cameraIndex, prcId, Is3dPhoto, currentProduct, sysConfig));
                                     if (targetPoint.AiVision == 1)
                                     {
@@ -2591,27 +2657,18 @@ namespace TeamAAS_VP.Core
                                 //     await _remoteCommandService.TurnOffLightAfterPhoto(selectedProcedure);
                                 // }
                             }
-
                             if (cameraIndex != currentProduct.PhotoCountStop)
                             {
                                 //直接放行,执行下一个检测点
-                                var statusWriteWatch = Stopwatch.StartNew();
-                                LogHelper.WriteLogInfo($"[PLC-TRACE] {fixedCameraTraceId} status write start, node={addressConfig.Out_FixedCameraPickStatus.Address}, value=1, beforeImageTaskCount={_ImageTaskList.Count}, thread={Thread.CurrentThread.ManagedThreadId}");
-                                bool statusWriteResult = await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)1);
-                                statusWriteWatch.Stop();
-                                LogHelper.WriteLogInfo($"[PLC-TRACE] {fixedCameraTraceId} status write done, node={addressConfig.Out_FixedCameraPickStatus.Address}, value=1, result={statusWriteResult}, elapsedMs={statusWriteWatch.ElapsedMilliseconds}");
+                                await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)1);
                             }
-
                             if (_ImageTaskList.Count > 0)//如果2D取图有队列
                             {
                                 //并发
-                                LogHelper.WriteLogInfo($"[PLC-TRACE] {fixedCameraTraceId} image task batch start, taskCount={_ImageTaskList.Count}");
                                 _ = Task.Run(async () =>
                                 {
-                                    var imageBatchWatch = Stopwatch.StartNew();
+                                   
                                     var ImageTaskResults = await Task.WhenAll(_ImageTaskList);
-                                    imageBatchWatch.Stop();
-                                    LogHelper.WriteLogInfo($"[PLC-TRACE] {fixedCameraTraceId} image task batch done, taskCount={ImageTaskResults.Length}, elapsedMs={imageBatchWatch.ElapsedMilliseconds}");
                                     // 并发执行所有 ToolBlock(每个 ToolBlock 在独立 STA 线程中运行)
                                     var procedureTasks = new List<Task>();
                                     foreach (var item in ImageTaskResults)
@@ -2634,12 +2691,8 @@ namespace TeamAAS_VP.Core
                                         }
                                     }
                                     // 等待所有并发 ToolBlock 执行完成
-                                    var concurrentWatch = Stopwatch.StartNew();
-                                    LogHelper.WriteLogInfo($"[PLC-TRACE] {fixedCameraTraceId} concurrent ToolBlock execution starting, taskCount={procedureTasks.Count}, thread={Thread.CurrentThread.ManagedThreadId}, time={DateTime.Now:HH:mm:ss.fff}");
                                     await Task.WhenAll(procedureTasks);
-                                    concurrentWatch.Stop();
-                                    LogHelper.WriteLogInfo($"[PLC-TRACE] {fixedCameraTraceId} concurrent ToolBlock execution done, taskCount={procedureTasks.Count}, elapsedMs={concurrentWatch.ElapsedMilliseconds}, thread={Thread.CurrentThread.ManagedThreadId}, time={DateTime.Now:HH:mm:ss.fff}");
-                                });
+                                    });
                             }
 
                             //如果最后一个拍照点,则需要等待前面所有的拍照处理完成
@@ -2650,8 +2703,7 @@ namespace TeamAAS_VP.Core
                                 //等待所有的图像处理流程处理完成
                                 //之前版本使用的是lock机制无法进行并发操作,目前能完美进行并发且能等待所有任务完成
                                 // 异步等待 countdown,避免同步阻塞线程池线程
-                                LogHelper.WriteLogInfo($"[PLC-TRACE] {fixedCameraTraceId} countdown wait start, expected={countdown.InitialCount}, current={countdown.CurrentCount}, thread={Thread.CurrentThread.ManagedThreadId}, time={DateTime.Now:HH:mm:ss.fff}");
-                                bool countdownCompleted;
+                                 bool countdownCompleted;
                                 using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
                                 {
                                     try
@@ -2664,7 +2716,6 @@ namespace TeamAAS_VP.Core
                                         countdownCompleted = false;
                                     }
                                 }
-                                LogHelper.WriteLogInfo($"[PLC-TRACE] {fixedCameraTraceId} countdown wait done, completed={countdownCompleted}, remaining={countdown.CurrentCount}, thread={Thread.CurrentThread.ManagedThreadId}, time={DateTime.Now:HH:mm:ss.fff}");
                                 if (!countdownCompleted)
                                 {
                                     SendTaskMessage($"检测流程超时!预期{countdown.InitialCount}个模板完成,实际仅完成{countdown.InitialCount - countdown.CurrentCount}个,按NG处理", MessageLevel.Alarm);
@@ -2688,12 +2739,10 @@ namespace TeamAAS_VP.Core
                                             resultFlog = false;
                                             break;
                                         }
-
                                     }
                                 }
                                 else
                                 {
-
                                     foreach (var item in AllResult)
                                     {
                                         if (!item)
@@ -2975,8 +3024,6 @@ namespace TeamAAS_VP.Core
                                 //过站检查
                                 if (state == 1)
                                 {
-                                    FinalResultStatus = "检测中";
-                                    FinalResultText = "检测中";
                                     //这里缺少从MES获取产品配方的内容,需要补充
                                     _LastMesCheckTime[i] = (await _mesService.GetServerTimeAsync(code, 2)).Now;
                                     DeviceInfo stationConfig2 = new DeviceInfo();
@@ -3078,7 +3125,8 @@ namespace TeamAAS_VP.Core
                                                 SendTaskMessage($"KB_PN:{kb_pn[i]}", MessageLevel.Info);
                                             }
                                             ShowMes_Query = true;
-
+                                            FinalResultStatus = "检测中";
+                                            FinalResultText = "检测中";
                                             if (stationConfig2.EnableMES)
                                             {
                                                 //切换产品
@@ -3108,10 +3156,20 @@ namespace TeamAAS_VP.Core
                                                                     if (currentProduct.ID != productId && q.ProductLoad.Contains(kb_pn[i]))
                                                                     {
                                                                         //切换产品
-                                                                        SendTaskMessage($"开始切换产品{q.ProductKBSN}", MessageLevel.Error);
-                                                                        _productService.LoadProduct(productId);
+                                                                        SendTaskMessage($"开始切换产品{q.ProductName}", MessageLevel.Error);
+                                                                        var _ = Application.Current.Dispatcher.BeginInvoke(new Action(() => { _dialogService.ShowDialog("UProgressBar", rst => { }); }));
+                                                                        await Task.Delay(50);
+                                                                        _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                                                                        {
+                                                                            _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"切换{q.ProductName}产品中...", Value = 0 });
+                                                                        }));
+                                                                        await _productService.LoadProductAsync(productId);
                                                                         _productService.SetCurrentProduct(productId);
                                                                         await WritePositionToPlcAsync();
+                                                                        _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                                                                        {
+                                                                            _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"{Lang.完成}", Value = -1 });
+                                                                        }));
                                                                         SendTaskMessage($"切换产品成功", MessageLevel.Debug);
                                                                         break;
                                                                     }
@@ -3122,9 +3180,19 @@ namespace TeamAAS_VP.Core
                                                                     {
                                                                         //切换产品
                                                                         SendTaskMessage($"开始切换产品{q.ProductCode}", MessageLevel.Error);
-                                                                        _productService.LoadProduct(productId);
+                                                                        var _ = Application.Current.Dispatcher.BeginInvoke(new Action(() => { _dialogService.ShowDialog("UProgressBar", rst => { }); }));
+                                                                        await Task.Delay(50);
+                                                                        _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                                                                        {
+                                                                            _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"切换{q.ProductName}产品中...", Value = 0 });
+                                                                        }));
+                                                                        await _productService.LoadProductAsync(productId);
                                                                         _productService.SetCurrentProduct(productId);
                                                                         await WritePositionToPlcAsync();
+                                                                        _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                                                                        {
+                                                                            _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"{Lang.完成}", Value = -1 });
+                                                                        }));
                                                                         SendTaskMessage($"切换产品成功", MessageLevel.Debug);
                                                                         break;
                                                                     }
@@ -3176,13 +3244,16 @@ namespace TeamAAS_VP.Core
                                     var stationConfig = _configService.GetDeviceInfo(0);
 
                                     List<DataFormCtq> DFC = new List<DataFormCtq>();
+                                    List<DataFormCtq> NGDFC = new List<DataFormCtq>();
                                     SendTaskMessage($"收到{i}上传本站数据请求...", MessageLevel.Debug);
                                     //读取产品结果
-
                                     Int16 resultIndex = plc.ReadNode<Int16>(string.Format(addressConfig.In_Result.Address, i));
                                     SendTaskMessage($"读取plc结果{resultIndex}", MessageLevel.Debug);
 
-                                    await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
+                                    if (!sysConfig.IsInOut)
+                                    {
+                                        await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
+                                    }
 
                                     //这里缺少上传MES的本站数据结果及开始和结束时间,需要补充
                                     //从服务器中获取当前时间
@@ -3215,16 +3286,16 @@ namespace TeamAAS_VP.Core
                                     LogHelper.WriteLogMes($"【mes开始ADD】");
                                     SendTaskMessage($"mes开始ADD", MessageLevel.Info);
 
+                                    string Error = "";
                                     StringBuilder submitDataError = new StringBuilder();
                                     foreach (var item in lockResultsAll)
                                     {
                                         if (item.PhotoPassed == "NG")
                                         {
                                             submitDataError.Append(item.PhotoName + "_" + item.ResultData + "_Fail,");
+                                            Error += item.PhotoName + "_" + item.ResultData + "_Fail;";
                                         }
                                     }
-
-
                                     StringBuilder submitData = new StringBuilder();
                                     StringBuilder submitData2 = new StringBuilder();
                                     StringBuilder submitData3 = new StringBuilder();
@@ -3332,7 +3403,10 @@ namespace TeamAAS_VP.Core
                                         LogHelper.WriteLogMes($"上传mes失败");
                                         SendTaskMessage($"上传mes失败", MessageLevel.Error);
                                     }
-
+                                    if (sysConfig.IsInOut)
+                                    {
+                                        await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
+                                    }
                                     //是否保存当前的产品记录至CSV?
                                     if (stationConfig.SaveCsv)
                                     {
@@ -3413,7 +3487,7 @@ namespace TeamAAS_VP.Core
                                                     PROCESS_END_TIME = serverTimeResult.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffffff") + "+08:00",
                                                     SUPPLIER_NAME = stationConfig.DataInfo.SUPPLIER_NAME,
                                                     COMMODITY_TYPE = temp_COMMODITY_TYPE,
-                                                    REVISION = stationConfig.DataInfo.REVISION + "_" + stage,//
+                                                    REVISION = stationConfig.DataInfo.REVISION + "_" + stage[i],//
                                                     WO = wo[i],//
                                                     MFG_LOT = stationConfig.DataInfo.MFG_LOT,//
                                                     MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE,
@@ -3448,7 +3522,7 @@ namespace TeamAAS_VP.Core
                                                         PROCESS_END_TIME = serverTimeResult.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffffff") + "+08:00",
                                                         SUPPLIER_NAME = stationConfig.DataInfo.SUPPLIER_NAME,
                                                         COMMODITY_TYPE = stationConfig.DataInfo.COMMODITY_TYPE,
-                                                        REVISION = stationConfig.DataInfo.REVISION + "_" + stage,//
+                                                        REVISION = stationConfig.DataInfo.REVISION + "_" + stage[i],//
                                                         WO = wo[i],//
                                                         MFG_LOT = stationConfig.DataInfo.MFG_LOT,//
                                                         MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE,
@@ -3480,7 +3554,7 @@ namespace TeamAAS_VP.Core
                                                         PROCESS_END_TIME = serverTimeResult.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffffff") + "+08:00",
                                                         SUPPLIER_NAME = stationConfig.DataInfo.SUPPLIER_NAME,
                                                         COMMODITY_TYPE = stationConfig.DataInfo.COMMODITY_TYPE,
-                                                        REVISION = stationConfig.DataInfo.REVISION + "_" + stage,//
+                                                        REVISION = stationConfig.DataInfo.REVISION + "_" + stage[i],//
                                                         WO = wo[i],//
                                                         MFG_LOT = stationConfig.DataInfo.MFG_LOT,//
                                                         MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE,
@@ -3503,13 +3577,24 @@ namespace TeamAAS_VP.Core
                                             //{
                                             //    resultToMes.Clear();
                                             //}
-                                            bool recordResult = RecordStationResult(i, DFC, ProductMainSN, serverTimeResult.Now.Subtract(_LastMesCheckTime[i]).TotalSeconds, _LastMesCheckTime[i], stationConfig.SaveCsvPath);
+                                            bool recordResult = RecordStationResult(i, DFC, ProductMainSN, serverTimeResult.Now.Subtract(_LastMesCheckTime[i]).TotalSeconds, _LastMesCheckTime[i], stationConfig.SaveCsvPath, out string actualCsvPath);
                                             // bool recordResult1 = WriteProductLog( ProductMainSN, serverTimeResult.Now.Subtract(_LastMesCheckTime[i]).TotalSeconds, _LastMesCheckTime[i], stationConfig.SaveCsvPath, DFC);
 
                                             //keyData.Clear();
                                             if (recordResult)
                                             {
                                                 SendTaskMessage($"{i}:{stationConfig.DeviceName}产品记录保存成功", MessageLevel.Debug);
+                                                if (Error != "")
+                                                {
+                                                    NGDFC.Add(new DataFormCtq
+                                                    {
+                                                        PROCESS_START_TIME = _LastMesCheckTime[i].ToString("yyyy-MM-ddTHH:mm:ss.fffffff") + "+08:00",
+                                                        PROCESS_END_TIME = serverTimeResult.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffffff") + "+08:00",
+                                                        ERROR_MESSAGE = Error,
+                                                        PD_ATTR_01 = ProductMainSN,//
+                                                    });
+                                                    RecordStationNGResult(NGDFC, _LastMesCheckTime[i]);
+                                                }
                                                 //上传数据
                                                 try
                                                 {
@@ -3528,20 +3613,34 @@ namespace TeamAAS_VP.Core
 
                                                             //filePath = $"D:\\Image\\Recored\\{dt.ToString("yyyy-MM")}\\{dt.ToString("MM-dd")}\\{currentProduct.Name}\\{ProductMainSN}";
                                                             filePath = $"{PhotoPath}\\Recored\\{dt.ToString("yyyy-MM")}\\{dt.ToString("MM-dd")}\\{currentProduct.Name}\\{ProductMainSN}";
-                                                            //string source_csv2 = $"{stationConfig.SaveCsvPath}\\{dt.ToString("yyyy-MM")}\\{i}_{stationConfig.DeviceName}_{dt.ToString("yyyy-MM-dd")}";
                                                             string csvfilename = $"{i}_{stationConfig.DeviceName}_{dt.ToString("yyyy-MM-dd")}";
-                                                            //复制csv
-                                                            string source_csv = $"{stationConfig.SaveCsvPath}\\{dt.ToString("yyyy-MM")}\\{i}_{stationConfig.DeviceName}_{dt.ToString("yyyy-MM-dd")}.csv";
-                                                            //string source_csv1 = $"{stationConfig.SaveCsvPath}\\{dt.ToString("yyyy-MM")}";
-                                                            string target_csv = Path.Combine(filePath, Path.GetFileName(source_csv));
-                                                            File.Copy(source_csv, target_csv, true);
+                                                            //复制csv — 使用 actualCsvPath(可能是备份路径)
+                                                            string target_csv = Path.Combine(filePath, Path.GetFileName(actualCsvPath));
+                                                            try
+                                                            {
+                                                                if (!Directory.Exists(filePath))
+                                                                    Directory.CreateDirectory(filePath);
+                                                                File.Copy(actualCsvPath, target_csv, true);
+                                                            }
+                                                            catch (Exception copyEx)
+                                                            {
+                                                                LogHelper.WriteLogError($"复制CSV到上传目录失败: src={actualCsvPath}, dst={target_csv}", copyEx);
+                                                            }
                                                             //复制log
-                                                            string source_log = $"..\\Log\\LogInfo\\{dt.ToString("yyyy-MM")}\\{dt.ToString("yyyy-MM-dd")}.txt";
-                                                            string target_log = Path.Combine(filePath, Path.GetFileName(source_log));
-                                                            File.Copy(source_log, target_log, true);
+                                                            try
+                                                            {
+                                                                string source_log = $"..\\Log\\LogInfo\\{dt.ToString("yyyy-MM")}\\{dt.ToString("yyyy-MM-dd")}.txt";
+                                                                string target_log = Path.Combine(filePath, Path.GetFileName(source_log));
+                                                                File.Copy(source_log, target_log, true);
+                                                            }
+                                                            catch (Exception copyLogEx)
+                                                            {
+                                                                LogHelper.WriteLogError($"复制Log到上传目录失败", copyLogEx);
+                                                            }
 
 
-                                                            CompressionImage(filePath, filename, device.Station, device.FixtureId);
+                                                            try { CompressionImage(filePath, filename, device.Station, device.FixtureId); }
+                                                            catch (Exception zipEx) { LogHelper.WriteLogError($"压缩图片文件夹失败", zipEx); }
                                                             //上传csv
                                                             bool isAfter20 = dt.Hour >= 20;
                                                             //判断是否是新的一天
@@ -3549,12 +3648,16 @@ namespace TeamAAS_VP.Core
                                                             //当天还没执行过压缩
                                                             if (isAfter20 && isNewDay && !_hasCompressedToday)
                                                             {
-                                                                // 执行压缩
-                                                                CompressionSingleCsv(source_csv, csvfilename, device.Station, device.FixtureId);
-                                                                _lastCompressDate = dt;
-                                                                _hasCompressedToday = true;
-                                                                LogHelper.WriteLogMes("csv上传数据成功");
-                                                                SendTaskMessage($"csv上传数据成功", MessageLevel.Debug);
+                                                                // 执行压缩 — 使用 actualCsvPath
+                                                                try
+                                                                {
+                                                                    CompressionSingleCsv(actualCsvPath, csvfilename, device.Station, device.FixtureId);
+                                                                    _lastCompressDate = dt;
+                                                                    _hasCompressedToday = true;
+                                                                    LogHelper.WriteLogMes("csv上传数据成功");
+                                                                    SendTaskMessage($"csv上传数据成功", MessageLevel.Debug);
+                                                                }
+                                                                catch (Exception csvZipEx) { LogHelper.WriteLogError($"压缩CSV文件失败", csvZipEx); }
                                                             }
                                                             // 跨天后重置当天执行标记
                                                             if (!isNewDay && _hasCompressedToday)
@@ -5682,8 +5785,9 @@ namespace TeamAAS_VP.Core
         /// <param name="savePath"></param>
         /// <param name="isFirstEnter"></param>
         /// <returns></returns>
-        private bool RecordStationResult(int stationIndex, IEnumerable<DataFormCtq> dfcs, string productMainSN, double ct, DateTime timestamp, string savePath, bool isFirstEnter = true)
+        private bool RecordStationResult(int stationIndex, IEnumerable<DataFormCtq> dfcs, string productMainSN, double ct, DateTime timestamp, string savePath, out string actualPath, bool isFirstEnter = true)
         {
+            actualPath = null;
             try
             {
                 //获取当前工位的配置
@@ -5734,6 +5838,75 @@ namespace TeamAAS_VP.Core
                     sb.Append($"{dfc.PPID},{dfc.PROCESS_NAME},{dfc.PROCESS_START_TIME},{dfc.CTQ_NAME},{dfc.CTQ_VALUE},{dfc.CTQ_LSL},{dfc.CTQ_USL},{dfc.PROCESS_END_TIME},{dfc.SUPPLIER_NAME},{dfc.COMMODITY_TYPE},{dfc.REVISION},{dfc.WO},{dfc.MFG_LOT},{dfc.MFG_ASSY_LINE},{dfc.PROCESS_OUTCOME},{dfc.PROCESS_MACHINE_ID},{dfc.CTQ_UNIT_OF_MEASURE},{dfc.ERROR_MESSAGE},{dfc.PD_ATTR_01},{dfc.PD_ATTR_02},{dfc.PD_ATTR_03},{dfc.PD_ATTR_04},{dfc.PD_ATTR_05},{dfc.PD_ATTR_06}");
                     sb.AppendLine();
                 }
+                // 使用 StreamWriter 以追加模式写入,使用 using 确保异常时句柄不泄漏
+                try
+                {
+                    using (StreamWriter sw = new StreamWriter(fullPath, true, Encoding.UTF8))
+                    {
+                        sw.Write(sb.ToString());
+                    }
+                    actualPath = fullPath;
+                    return true;
+                }
+                catch (IOException ioEx) when (ioEx.Message.Contains("另一进程") || ioEx.Message.Contains("being used"))
+                {
+                    // 主文件被占用,生成备份文件写入
+                    string mainDir = Path.GetDirectoryName(fullPath);
+                    if (string.IsNullOrEmpty(mainDir))
+                        mainDir = savePath;
+                    string backupDir = Path.Combine(mainDir, "Backup");
+                    if (!Directory.Exists(backupDir))
+                        Directory.CreateDirectory(backupDir);
+                    string backupFileName = $"{stationIndex}_{stationConfig.DeviceName}_{DateTime.Now:yyyy-MM-dd_HHmmssfff}.csv";
+                    string backupPath = Path.Combine(backupDir, backupFileName);
+                    using (StreamWriter sw = new StreamWriter(backupPath, false, Encoding.UTF8))
+                    {
+                        sw.Write(sb.ToString());
+                    }
+                    actualPath = backupPath;
+                    SendTaskMessage($"主CSV文件被占用,已写入备份文件: {backupPath}", MessageLevel.Alarm);
+                    LogHelper.WriteLogInfo($"CSV主文件被占用,已写备份: 主={fullPath}, 备份={backupPath}");
+                    return true;
+                } 
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("记录其他工位结果时出错!", ex);
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// 记录工位的NG结果
+        /// </summary>
+        /// <param name="dfcs">NG数据</param>
+        /// <param name="timestamp">时间</param>
+        /// <returns></returns>
+        private bool RecordStationNGResult(IEnumerable<DataFormCtq> dfcs, DateTime timestamp)
+        {
+            try
+            {
+                string datePath = timestamp.ToString("yyyy-MM") + "\\";
+                string dirPath = Path.Combine(@"D:\NG", datePath);
+                if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);
+                string fileName = $"NG_{timestamp.ToString("yyyy-MM-dd")}.csv";
+                string fullPath = Path.Combine(dirPath, fileName);
+                // ============================================================
+                StringBuilder sb = new StringBuilder();
+                if (!File.Exists(fullPath))
+                {
+                    sb.Append("PD_ATTR_01,ERROR_MESSAGE,PROCESS_START_TIME,PROCESS_END_TIME");
+                    sb.AppendLine();
+                }
+                if (dfcs == null || dfcs.Count() == 0)
+                {
+                    return false;
+                }
+                foreach (var dfc in dfcs)
+                {
+                    sb.Append($"{dfc.PD_ATTR_01},{dfc.ERROR_MESSAGE},{dfc.PROCESS_START_TIME},{dfc.PROCESS_END_TIME}");
+                    sb.AppendLine();
+                }
                 // 使用 StreamWriter 以追加模式打开文件
                 StreamWriter sw = new StreamWriter(fullPath, true, Encoding.UTF8);
                 sw.Write(sb.ToString());
@@ -5743,17 +5916,7 @@ namespace TeamAAS_VP.Core
             }
             catch (Exception ex)
             {
-                if (!isFirstEnter)
-                {
-                    return false;
-                }
                 LogHelper.WriteLogError("记录其他工位结果时出错!", ex);
-                //我希望在这里生成一个临时文件来保存这次的记录,因为如果本地文件被打开时可能会导致无法写入,如果不保存的话可能会丢失这次的记录,所以我想在这里生成一个临时文件来保存这次的记录
-                //生成临时文件的路径,格式为savePath\yyyy-MM\工位编号_yyyy-MM-dd_temp\工位编号_yyyy-MM-dd_HH-mm-ss-fff_temp.csv
-                string datePath = timestamp.ToString("yyyy-MM") + "\\";
-                string tempDirPath = Path.Combine(savePath, datePath, $"{stationIndex}_{timestamp.ToString("yyyy-MM-dd")}_temp");
-                string tempFileName = $"{stationIndex}_{timestamp.ToString("yyyy-MM-dd_HH-mm-ss-fff")}_temp.csv";
-                RecordStationResult(stationIndex, dfcs, productMainSN, ct, timestamp, tempFileName, false);
                 return false;
             }
         }
@@ -5949,7 +6112,6 @@ namespace TeamAAS_VP.Core
         /// <returns></returns>
         private async Task<(bool IsSucceed, ICogImage[] imges, string Message, int pointIndex, Guid procedureId)> GrabImageEx(int pointIndex, Guid procedureId, bool Is3dPhoto, ProductModel currentProduct, SystemConfiguration sysConfig)
         {
-            var grabExWatch = Stopwatch.StartNew();
             // 获取当前产品
             //var currentProduct = _productService.GetCurrentProduct();
             if (currentProduct == null)
@@ -5966,7 +6128,7 @@ namespace TeamAAS_VP.Core
 
             try
             {
-                var procFindWatch = Stopwatch.StartNew();
+               
                 ProcedureModel selectedProcedure = null;
                 try
                 {
@@ -5982,9 +6144,7 @@ namespace TeamAAS_VP.Core
                 {
                     LogHelper.WriteLogError("查找 AOI 点位对应视觉流程时出错", ex);
                 }
-                procFindWatch.Stop();
-                LogHelper.WriteLogInfo($"[PLC-TRACE] GrabImageEx procFind done, pointIndex={pointIndex}, procId={procedureId}, is3D={Is3dPhoto}, elapsedMs={procFindWatch.ElapsedMilliseconds}, thread={Thread.CurrentThread.ManagedThreadId}");
-
+              
                 if (selectedProcedure == null)
                 {
                     SendTaskMessage($"未找到 AOI 点位对应的视觉流程,Point#{pointIndex} ProcedureId={pointIndex}", MessageLevel.Alarm);
@@ -5997,19 +6157,10 @@ namespace TeamAAS_VP.Core
                     //var sysConfig = _configService.GetSystemConfiguration();
                     if (selectedProcedure.Id == currentProduct.FixedDownCameraProcedureId && sysConfig.TurnOnAllLightUse == true)
                     {
-                        var lightWatch = Stopwatch.StartNew();
                         await _remoteCommandService.SetLightBeforePhoto(selectedProcedure);
-                        lightWatch.Stop();
-                        LogHelper.WriteLogInfo($"[PLC-TRACE] GrabImageEx SetLightBeforePhoto done, pointIndex={pointIndex}, elapsedMs={lightWatch.ElapsedMilliseconds}");
                     }
                     //1. 执行拍照
-                    var grabAsyncWatch = Stopwatch.StartNew();
-                    LogHelper.WriteLogInfo($"[PLC-TRACE] GrabImageEx ExecuteGrabImageAsync start, pointIndex={pointIndex}, procedureId={procedureId}, is3D={Is3dPhoto}, cameraId={selectedProcedure.CameraId}, photoCount={selectedProcedure.PhotoCount}, thread={Thread.CurrentThread.ManagedThreadId}");
                     var res = await _remoteCommandService.ExecuteGrabImageAsync(selectedProcedure, Is3dPhoto, sysConfig);
-                    grabAsyncWatch.Stop();
-                    LogHelper.WriteLogInfo($"[PLC-TRACE] GrabImageEx ExecuteGrabImageAsync done, pointIndex={pointIndex}, procedureId={procedureId}, success={res.IsSucceed}, elapsedMs={grabAsyncWatch.ElapsedMilliseconds}, msg={res.Msg}");
-                    grabExWatch.Stop();
-                    LogHelper.WriteLogInfo($"[PLC-TRACE] GrabImageEx total done, pointIndex={pointIndex}, procedureId={procedureId}, totalElapsedMs={grabExWatch.ElapsedMilliseconds}");
                     return (res.IsSucceed, res.Image, res.Msg, pointIndex, procedureId);
                 }
                 catch (Exception ex)

+ 38 - 38
TeamAAS-VM/Core/PLCs/OPCuaClientPLC.cs

@@ -260,7 +260,7 @@ namespace TeamAAS_VP.Core.PLCs
         /// <returns></returns>
         public async Task<Dictionary<string, object>> ReadNodesAsync(string[] nodeIds)
         {
-            var readWatch = Stopwatch.StartNew();
+            //var readWatch = Stopwatch.StartNew();
             var result = new Dictionary<string, object>();
             var readNodeIds = nodeIds.Select(s =>
             {
@@ -279,11 +279,11 @@ namespace TeamAAS_VP.Core.PLCs
                 readNodeIdList.Add(new NodeId(readNodeId));
             }
             var values = await OpcUaClient.ReadNodesAsync(readNodeIdList.ToArray());
-            readWatch.Stop();
-            if (readWatch.ElapsedMilliseconds > Math.Max(50, DefaultSubscriptionPollingInterval))
-            {
-                LogHelper.WriteLogInfo($"[PLC-TRACE] ReadNodesAsync slow, plc={Name}, nodeCount={nodeIds.Length}, elapsedMs={readWatch.ElapsedMilliseconds}, thread={Thread.CurrentThread.ManagedThreadId}");
-            }
+           // readWatch.Stop();
+            //if (readWatch.ElapsedMilliseconds > Math.Max(50, DefaultSubscriptionPollingInterval))
+            //{
+            //    LogHelper.WriteLogInfo($"[PLC-TRACE] ReadNodesAsync slow, plc={Name}, nodeCount={nodeIds.Length}, elapsedMs={readWatch.ElapsedMilliseconds}, thread={Thread.CurrentThread.ManagedThreadId}");
+            //}
             for (int i = 0; i < nodeIds.Length; i++)
             {
                 result[nodeIds[i]] = values[i].Value;
@@ -379,24 +379,24 @@ namespace TeamAAS_VP.Core.PLCs
         /// <returns></returns>
         public async Task<bool> WriteNodeAsync<T>(string nodeId, T value)
         {
-            var writeWatch = Stopwatch.StartNew();
+            //var writeWatch = Stopwatch.StartNew();
             string writeNodeId = nodeId;
             if (!nodeId.StartsWith(NodeHeader))
             {
                 writeNodeId = NodeHeader + nodeId;
             }
-            LogHelper.WriteLogInfo($"[PLC-TRACE] WriteNodeAsync start, plc={Name}, node={nodeId}, fullNode={writeNodeId}, value={value}, thread={Thread.CurrentThread.ManagedThreadId}, time={DateTime.Now:HH:mm:ss.fff}");
+           // LogHelper.WriteLogInfo($"[PLC-TRACE] WriteNodeAsync start, plc={Name}, node={nodeId}, fullNode={writeNodeId}, value={value}, thread={Thread.CurrentThread.ManagedThreadId}, time={DateTime.Now:HH:mm:ss.fff}");
             try
             {
                 bool result = await OpcUaClient.WriteNodeAsync<T>(writeNodeId, value).ConfigureAwait(false);
-                writeWatch.Stop();
-                LogHelper.WriteLogInfo($"[PLC-TRACE] WriteNodeAsync done, plc={Name}, node={nodeId}, value={value}, result={result}, elapsedMs={writeWatch.ElapsedMilliseconds}, thread={Thread.CurrentThread.ManagedThreadId}, time={DateTime.Now:HH:mm:ss.fff}");
+                //writeWatch.Stop();
+                //LogHelper.WriteLogInfo($"[PLC-TRACE] WriteNodeAsync done, plc={Name}, node={nodeId}, value={value}, result={result}, elapsedMs={writeWatch.ElapsedMilliseconds}, thread={Thread.CurrentThread.ManagedThreadId}, time={DateTime.Now:HH:mm:ss.fff}");
                 return result;
             }
             catch (Exception ex)
             {
-                writeWatch.Stop();
-                LogHelper.WriteLogError($"[PLC-TRACE] WriteNodeAsync failed, plc={Name}, node={nodeId}, value={value}, elapsedMs={writeWatch.ElapsedMilliseconds}", ex);
+                //writeWatch.Stop();
+                //LogHelper.WriteLogError($"[PLC-TRACE] WriteNodeAsync failed, plc={Name}, node={nodeId}, value={value}, elapsedMs={writeWatch.ElapsedMilliseconds}", ex);
                 throw;
             }
         }
@@ -583,15 +583,15 @@ namespace TeamAAS_VP.Core.PLCs
         {
             while (!context.CancellationTokenSource.IsCancellationRequested)
             {
-                var cycleWatch = Stopwatch.StartNew();
+               // var cycleWatch = Stopwatch.StartNew();
                 int notifyCount = 0;
                 try
                 {
                     if (IsConnected && context.NodeIds.Count > 0)
                     {
-                        var pollingReadWatch = Stopwatch.StartNew();
+                        //var pollingReadWatch = Stopwatch.StartNew();
                         var values = await ReadNodesAsync(context.NodeIds.ToArray()).ConfigureAwait(false);
-                        pollingReadWatch.Stop();
+                       // pollingReadWatch.Stop();
                         foreach (var nodeId in context.NodeIds)
                         {
                             object currentValue;
@@ -606,14 +606,14 @@ namespace TeamAAS_VP.Core.PLCs
                                 context.LastValues[nodeId] = currentValue;
                                 if (context.NotifyOnFirstScan)
                                 {
-                                    var handlerWatch = Stopwatch.StartNew();
+                                   // var handlerWatch = Stopwatch.StartNew();
                                     context.DataChangeHandler?.Invoke((context.Key, nodeId, currentValue));
-                                    handlerWatch.Stop();
-                                    notifyCount++;
-                                    if (handlerWatch.ElapsedMilliseconds > 20)
-                                    {
-                                        LogHelper.WriteLogInfo($"[PLC-TRACE] Polling handler slow(first), key={context.Key}, node={nodeId}, elapsedMs={handlerWatch.ElapsedMilliseconds}");
-                                    }
+                                    //handlerWatch.Stop();
+                                    //notifyCount++;
+                                    //if (handlerWatch.ElapsedMilliseconds > 20)
+                                    //{
+                                    //    LogHelper.WriteLogInfo($"[PLC-TRACE] Polling handler slow(first), key={context.Key}, node={nodeId}, elapsedMs={handlerWatch.ElapsedMilliseconds}");
+                                    //}
                                 }
                                 continue;
                             }
@@ -621,20 +621,20 @@ namespace TeamAAS_VP.Core.PLCs
                             if (!Utils.IsEqual(lastValue, currentValue))
                             {
                                 context.LastValues[nodeId] = currentValue;
-                                var handlerWatch = Stopwatch.StartNew();
+                               // var handlerWatch = Stopwatch.StartNew();
                                 context.DataChangeHandler?.Invoke((context.Key, nodeId, currentValue));
-                                handlerWatch.Stop();
-                                notifyCount++;
-                                if (handlerWatch.ElapsedMilliseconds > 20)
-                                {
-                                    LogHelper.WriteLogInfo($"[PLC-TRACE] Polling handler slow, key={context.Key}, node={nodeId}, elapsedMs={handlerWatch.ElapsedMilliseconds}, value={currentValue}");
-                                }
+                                //handlerWatch.Stop();
+                                //notifyCount++;
+                                //if (handlerWatch.ElapsedMilliseconds > 20)
+                                //{
+                                //    LogHelper.WriteLogInfo($"[PLC-TRACE] Polling handler slow, key={context.Key}, node={nodeId}, elapsedMs={handlerWatch.ElapsedMilliseconds}, value={currentValue}");
+                                //}
                             }
                         }
-                        if (pollingReadWatch.ElapsedMilliseconds > context.PollingInterval)
-                        {
-                            LogHelper.WriteLogInfo($"[PLC-TRACE] Polling read slower than interval, key={context.Key}, nodeCount={context.NodeIds.Count}, readMs={pollingReadWatch.ElapsedMilliseconds}, intervalMs={context.PollingInterval}");
-                        }
+                        //if (pollingReadWatch.ElapsedMilliseconds > context.PollingInterval)
+                        //{
+                        //    LogHelper.WriteLogInfo($"[PLC-TRACE] Polling read slower than interval, key={context.Key}, nodeCount={context.NodeIds.Count}, readMs={pollingReadWatch.ElapsedMilliseconds}, intervalMs={context.PollingInterval}");
+                        //}
                     }
                 }
                 catch (OperationCanceledException)
@@ -648,11 +648,11 @@ namespace TeamAAS_VP.Core.PLCs
 
                 try
                 {
-                    cycleWatch.Stop();
-                    if (cycleWatch.ElapsedMilliseconds > context.PollingInterval || notifyCount > 0)
-                    {
-                        LogHelper.WriteLogInfo($"[PLC-TRACE] Polling cycle, key={context.Key}, nodeCount={context.NodeIds.Count}, notifyCount={notifyCount}, elapsedMs={cycleWatch.ElapsedMilliseconds}, intervalMs={context.PollingInterval}");
-                    }
+                    //cycleWatch.Stop();
+                    //if (cycleWatch.ElapsedMilliseconds > context.PollingInterval || notifyCount > 0)
+                    //{
+                    //    LogHelper.WriteLogInfo($"[PLC-TRACE] Polling cycle, key={context.Key}, nodeCount={context.NodeIds.Count}, notifyCount={notifyCount}, elapsedMs={cycleWatch.ElapsedMilliseconds}, intervalMs={context.PollingInterval}");
+                    //}
                     await Task.Delay(context.PollingInterval, context.CancellationTokenSource.Token).ConfigureAwait(false);
                 }
                 catch (OperationCanceledException)

+ 51 - 19
TeamAAS-VM/Data/DatabaseInitializer.cs

@@ -21,31 +21,63 @@ namespace TeamAAS_VP.Data
 
         public void InitializeDatabase()
         {
-            try
+            const int maxRetry = 3;
+            Exception lastEx = null;
+
+            for (int attempt = 1; attempt <= maxRetry; attempt++)
             {
-                // 创建数据库(如果不存在)
-                _db.DbMaintenance.CreateDatabase();
-                // 创建表
-                _db.CodeFirst.InitTables(typeof(User), typeof(ProductionRecord), typeof(UserLoginRecord), typeof(AlarmRecord), typeof(LockResult), typeof(ProductWithMes));
+                try
+                {
+                    // 创建数据库(如果不存在)
+                    _db.DbMaintenance.CreateDatabase();
+                    // 创建表
+                    _db.CodeFirst.InitTables(typeof(User), typeof(ProductionRecord), typeof(UserLoginRecord), typeof(AlarmRecord), typeof(LockResult), typeof(ProductWithMes));
 
-                // 创建索引
-                CreateIndexes();
+                    // 创建索引
+                    CreateIndexes();
 
-                // 如果用户表为空,插入三个基础用户
-                var userCount = _db.Queryable<User>().Count();
-                if (userCount == 0)
+                    // 如果用户表为空,插入三个基础用户
+                    var userCount = _db.Queryable<User>().Count();
+                    if (userCount == 0)
+                    {
+                        var op = new User { UserName = "操作员", UserPassword = "", userPart = Enums.UserPart.Operator, CreateTime = DateTime.Now, IsRemember = false };
+                        var eng = new User { UserName = "工程师", UserPassword = "10086", userPart = Enums.UserPart.Engineer, CreateTime = DateTime.Now, IsRemember = false };
+                        var admin = new User { UserName = "管理员", UserPassword = "team123456", userPart = Enums.UserPart.Administrator, CreateTime = DateTime.Now, IsRemember = false };
+                        _db.Insertable(new List<User> { op, eng, admin }).ExecuteCommand();
+                    }
+                    return; // 成功,退出
+                }
+                catch (Exception ex)
                 {
-                    var op = new User { UserName = "操作员", UserPassword = "", userPart = Enums.UserPart.Operator, CreateTime = DateTime.Now, IsRemember = false };
-                    var eng = new User { UserName = "工程师", UserPassword = "10086", userPart = Enums.UserPart.Engineer, CreateTime = DateTime.Now, IsRemember = false };
-                    var admin = new User { UserName = "管理员", UserPassword = "team123456", userPart = Enums.UserPart.Administrator, CreateTime = DateTime.Now, IsRemember = false };
-                    _db.Insertable(new List<User> { op, eng, admin }).ExecuteCommand();
+                    lastEx = ex;
+                    bool isDbCorrupted = ex.Message.Contains("invalid") || ex.Message.Contains("corrupt")
+                                      || ex.Message.Contains("无效") || ex.Message.Contains("损坏");
+
+                    if (attempt < maxRetry)
+                    {
+                        if (isDbCorrupted)
+                        {
+                            // 数据库可能损坏,尝试删除重建
+                            try
+                            {
+                                string dbPath = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "AlignerDB.db");
+                                if (System.IO.File.Exists(dbPath))
+                                {
+                                    System.IO.File.Delete(dbPath);
+                                }
+                                LogHelper.WriteLogInfo($"数据库文件已删除,准备重建 (attempt {attempt})");
+                            }
+                            catch
+                            {
+                                // 删除失败,继续重试
+                            }
+                        }
+                        System.Threading.Thread.Sleep(500 * attempt); // 递增等待
+                    }
                 }
             }
-            catch (Exception ex)
-            {
-                // 记录日志
-                throw new Exception($"数据库初始化失败: {ex.Message}", ex);
-            }
+            // 所有重试均失败
+            throw new Exception($"数据库初始化失败(已重试{maxRetry}次): {lastEx?.Message}", lastEx);
         }
 
         public bool DatabaseExists()

+ 16 - 16
TeamAAS-VM/Data/SystemDatabaseService.cs

@@ -398,23 +398,23 @@ namespace TeamAAS_VP.Data
             DateTime todayStart;
             DateTime todayEnd;
 
-            if (DateTime.Now.Hour >= 8 && DateTime.Now.Hour < 20)
+            if (DateTime.UtcNow.Hour >= 1 && DateTime.UtcNow.Hour < 13)
             {
                 // 白班
-                todayStart = DateTime.Today.AddHours(8);
-                todayEnd = DateTime.Today.AddHours(20);
+                todayStart = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 1, 0, 0, DateTimeKind.Utc);
+                todayEnd = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 13, 0, 0, DateTimeKind.Utc);
             }
-            else if (DateTime.Now.Hour >= 20)
+            else if (DateTime.UtcNow.Hour >= 13)
             {
                 // 当天夜班
-                todayStart = DateTime.Today.AddHours(20);
-                todayEnd = DateTime.Today.AddDays(1).AddHours(8);
+                todayStart = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 13, 0, 0, DateTimeKind.Utc);
+                todayEnd = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day + 1, 1, 0, 0, DateTimeKind.Utc);
             }
             else
             {
                 // 凌晨属于前一天夜班
-                todayStart = DateTime.Today.AddDays(-1).AddHours(20);
-                todayEnd = DateTime.Today.AddHours(8);
+                todayStart = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day - 1, 13, 0, 0, DateTimeKind.Utc);
+                todayEnd = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 1, 0, 0, DateTimeKind.Utc);
             }
 
             //按照产品名称和时间范围查询生产记录的总数
@@ -461,23 +461,23 @@ namespace TeamAAS_VP.Data
             DateTime todayStart;
             DateTime todayEnd;
 
-            if (DateTime.Now.Hour >= 8 && DateTime.Now.Hour < 20)
+            if (DateTime.UtcNow.Hour >= 1 && DateTime.UtcNow.Hour < 13)
             {
                 // 白班
-                todayStart = DateTime.Today.AddHours(8);
-                todayEnd = DateTime.Today.AddHours(20);
+                todayStart = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 1, 0, 0, DateTimeKind.Utc);
+                todayEnd = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 13, 0, 0, DateTimeKind.Utc);
             }
-            else if (DateTime.Now.Hour >= 20)
+            else if (DateTime.UtcNow.Hour >= 13)
             {
                 // 当天夜班
-                todayStart = DateTime.Today.AddHours(20);
-                todayEnd = DateTime.Today.AddDays(1).AddHours(8);
+                todayStart = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 13, 0, 0, DateTimeKind.Utc);
+                todayEnd = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day + 1, 1, 0, 0, DateTimeKind.Utc);
             }
             else
             {
                 // 凌晨属于前一天夜班
-                todayStart = DateTime.Today.AddDays(-1).AddHours(20);
-                todayEnd = DateTime.Today.AddHours(8);
+                todayStart = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day - 1, 13, 0, 0, DateTimeKind.Utc);
+                todayEnd = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 1, 0, 0, DateTimeKind.Utc);
             }
 
             //按照产品名称和时间范围查询生产记录的总数

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

@@ -551,5 +551,15 @@ namespace TeamAAS_VP.Models
             get => _IsTP_AOI;
             set => SetProperty(ref _IsTP_AOI, value);
         }
+
+        private bool _IsInOut = false;
+        /// <summary>
+        ///  ÊÇ·ñÊÇͬ½øÍ¬³ö
+        /// </summary>
+        public bool IsInOut
+        {
+            get => _IsInOut;
+            set => SetProperty(ref _IsInOut, value);
+        }
     }
 }

+ 12 - 7
TeamAAS-VM/Services/RemoteCommandService.cs

@@ -2531,13 +2531,15 @@ namespace TeamAAS_VP.Services
                             {
                                 SendTaskMessage($"相机曝光设置失败!", MessageLevel.Error);
                             }
-                            SendTaskMessage($"设置曝光:{procedure.ExposureTime}",MessageLevel.Info);
+                            //SendTaskMessage($"设置曝光:{procedure.ExposureTime}",MessageLevel.Info);
+                            LogHelper.WriteLogInfo($"设置曝光:{procedure.ExposureTime}");
                             succed = camera.SetGain(procedure.Gain);
                             if (!succed)
                             {
                                 SendTaskMessage($"相机增益设置失败!", MessageLevel.Error);
                             }
-                            SendTaskMessage($"设置增益:{procedure.Gain}", MessageLevel.Info);
+                            //SendTaskMessage($"设置增益:{procedure.Gain}", MessageLevel.Info);
+                            LogHelper.WriteLogInfo($"设置增益:{procedure.Gain}");
                             image = camera.Grab();
                             //isSucceed = await _lightManagerService.TurnOffGlobalChannelAsync(0);
                             //if (!isSucceed)
@@ -2579,13 +2581,15 @@ namespace TeamAAS_VP.Services
                             {
                                 SendTaskMessage($"相机曝光设置失败!", MessageLevel.Error);
                             }
-                            SendTaskMessage($"设置曝光2:{procedure.ExposureTime2}", MessageLevel.Info);
+                            //SendTaskMessage($"设置曝光2:{procedure.ExposureTime2}", MessageLevel.Info);
+                            LogHelper.WriteLogInfo($"设置曝光2:{procedure.ExposureTime2}");
                             succed = camera.SetGain(procedure.Gain2);
                             if (!succed)
                             {
                                 SendTaskMessage($"相机增益设置失败!", MessageLevel.Error);
                             }
-                            SendTaskMessage($"设置增益2:{procedure.Gain2}", MessageLevel.Info);
+                            //SendTaskMessage($"设置增益2:{procedure.Gain2}", MessageLevel.Info);
+                            LogHelper.WriteLogInfo($"设置增益2:{procedure.ExposureTime2}");
                             image2 = camera.Grab();
 
                             //isSucceed = await _lightManagerService.TurnOffGlobalChannelAsync(0);
@@ -2673,7 +2677,8 @@ namespace TeamAAS_VP.Services
                     else
                     {
                         TempExposureTime = procedure.ExposureTime;
-                        SendTaskMessage($"设置相机曝光:{procedure.ExposureTime}", MessageLevel.Info);
+                        //SendTaskMessage($"设置相机曝光:{procedure.ExposureTime}", MessageLevel.Info);
+                        LogHelper.WriteLogInfo($"设置相机曝光:{procedure.ExposureTime}");
                     }
                     //else
                     //{
@@ -2700,7 +2705,8 @@ namespace TeamAAS_VP.Services
                     else
                     {
                         TempGain = procedure.Gain;
-                        SendTaskMessage($"设置相机增益:{TempGain}",MessageLevel.Info);
+                        //SendTaskMessage($"设置相机增益:{TempGain}",MessageLevel.Info);
+                        LogHelper.WriteLogInfo($"设置相机增益:{TempGain}");
                     }
 
                     //else
@@ -2708,7 +2714,6 @@ namespace TeamAAS_VP.Services
                     //    SendTaskMessage($"相机增益重复", MessageLevel.Error);
                     //}
 
-                    LogHelper.WriteLogInfo($"[PLC-TRACE] ExecuteGrabImageAsync before Grab, cameraId={procedure.CameraId}, is3D={Is3dPhoto}, setExpMs={setExpMs}, setGainMs={setGainMs}, thread={Thread.CurrentThread.ManagedThreadId}");
                     DateTime nowtime = DateTime.Now;
                     LogHelper.WriteLogInfo("开始采集图像");
                     //var image = camera.Grab();

+ 15 - 12
TeamAAS-VM/ViewModels/HomeViewModel.cs

@@ -10,6 +10,7 @@ using Prism.Events;
 using Prism.Ioc;
 using Prism.Mvvm;
 using Prism.Regions;
+using Prism.Services.Dialogs;
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
@@ -40,7 +41,7 @@ namespace TeamAAS_VP.ViewModels
         IConfigService _configService;
         IRemoteCommandService _remoteCommandService;
         ISystemDatabaseService _systemDatabaseService;
-
+        IDialogService _dialogService;
         #region 属性
 
         private bool _IsShowLabel = false;
@@ -210,11 +211,12 @@ namespace TeamAAS_VP.ViewModels
         #endregion
 
         public HomeViewModel(IEventAggregator ea, IContainerProvider container, IProductService productService, IConfigService configService, IRemoteCommandService remoteCommandService
-            , ISystemDatabaseService systemDatabaseService)
+        , IDialogService dialogService, ISystemDatabaseService systemDatabaseService)
         {
             _eventAggregator = ea;
             _container = container;
             _productService = productService;
+            _dialogService = dialogService;
             LoadedCommand = new DelegateCommand(OnLoad);
             _eventAggregator.GetEvent<TaskMessageNotification>().Subscribe(AddMessageInvoke);
             //订阅权限登录事件
@@ -272,26 +274,27 @@ namespace TeamAAS_VP.ViewModels
         {
             if (product == null) return;
             var view = new ShowMessage(Lang.是否加载产品, $"{Lang.加载产品}:{product.Name}?");
-            //show the dialog
+
             var result = await DialogHost.Show(view, "RootDialog", null, null, null);
             if (result != null && result is bool)
             {
                 if (((bool)result))
                 {
+                    var _ = Application.Current.Dispatcher.BeginInvoke(new Action(() => { _dialogService.ShowDialog("UProgressBar", rst => { }); }));
+                    await Task.Delay(50);
+                    _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                    {
+                        _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"切换{product.Name}产品中...", Value = 0 });
+                    }));
                     IsLoadProduct = false;
-                    var waiting = new WaitingControl();
-                    //show the dialog
-                    var task = DialogHost.Show(waiting, "RootDialog", null, null, null);
                     await _productService.LoadProductAsync(product.ID);
                     _productService.SetCurrentProduct(product.ID);
                     await management.WritePositionToPlcAsync();
-                    if (DialogHost.IsDialogOpen("RootDialog"))
-                    {
-                        DialogHost.Close("RootDialog");
-                    }
-                    await task;
-                    //DatabaseHelper.AddLoadProductRecord(product.ID, product.Name, false);
                     IsLoadProduct = true;
+                    _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                    {
+                        _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = 2, Minimum = 0, SubTitle = $"切换产品...", Message = $"{Lang.完成}", Value = -1 });
+                    }));
                 }
             }
         }

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

@@ -719,7 +719,7 @@ namespace TeamAAS_VP.Views.Home
                 return;
             }
 
-            LogHelper.WriteLogInfo($"[SAVE-IMAGE] 开始保存流程: Id={render.Id}, SaveModel={render.SaveImageModel}, SavePath={render.SavePath}, IsDelay={render.IsDelaySaveImage}, ProductCode={_productService.CurrentProductCode}");
+            //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 ||
@@ -850,7 +850,7 @@ namespace TeamAAS_VP.Views.Home
                 return;
             }
 
-            LogHelper.WriteLogInfo($"[SAVE-IMAGE] SaveImageAsync 开始: path={path}, model={saveImageModel}, pathModel={saveImagePathModel}, result={result}, is3D={is3Dmodel}, fileName={imageFileName}");
+            //LogHelper.WriteLogInfo($"[SAVE-IMAGE] SaveImageAsync 开始: path={path}, model={saveImageModel}, pathModel={saveImagePathModel}, result={result}, is3D={is3Dmodel}, fileName={imageFileName}");
 
             try
             {
@@ -898,8 +898,11 @@ namespace TeamAAS_VP.Views.Home
 
                         // 产品SN(过滤非法字符)
                         string productSN = _productService.CurrentProductCode;
-                        foreach (char c in Path.GetInvalidFileNameChars())
-                            productSN = productSN.Replace(c, '_');
+                        if (productSN != null)
+                        {
+                            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";

+ 2 - 2
TeamAAS-VM/Views/HomeView.xaml

@@ -76,7 +76,7 @@
                         UniformCornerRadius="10">
                         <DockPanel Margin="8" LastChildFill="True">
                             <Border
-                                Height="24"
+                                Height="30"
                                 Margin="0,0,0,8"
                                 BorderThickness="1"
                                 CornerRadius="6"
@@ -104,7 +104,7 @@
                                 <TextBlock
                                     HorizontalAlignment="Center"
                                     VerticalAlignment="Center"
-                                    FontSize="14"
+                                    FontSize="20"
                                     FontWeight="Bold"
                                     Foreground="White"
                                     Text="{Binding management.FinalResultText, FallbackValue=检测中}"