Przeglądaj źródła

Merge branch 'master' of http://49.235.130.76/XXF_1122/DaisyNPILine

KWD 7 miesięcy temu
rodzic
commit
808a2ec4a7

+ 78 - 50
TeamAAS-VM/Core/Cameras/OptCamera.cs

@@ -1,5 +1,6 @@
 using Cognex.VisionPro;
 using Microsoft.Win32;
+using netDxf.Entities;
 using NPOI.POIFS.Crypt.Dsig;
 using NPOI.SS.Formula.Eval;
 using SciCamera.Net;
@@ -29,6 +30,7 @@ namespace TeamAAS_VP.Core.Cameras
         //private readonly SemaphoreSlim _operationSemaphore = new SemaphoreSlim(1, 1);
         // 用于保护状态变量(例如 IsGrabbing)的并发访问
         private readonly object _stateLock = new object();
+        private TaskCompletionSource<ICogImage> _tcs;
         #endregion
 
         #region 属性
@@ -352,10 +354,10 @@ namespace TeamAAS_VP.Core.Cameras
                 // ch:打开设备 | en:Open device
                 nReVal = mvCameraAcq.OpenDevice();
                 // ch:设置触发模式为off | en:Set trigger mode as off
-                nReVal = mvCameraAcq.SetEnumValueByStringEx(SciCam.SciCamDeviceXmlType.SciCam_DeviceXml_Camera, "TriggerMode", "Off");
+                nReVal = mvCameraAcq.SetEnumValueByStringEx(SciCam.SciCamDeviceXmlType.SciCam_DeviceXml_Camera, "TriggerMode", "On");
                 nReVal = mvCameraAcq.SetEnumValueByString("TriggerSource", "Software");
                 // ch:注册事件回调 | en:Register event callback
-                //nReVal = mvCameraAcq.RegisterPayloadCallBack(new SciCam.fnOnPayloadDelegate(ImageCallBack), IntPtr.Zero, true);
+                nReVal = mvCameraAcq.RegisterPayloadCallBack(new SciCam.fnOnPayloadDelegate(ImageCallBack), IntPtr.Zero, true);
                 // ch:设置采集策略 | en:Set Grab Strategy
                 nReVal = mvCameraAcq.SetGrabStrategy(CurrentGrabStrategy);
                 if (nReVal != SciCam.SCI_CAMERA_OK)
@@ -401,8 +403,8 @@ namespace TeamAAS_VP.Core.Cameras
             //_operationSemaphore.Wait();
             try
             {
-                mvCameraAcq.StopGrabbing();
-                mvCameraAcq.CloseDevice();
+                mvCameraAcq?.StopGrabbing();
+                mvCameraAcq?.CloseDevice();
             }
             finally
             {
@@ -432,42 +434,37 @@ namespace TeamAAS_VP.Core.Cameras
                             OpenDevice();
                         }
 
-                        //如果仅仅采集最后一帧,则清空缓存
-                        if (lastImageOnly)
-                        {
-                            //判断当前的模式是否为最新模式,如果不是则设置为最新模式
-                            if (CurrentGrabStrategy != SciCam.SciCamGrabStrategy.SciCam_GrabStrategy_Upcoming)
-                            {
-                                CurrentGrabStrategy = SciCam.SciCamGrabStrategy.SciCam_GrabStrategy_Upcoming;
-                                nReVal = mvCameraAcq.SetGrabStrategy(CurrentGrabStrategy);
-                            }
-                        }
-                        else
-                        {
-                            //判断当前的模式是否为顺序模式,如果不是则设置为顺序模式
-                            if (CurrentGrabStrategy != SciCam.SciCamGrabStrategy.SciCam_GrabStrategy_Latest)
-                            {
-                                CurrentGrabStrategy = SciCam.SciCamGrabStrategy.SciCam_GrabStrategy_Latest;
-                                nReVal = mvCameraAcq.SetGrabStrategy(CurrentGrabStrategy);
-                            }
-                        }
-                        //if (!mvCameraAcq.IsGrabbing())
-                        //{
-                        //    mvCameraAcq.StartGrabbing();
-                        //}
-                        //nReVal = mvCameraAcq.StartGrabbing();
-                        //mvCameraAcq.ClearPayloadBuffer();
+                        // 重置TaskCompletionSource
+                        _tcs?.TrySetCanceled();
+                        _tcs = new TaskCompletionSource<ICogImage>();
+
+                        // 注册取消令牌
+                        CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(2000); // 2秒超时
+                        cancellationTokenSource.Token.Register(() => _tcs.TrySetCanceled());
+
+                        // 触发图像采集
                         // ch:发送软触发命令 | en:Send Trigger Software command
-                        //nReVal = mvCameraAcq.SetCommandValueEx(SciCam.SciCamDeviceXmlType.SciCam_DeviceXml_Camera, "TriggerSoftware");
-                        nReVal = mvCameraAcq.Grab(ref payload);
+                        nReVal = mvCameraAcq.SetCommandValueEx(SciCam.SciCamDeviceXmlType.SciCam_DeviceXml_Camera, "TriggerSoftware");
                         if (nReVal == SciCam.SCI_CAMERA_OK)
                         {
-                            Image = GetConvertedInfo(payload);
-                            mvCameraAcq.FreePayload(payload);
-                            //nReVal = mvCameraAcq.StopGrabbing();
+                            // 等待图像接收事件
+                            Image = _tcs.Task.Result;
                             TotalTime = sw.Elapsed;
                             ErrorMessage = $"{i + 1}";
-                            return Image;
+                            if (Image != null)
+                            {
+                                return Image;
+                            }
+                            else
+                            {
+                                //nReVal = mvCameraAcq.FreePayload(payload);
+                                //nReVal = mvCameraAcq.StopGrabbing();
+                                //_operationSemaphore.Release();
+                                CloseDevice();
+                                OpenDevice();
+                                ErrorMessage = "图像采集失败!";
+                            }
+
                         }
                         else
                         {
@@ -503,21 +500,31 @@ namespace TeamAAS_VP.Core.Cameras
         // ch:采集回调接口 | en:Grab Callback interface     
         void ImageCallBack(IntPtr payload, IntPtr tag)
         {
-            if (payload == IntPtr.Zero) return;
-            uint nReVal = SciCam.SCI_CAMERA_OK;
-
-            SciCam.SCI_CAM_PAYLOAD_ATTRIBUTE attribute = new SciCam.SCI_CAM_PAYLOAD_ATTRIBUTE();
-            nReVal = SciCam.PayloadGetAttribute(payload, ref attribute);
-            if (nReVal != SciCam.SCI_CAMERA_OK || !attribute.isComplete)
+            try
             {
-                Console.WriteLine("Failed to get image attributes or frame is incomplete! nRet:{0}", nReVal);
+                if (payload == IntPtr.Zero) return;
+                uint nReVal = SciCam.SCI_CAMERA_OK;
+
+                SciCam.SCI_CAM_PAYLOAD_ATTRIBUTE attribute = new SciCam.SCI_CAM_PAYLOAD_ATTRIBUTE();
+                nReVal = SciCam.PayloadGetAttribute(payload, ref attribute);
+                if (nReVal != SciCam.SCI_CAMERA_OK || !attribute.isComplete)
+                {
+                    Console.WriteLine("Failed to get image attributes or frame is incomplete! nRet:{0}", nReVal);
+                    _tcs?.TrySetException(new Exception($"Failed to get image attributes or frame is incomplete! nRet: {nReVal}"));
+                }
+                else
+                {
+                    Image = GetConvertedInfo(payload);
+                    //ImageCallbackEvent?.Invoke(Image, TotalTime, ErrorMessage);
+                    //Console.WriteLine("Get One Frame: Width[{0}], Height[{1}], nFrameNum[{2}]",
+                    //attribute.imgAttr.width, attribute.imgAttr.height, attribute.frameID);
+                    // 收到图像时完成任务
+                    _tcs?.TrySetResult(Image);
+                }
             }
-            else
+            catch (Exception ex)
             {
-                Image = GetConvertedInfo(payload);
-                ImageCallbackEvent?.Invoke(Image, TotalTime, ErrorMessage);
-                //Console.WriteLine("Get One Frame: Width[{0}], Height[{1}], nFrameNum[{2}]",
-                //attribute.imgAttr.width, attribute.imgAttr.height, attribute.frameID);
+                _tcs?.TrySetException(new Exception($"相机采集错误: {ex.Message}"));
             }
         }
 
@@ -566,16 +573,37 @@ namespace TeamAAS_VP.Core.Cameras
         /// <returns></returns>
         public bool SetExposureTime(float ExposureTime)
         {
-            //_operationSemaphore.Wait();
             try
             {
                 if (!mvCameraAcq.IsDeviceOpen())
                     return false;
+                string[] nodeName = new string[]{
+                "ExposureTime",
+                "ExposureTimeAbs",
+                "ExposureTimeRaw"};
+
                 uint nReVal = SciCam.SCI_CAMERA_OK;
-                nReVal = mvCameraAcq.SetIntValue("ExposureTime", (int)ExposureTime);
+                for (int i = 0; i < nodeName.Count(); i++)
+                {
+                    nReVal = mvCameraAcq.SetIntValue(nodeName[i], (int)ExposureTime);
+
+                    if (nReVal != SciCam.SCI_CAMERA_OK)
+                    {
+                        nReVal = mvCameraAcq.SetFloatValue(nodeName[i], ExposureTime);
+                        if (nReVal == SciCam.SCI_CAMERA_OK)
+                        {
+                            break;
+                        }
+                    }
+                    else
+                    {
+                        break;
+                    }
+                }
+
                 if (nReVal != SciCam.SCI_CAMERA_OK)
                 {
-                    mvCameraAcq.SetFloatValue("ExposureTime", ExposureTime);
+                    return false;
                 }
                 return true;
             }

+ 7 - 18
TeamAAS-VM/Core/Management.cs

@@ -130,17 +130,6 @@ namespace TeamAAS_VP.Core
             set { SetProperty(ref _Renders, value); }
         }
 
-        private ProductModel _CurrentProduct;
-
-        /// <summary>
-        /// 当前产品
-        /// </summary>
-        public ProductModel CurrentProduct
-        {
-            get { return _CurrentProduct; }
-            set { SetProperty(ref _CurrentProduct, value); }
-        }
-
         public BgCommunicate BgCommunicate { get; private set; }
 
         public BgModbusTcpCommunicate BgModbusTcpCommunicate { get; private set; }
@@ -1339,7 +1328,7 @@ namespace TeamAAS_VP.Core
 
                             // 执行视觉任务
                             var _cts = new CancellationTokenSource();
-                            var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, null, _cts.Token, null);
+                            var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, null, _cts.Token, cameraIndex, null);
                             if (visionResult.IsSucceed)
                             {
                                 //披头偏差
@@ -1406,7 +1395,7 @@ namespace TeamAAS_VP.Core
 
                             // 执行视觉任务
                             var _cts = new CancellationTokenSource();
-                            var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, null, _cts.Token, null);
+                            var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, null, _cts.Token, cameraIndex, null);
                             if (visionResult.IsSucceed)
                             {
                                 //披头偏差
@@ -1513,7 +1502,7 @@ namespace TeamAAS_VP.Core
 
                             // 执行视觉任务
                             var _cts = new CancellationTokenSource();
-                            var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, null);
+                            var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, cameraIndex, null);
                             if (visionResult.IsSucceed)
                             {
                                 //是拍一个锁一个还是拍两个点锁所有
@@ -2261,12 +2250,12 @@ namespace TeamAAS_VP.Core
         {
             try
             {
-                if (CurrentProduct == null) return;
                 //获取当前产品
                 var currentproduct = _productService.GetCurrentProduct();
-                TotalQuantity = await _systemDatabaseService.GetOverallProductionAsync(CurrentProduct.Name);
-                TotalQuantityToday = await _systemDatabaseService.GetTodayProductionAsync(CurrentProduct.Name);
-                CurrentUPH = await _systemDatabaseService.GetLastHourProductionAsync(CurrentProduct.Name);
+                if (currentproduct == null) return;
+                TotalQuantity = await _systemDatabaseService.GetOverallProductionAsync(currentproduct.Name);
+                TotalQuantityToday = await _systemDatabaseService.GetTodayProductionAsync(currentproduct.Name);
+                CurrentUPH = await _systemDatabaseService.GetLastHourProductionAsync(currentproduct.Name);
             }
             catch (Exception)
             {

+ 7 - 2
TeamAAS-VM/Core/ScriptHelper.cs

@@ -113,13 +113,16 @@ namespace TeamAAS_VP.Core
         {
             try
             {
+                //获取
+                IProductService productService = container.Resolve<IProductService>();
+
                 var eval = CSScript.Evaluator.ReferenceDomainAssemblies(DomainAssemblies.AllStatic);
                 IBackgroundScripting scripting = eval.LoadCode<IBackgroundScripting>(strSourceCode);
                 scripting.regionManager = regionManager;
                 scripting.eventAggregator = ea;
                 scripting.container = container;
                 scripting.management = container.Resolve<Management>();
-                scripting.CurrentProduct = scripting.management.CurrentProduct;
+                scripting.CurrentProduct = productService.GetCurrentProduct();
                 return scripting.Execute();
             }
             catch (CSScriptLib.CompilerException e1)
@@ -160,13 +163,15 @@ namespace TeamAAS_VP.Core
         {
             try
             {
+                IProductService productService = container.Resolve<IProductService>();
+
                 var eval = CSScript.Evaluator.ReferenceDomainAssemblies(DomainAssemblies.AllStatic);
                 IVisionScripting scripting = eval.LoadCode<IVisionScripting>(strSourceCode);
                 scripting.regionManager = regionManager;
                 scripting.eventAggregator = ea;
                 scripting.container= container;
                 scripting.management= container.Resolve<Management>();
-                scripting.CurrentProduct = scripting.management.CurrentProduct;
+                scripting.CurrentProduct = productService.GetCurrentProduct();
                 await scripting.Execute(procedure, items, rst =>
                 {
                     callback.Invoke(rst);

+ 29 - 5
TeamAAS-VM/Data/SystemDatabaseService.cs

@@ -178,9 +178,9 @@ namespace TeamAAS_VP.Data
             ////如果没有登录记录则返回一个默认的用户,即第一个用户
             //if (rec == null) return _db.Queryable<User>().First();
             //return _db.Queryable<User>().InSingle(rec.UserId);
-            if (CurrentUser==null)
+            if (CurrentUser == null)
             {
-                CurrentUser= _db.Queryable<User>().ToList().FirstOrDefault();
+                CurrentUser = _db.Queryable<User>().ToList().FirstOrDefault();
             }
             return CurrentUser;
         }
@@ -402,7 +402,7 @@ namespace TeamAAS_VP.Data
             var todayStart = DateTime.UtcNow.Date;
             //今天的结束时间
             var todayEnd = DateTime.UtcNow.Date;
-            todayEnd= todayEnd.AddDays(1);
+            todayEnd = todayEnd.AddDays(1);
             //按照产品名称和时间范围查询生产记录的总数
             var total = await _db.Queryable<ProductionRecord>()
                 .Where(r => r.ProductName == productName && r.Timestamp >= todayStart && r.Timestamp < todayEnd)
@@ -417,7 +417,7 @@ namespace TeamAAS_VP.Data
         /// <returns></returns>
         public async Task<int> GetLastHourProductionAsync(string productName)
         {
-                        var lastHourStart = DateTime.UtcNow.AddHours(-1);
+            var lastHourStart = DateTime.UtcNow.AddHours(-1);
             var lastHourEnd = DateTime.UtcNow;
             //按照产品名称和时间范围查询生产记录的总数
             var total = await _db.Queryable<ProductionRecord>()
@@ -680,7 +680,7 @@ namespace TeamAAS_VP.Data
         /// <summary>
         /// 查询所有符合条件的锁付结果(不分页)。
         /// </summary>
-        public async Task<IEnumerable<LockResult>> QueryAllLockResultsAsync(DateTime start, DateTime end, string productNumber = null, string productName= null, int? screwNumber = null, bool? lockPassed = null)
+        public async Task<IEnumerable<LockResult>> QueryAllLockResultsAsync(DateTime start, DateTime end, string productNumber = null, string productName = null, int? screwNumber = null, bool? lockPassed = null)
         {
             var q = _db.Queryable<LockResult>().Where(r => r.Timestamp >= start && r.Timestamp < end);
             if (!string.IsNullOrEmpty(productNumber)) q = q.Where(r => r.ProductNumber == productNumber);
@@ -1086,6 +1086,30 @@ namespace TeamAAS_VP.Data
                 await RecordLockResultAsync(result);
             }
 
+            //生成一些相机拍照记录的测试数据
+            for (int i = 0; i < 20; i++)
+            {
+                try
+                {
+                    var record = new PhotoCaptureRecord
+                    {
+                        Id = Guid.NewGuid(),
+                        RecipeName = "产品1",
+                        CameraName = $"下相机",
+                        ProductSN = $"SN-{i}",
+                        IsSuccess = (i % 2 == 0),
+                        CaptureTime = DateTime.UtcNow.AddMinutes(-i * 7),
+                        ImagePath = $"/images/photo_{i}.jpg"
+                    };
+                    await RecordPhotoCaptureAsync(record);
+                }
+                catch (Exception ex)
+                {
+                    Console.WriteLine(ex.ToString());
+                }
+
+            }
+
         }
 
         #endregion

+ 3 - 1
TeamAAS-VM/Interfaces/IRemoteCommandService.cs

@@ -255,8 +255,10 @@ namespace TeamAAS_VP.Interfaces
         /// <param name="InputTerminal"></param>
         /// <param name="robotCoord"></param>
         /// <param name="cancellationToken"></param>
+        /// <param name="positionIndex"></param>
+        /// <param name="remark"></param>
         /// <returns></returns>
-        Task<(bool IsSucceed, double X, double Y, double U)> ExecutePhotoGetSinglePoint(ProcedureModel procedure, Dictionary<string, string> InputTerminal, double[] robotCoord, CancellationToken cancellationToken, string remark);
+        Task<(bool IsSucceed, double X, double Y, double U)> ExecutePhotoGetSinglePoint(ProcedureModel procedure, Dictionary<string, string> InputTerminal, double[] robotCoord, CancellationToken cancellationToken, int positionIndex, string remark);
 
         /// <summary>
         /// 执行相机拍照获取单个点位结果,并且返回图像和图形

+ 8 - 0
TeamAAS-VM/Models/PhotoCaptureRecord.cs

@@ -38,6 +38,11 @@ namespace TeamAAS_VP.Models
         [SugarColumn(Length = 255, IsNullable = true)]
         public string ProcessName { get; set; }
 
+        /// <summary>
+        /// 位置编号
+        /// </summary>
+        public int PositionIndex { get; set; }
+
         /// <summary>
         /// 是否成功
         /// </summary>
@@ -46,16 +51,19 @@ namespace TeamAAS_VP.Models
         /// <summary>
         /// 机器人拍照位置
         /// </summary>
+        [SugarColumn(Length = 255, IsNullable = true)]
         public string RobotPosition { get; set; }
 
         /// <summary>
         /// 像素坐标
         /// </summary>
+        [SugarColumn(Length = 255, IsNullable = true)]
         public string PixelPosition { get; set; }
 
         /// <summary>
         /// 绝对坐标
         /// </summary>
+        [SugarColumn(Length = 255, IsNullable = true)]
         public string AbsolutePosition { get; set; }
 
         /// <summary>

+ 2 - 2
TeamAAS-VM/Properties/AssemblyInfo.cs

@@ -51,5 +51,5 @@ using System.Windows;
 //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
 //通过使用 "*",如下所示:
 // [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.2.0.7")]
-[assembly: AssemblyFileVersion("1.2.0.7")]
+[assembly: AssemblyVersion("1.2.0.8")]
+[assembly: AssemblyFileVersion("1.2.0.8")]

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

@@ -1765,8 +1765,10 @@ namespace TeamAAS_VP.Services
         /// <param name="InputTerminal"></param>
         /// <param name="robotCoord"></param>
         /// <param name="cancellationToken"></param>
+        /// <param name="positionIndex"></param>
+        /// <param name="remark"></param>
         /// <returns></returns>
-        public async Task<(bool IsSucceed, double X, double Y, double U)> ExecutePhotoGetSinglePoint(ProcedureModel procedure, Dictionary<string, string> InputTerminal, double[] robotCoord, CancellationToken cancellationToken, string remark)
+        public async Task<(bool IsSucceed, double X, double Y, double U)> ExecutePhotoGetSinglePoint(ProcedureModel procedure, Dictionary<string, string> InputTerminal, double[] robotCoord, CancellationToken cancellationToken, int positionIndex, string remark)
         {
             DateTime nowtime = DateTime.Now;
             await SetLightBeforePhoto(procedure);
@@ -1788,7 +1790,7 @@ namespace TeamAAS_VP.Services
                     if (!outputCollection.Contains("Found"))
                     {
                         SendTaskMessage(Lang.未找到视觉输出结果, MessageLevel.Error);
-                        await RecordPhotoCaptureAsync(procedure, robotCoord, false, new double[] { 0, 0, 0 }, new double[] { 0, 0, 0 }, string.Empty, remark);
+                        await RecordPhotoCaptureAsync(procedure, robotCoord, false, new double[] { 0, 0, 0 }, new double[] { 0, 0, 0 }, string.Empty, positionIndex, remark);
                         return (false, 0, 0, 0);
                     }
 
@@ -1817,20 +1819,20 @@ namespace TeamAAS_VP.Services
                     var calibResult = _calibrationService.ConvertPixelToPosition((_pixel_x, _pixel_y, _pixel_u), robotCoord, calib, RobotBrand.XYZ_Platform);
                     if (!calibResult.IsSucceed)
                     {
-                        await RecordPhotoCaptureAsync(procedure, robotCoord, false, new double[] { _pixel_x, _pixel_y, _pixel_u }, new double[] { 0, 0, 0 }, string.Empty, remark);
+                        await RecordPhotoCaptureAsync(procedure, robotCoord, false, new double[] { _pixel_x, _pixel_y, _pixel_u }, new double[] { 0, 0, 0 }, string.Empty, positionIndex, remark);
                         return (false, 0, 0, 0);
                     }
-                    await RecordPhotoCaptureAsync(procedure, robotCoord,true, new double[] { _pixel_x, _pixel_y, _pixel_u }, new double[] { calibResult.X, calibResult.Y, calibResult.U }, string.Empty, remark);
+                    await RecordPhotoCaptureAsync(procedure, robotCoord,true, new double[] { _pixel_x, _pixel_y, _pixel_u }, new double[] { calibResult.X, calibResult.Y, calibResult.U }, string.Empty, positionIndex, remark);
                     return (true, calibResult.X, calibResult.Y, calibResult.U);
                 }
                 SendTaskMessage(Lang.多次拍照失败, MessageLevel.Alarm);
-                await RecordPhotoCaptureAsync(procedure, robotCoord, false, new double[] { 0, 0, 0 }, new double[] { 0, 0, 0 }, string.Empty, remark);
+                await RecordPhotoCaptureAsync(procedure, robotCoord, false, new double[] { 0, 0, 0 }, new double[] { 0, 0, 0 }, string.Empty, positionIndex, remark);
                 return (false, 0, 0, 0);
             }
             catch (Exception ex)
             {
                 LogHelper.WriteLogError(Lang.执行相机取图并获取单点位时出错, ex);
-                await RecordPhotoCaptureAsync(procedure, robotCoord, false, new double[] { 0, 0, 0 }, new double[] { 0, 0, 0 }, string.Empty, remark);
+                await RecordPhotoCaptureAsync(procedure, robotCoord, false, new double[] { 0, 0, 0 }, new double[] { 0, 0, 0 }, string.Empty, positionIndex, remark);
                 return (false, 0, 0, 0);
             }
             finally
@@ -1849,9 +1851,10 @@ namespace TeamAAS_VP.Services
         /// <param name="pixelPosition"></param>
         /// <param name="absolutePosition"></param>
         /// <param name="imagePath"></param>
+        /// <param name="positionIndex"></param>
         /// <param name="emarks"></param>
         /// <returns></returns>
-        private async Task RecordPhotoCaptureAsync(ProcedureModel procedure, double[] robotCoord, bool isSuccess, double[] pixelPosition, double[] absolutePosition, string imagePath, string emarks)
+        private async Task RecordPhotoCaptureAsync(ProcedureModel procedure, double[] robotCoord, bool isSuccess, double[] pixelPosition, double[] absolutePosition, string imagePath, int positionIndex, string emarks)
         {
             try
             {

+ 3 - 2
TeamAAS-VM/ViewModels/HomeViewModel.cs

@@ -403,7 +403,8 @@ namespace TeamAAS_VP.ViewModels
         /// </summary>
         async void ExecuteResetCounterCommand()
         {
-            if (management.CurrentProduct == null) return;
+            var currentProduct =_productService.GetCurrentProduct();
+            if (currentProduct == null) return;
             var view = new ShowMessage(Lang.计数清零, Lang.是否重置当前产品的计数);
             //show the dialog
             var result = await DialogHost.Show(view, "RootDialog", null, null, null);
@@ -412,7 +413,7 @@ namespace TeamAAS_VP.ViewModels
                 if (((bool)result))
                 {
                     isCanExecute = false;
-                    await _systemDatabaseService.ResetProductionAsync(management.CurrentProduct.Name);
+                    await _systemDatabaseService.ResetProductionAsync(currentProduct.Name);
                     isCanExecute = true;
                 }
             }

+ 8 - 1
TeamAAS-VM/ViewModels/Statistics/NozzleChangeRecordQueryViewModel.cs

@@ -44,7 +44,14 @@ namespace TeamAAS_VP.ViewModels.Statistics
             get { return _NozzleBatchNumber; }
             set { SetProperty(ref _NozzleBatchNumber, value); }
         }
-        public ObservableCollection<NozzleChangeRecord> Results { get; set; } = new ObservableCollection<NozzleChangeRecord>();
+
+        private ObservableCollection<NozzleChangeRecord> _Results;
+        public ObservableCollection<NozzleChangeRecord> Results
+        {
+            get { return _Results; }
+            set { SetProperty(ref _Results, value); }
+        }
+        
         private int _PageIndex;
         public int PageIndex
         {

+ 29 - 4
TeamAAS-VM/ViewModels/Statistics/PhotoCaptureQueryViewModel.cs

@@ -20,7 +20,7 @@ namespace TeamAAS_VP.ViewModels.Statistics
         private readonly IProductService _productService;
         private readonly IConfigService _configService;
 
-        public PhotoCaptureQueryViewModel(ISystemDatabaseService systemDatabaseService, ProductService productService, IConfigService configService)
+        public PhotoCaptureQueryViewModel(ISystemDatabaseService systemDatabaseService, IProductService productService, IConfigService configService)
         {
             _systemDatabaseService = systemDatabaseService;
             _productService = productService;
@@ -118,7 +118,7 @@ namespace TeamAAS_VP.ViewModels.Statistics
         void ExecuteLoadedCommand()
         {
             AllProduct = new ObservableCollection<ProductModel>(_productService.GetAllProducts());
-            _AllCamera = new ObservableCollection<CameraInfo>(_configService.GetAllCameras());
+            AllCamera = new ObservableCollection<CameraInfo>(_configService.GetAllCameras());
         }
 
         private async Task ExecuteSearchCommand()
@@ -150,10 +150,25 @@ namespace TeamAAS_VP.ViewModels.Statistics
                 var path = dlg.FileName;
                 using (var sw = new StreamWriter(path, false, Encoding.UTF8))
                 {
-                    sw.WriteLine("CaptureTime,RecipeName,CameraName,ProductSN,Result,ImagePath");
+                    sw.WriteLine("Id,CaptureTime,RecipeName,ProcessName,CameraName,ProductSN,PositionIndex,IsSuccess,RobotPosition,PixelPosition,AbsolutePosition,ImagePath,OperatorName,Remarks");
                     foreach (var r in all)
                     {
-                        sw.WriteLine($"{r.CaptureTime:yyyy-MM-dd HH:mm:ss},{r.RecipeName},{r.CameraName},{r.ProductSN},{r.IsSuccess},{r.ImagePath}");
+                        sw.WriteLine(string.Join(",",
+                            EscapeCsv(r.Id.ToString()),
+                            EscapeCsv(r.CaptureTime.ToString("yyyy-MM-dd HH:mm:ss")),
+                            EscapeCsv(r.RecipeName),
+                            EscapeCsv(r.ProcessName),
+                            EscapeCsv(r.CameraName),
+                            EscapeCsv(r.ProductSN),
+                            EscapeCsv(r.PositionIndex.ToString()),
+                            EscapeCsv(r.IsSuccess ? "1" : "0"),
+                            EscapeCsv(r.RobotPosition),
+                            EscapeCsv(r.PixelPosition),
+                            EscapeCsv(r.AbsolutePosition),
+                            EscapeCsv(r.ImagePath),
+                            EscapeCsv(r.OperatorName),
+                            EscapeCsv(r.Remarks)
+                        ));
                     }
                 }
             }
@@ -162,5 +177,15 @@ namespace TeamAAS_VP.ViewModels.Statistics
                 MessageBox.Show(ex.Message, "´íÎó", MessageBoxButton.OK, MessageBoxImage.Error);
             }
         }
+
+        private string EscapeCsv(string s)
+        {
+            if (s == null) return "";
+            if (s.Contains(",") || s.Contains("\"") || s.Contains("\r") || s.Contains("\n"))
+            {
+                return "\"" + s.Replace("\"", "\"\"") + "\"";
+            }
+            return s;
+        }
     }
 }

+ 28 - 8
TeamAAS-VM/ViewModels/Statistics/ProductionRecordQueryViewModel.cs

@@ -2,6 +2,7 @@ using Prism.Commands;
 using Prism.Mvvm;
 using System;
 using System.Collections.ObjectModel;
+using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Text;
@@ -108,19 +109,27 @@ namespace TeamAAS_VP.ViewModels.Statistics
         {
             try
             {
-                var start = StartDate.HasValue ? StartDate.Value.Date : DateTime.MinValue;
-                var end = EndDate.HasValue ? EndDate.Value.Date.AddDays(1) : DateTime.MaxValue;
-                string productName = SelectedProduct?.Name;
-                var resAll = await _systemDatabaseService.GetProductionStatsAsync(new ProductionStatQuery { ProductName = productName, Start = start, End = end, GroupBy = "total" });
-                var saveFileDialog = new Microsoft.Win32.SaveFileDialog { FileName = $"Production_{DateTime.Now:yyyyMMddHHmmss}.csv", DefaultExt = ".csv", Filter = "CSV Files (*.csv)|*.csv" };
+                // Export current page Results (paged) to CSV including all ProductionRecord fields
+                var saveFileDialog = new Microsoft.Win32.SaveFileDialog { FileName = $"ProductionRecords_{DateTime.Now:yyyyMMddHHmmss}.csv", DefaultExt = ".csv", Filter = "CSV Files (*.csv)|*.csv" };
                 if (saveFileDialog.ShowDialog() != true) return;
                 var path = saveFileDialog.FileName;
                 using (var sw = new StreamWriter(path, false, Encoding.UTF8))
                 {
-                    sw.WriteLine("PeriodStart,PeriodEnd,Quantity,ProductName");
-                    foreach (var r in resAll)
+                    sw.WriteLine("Id,Timestamp,ProductName,ProductCode,DeviceId,DeviceName,Category,Quantity,UserName,Remark");
+                    foreach (var r in Results)
                     {
-                        sw.WriteLine($"{r.PeriodStart:yyyy-MM-dd HH:mm:ss},{r.PeriodEnd:yyyy-MM-dd HH:mm:ss},{r.Quantity},{r.ProductName}");
+                        sw.WriteLine(string.Join(",",
+                            EscapeCsv(r.Id.ToString()),
+                            EscapeCsv(r.Timestamp.ToString("yyyy-MM-dd HH:mm:ss")),
+                            EscapeCsv(r.ProductName),
+                            EscapeCsv(r.ProductCode),
+                            EscapeCsv(r.DeviceId.ToString()),
+                            EscapeCsv(r.DeviceName),
+                            EscapeCsv(r.Category),
+                            EscapeCsv(r.Quantity.ToString()),
+                            EscapeCsv(r.UserName),
+                            EscapeCsv(r.Remark)
+                        ));
                     }
                 }
             }
@@ -128,6 +137,7 @@ namespace TeamAAS_VP.ViewModels.Statistics
             {
                 MessageBox.Show(ex.Message, "´íÎó", MessageBoxButton.OK, MessageBoxImage.Error);
             }
+            await Task.CompletedTask;
         }
 
         private async void LoadCategoriesForProduct(string productName)
@@ -143,5 +153,15 @@ namespace TeamAAS_VP.ViewModels.Statistics
             }
             catch { }
         }
+
+        private string EscapeCsv(string s)
+        {
+            if (s == null) return "";
+            if (s.Contains(",") || s.Contains("\"") || s.Contains("\r") || s.Contains("\n"))
+            {
+                return "\"" + s.Replace("\"", "\"\"") + "\"";
+            }
+            return s;
+        }
     }
 }

+ 15 - 13
TeamAAS-VM/ViewModels/Statistics/ProductionStatementViewModel.cs

@@ -14,6 +14,7 @@ using TeamAAS_VP.Data;
 using TeamAAS_VP.Interfaces;
 using TeamAAS_VP.Models;
 using TeamAAS_VP.Resources.Languages;
+using TeamAAS_VP.Services;
 using static MaterialDesignThemes.Wpf.Theme.ToolBar;
 
 namespace TeamAAS_VP.ViewModels.Statistics
@@ -22,6 +23,7 @@ namespace TeamAAS_VP.ViewModels.Statistics
     {
         IContainerProvider _container;
         ISystemDatabaseService _systemDatabaseService;
+        IProductService _productService;
 
         Management management;
 
@@ -119,27 +121,27 @@ namespace TeamAAS_VP.ViewModels.Statistics
 
         #endregion
 
-        public ProductionStatementViewModel(IContainerProvider container, ISystemDatabaseService systemDatabaseService)
+        public ProductionStatementViewModel(IContainerProvider container, ISystemDatabaseService systemDatabaseService, IProductService productService)
         {
             _container= container;
             _systemDatabaseService = systemDatabaseService;
+            _productService = productService;
         }
 
         #region 方法
         async void ExecuteLoadedCommand()
         {
-            if (management == null)
-                management = _container.Resolve<Management>();
-            if (management.CurrentProduct == null) return;
-           await  App.Current.Dispatcher.InvokeAsync(new Action(async () => {
-                YieldChartModel = CreateYieldChartModel(await _systemDatabaseService.GetProductionByCategoriesAsync(management.CurrentProduct.Name));
-               TotalCount = await _systemDatabaseService.GetOverallProductionAsync(management.CurrentProduct.Name);
-                MonthCount = await _systemDatabaseService.GetCurrentMonthProductionAsync(management.CurrentProduct.Name);
-               WeekCount = await _systemDatabaseService.GetCurrentWeekProductionAsync(management.CurrentProduct.Name);
-               DayCount = await _systemDatabaseService.GetTodayProductionAsync(management.CurrentProduct.Name);
-               MonthChartModel = CreateMonth(await _systemDatabaseService.GetMonthlyProductionByDayAndCategoryAsync(management.CurrentProduct.Name));
-                WeekChartModel = CreateWeek(await _systemDatabaseService.GetWeeklyProductionByDayAndCategoryAsync(management.CurrentProduct.Name));
-                DayChartModel = CreateDay(await _systemDatabaseService.GetDailyProductionByHourAndCategoryAsync(management.CurrentProduct.Name));
+            var currentproduct = _productService.GetCurrentProduct();
+            if (currentproduct == null) return;
+            await  App.Current.Dispatcher.InvokeAsync(new Action(async () => {
+                YieldChartModel = CreateYieldChartModel(await _systemDatabaseService.GetProductionByCategoriesAsync(currentproduct.Name));
+               TotalCount = await _systemDatabaseService.GetOverallProductionAsync(currentproduct.Name);
+                MonthCount = await _systemDatabaseService.GetCurrentMonthProductionAsync(currentproduct.Name);
+               WeekCount = await _systemDatabaseService.GetCurrentWeekProductionAsync(currentproduct.Name);
+               DayCount = await _systemDatabaseService.GetTodayProductionAsync(currentproduct.Name);
+               MonthChartModel = CreateMonth(await _systemDatabaseService.GetMonthlyProductionByDayAndCategoryAsync(currentproduct.Name));
+               WeekChartModel = CreateWeek(await _systemDatabaseService.GetWeeklyProductionByDayAndCategoryAsync(currentproduct.Name));
+               DayChartModel = CreateDay(await _systemDatabaseService.GetDailyProductionByHourAndCategoryAsync(currentproduct.Name));
             }));
             
         }

+ 58 - 7
TeamAAS-VM/Views/Statistics/PhotoCaptureQuery.xaml

@@ -83,18 +83,69 @@
                   CanUserAddRows="False"
                   IsReadOnly="True">
             <DataGrid.Columns>
+                <DataGridTextColumn Header="Id"
+                                    Binding="{Binding Id}"
+                                    Width="50" />
                 <DataGridTextColumn Header="CaptureTime"
-                                    Binding="{Binding CaptureTime, StringFormat=yyyy-MM-dd HH:mm:ss}" />
-                <DataGridTextColumn Header="RecipeName"
+                                    Binding="{Binding CaptureTime, StringFormat=yyyy-MM-dd HH:mm:ss}"/>
+                <DataGridTextColumn Header="配方名"
                                     Binding="{Binding RecipeName}" />
-                <DataGridTextColumn Header="CameraName"
+                <DataGridTextColumn Header="流程名"
+                                    Binding="{Binding ProcessName}" />
+                <DataGridTextColumn Header="相机名"
                                     Binding="{Binding CameraName}" />
-                <DataGridTextColumn Header="ProductSN"
+                <DataGridTextColumn Header="产品SN"
                                     Binding="{Binding ProductSN}" />
-                <DataGridTextColumn Header="Result"
-                                    Binding="{Binding Result}" />
-                <DataGridTextColumn Header="ImagePath"
+                <DataGridTextColumn Header="拍照编号"
+                                    Binding="{Binding PositionIndex}" />
+                <!-- Changed to TemplateColumn to show OK/NG with color -->
+                <DataGridTemplateColumn Header="是否成功"
+                                        Width="85">
+                    <DataGridTemplateColumn.CellTemplate>
+                        <DataTemplate>
+                            <Border CornerRadius="4"
+                                    Padding="2"
+                                    HorizontalAlignment="Center"
+                                    VerticalAlignment="Center">
+                                <TextBlock HorizontalAlignment="Center"
+                                           VerticalAlignment="Center"
+                                           Foreground="White"
+                                           FontWeight="Bold"
+                                           TextAlignment="Center">
+                                    <TextBlock.Style>
+                                        <Style TargetType="TextBlock">
+                                            <Setter Property="Text"
+                                                    Value="OK" />
+                                            <Setter Property="Background"
+                                                    Value="Green" />
+                                            <Style.Triggers>
+                                                <DataTrigger Binding="{Binding IsSuccess}"
+                                                             Value="False">
+                                                    <Setter Property="Text"
+                                                            Value="NG" />
+                                                    <Setter Property="Background"
+                                                            Value="Red" />
+                                                </DataTrigger>
+                                            </Style.Triggers>
+                                        </Style>
+                                    </TextBlock.Style>
+                                </TextBlock>
+                            </Border>
+                        </DataTemplate>
+                    </DataGridTemplateColumn.CellTemplate>
+                </DataGridTemplateColumn>
+                <DataGridTextColumn Header="机器人位置"
+                                    Binding="{Binding RobotPosition}" />
+                <DataGridTextColumn Header="像素坐标"
+                                    Binding="{Binding PixelPosition}"/>
+                <DataGridTextColumn Header="绝对坐标"
+                                    Binding="{Binding AbsolutePosition}" />
+                <DataGridTextColumn Header="图片路径"
                                     Binding="{Binding ImagePath}" />
+                <DataGridTextColumn Header="操作员"
+                                    Binding="{Binding OperatorName}"/>
+                <DataGridTextColumn Header="备注"
+                                    Binding="{Binding Remarks}" />
             </DataGrid.Columns>
         </DataGrid>
         <StackPanel Grid.Row="3"

+ 9 - 0
TeamAAS-VM/Views/Statistics/ProductionRecordQuery.xaml

@@ -79,8 +79,17 @@
             <DataGrid.Columns>
                 <DataGridTextColumn Header="时间"
                                     Binding="{Binding Timestamp, StringFormat=yyyy-MM-dd HH:mm:ss}" />
+                <DataGridTextColumn Header="Id"
+                                    Binding="{Binding Id}"
+                                    Width="50" />
                 <DataGridTextColumn Header="产品"
                                     Binding="{Binding ProductName}" />
+                <DataGridTextColumn Header="产品编码"
+                                    Binding="{Binding ProductCode}" />
+                <DataGridTextColumn Header="设备Id"
+                                    Binding="{Binding DeviceId}" />
+                <DataGridTextColumn Header="设备名称"
+                                    Binding="{Binding DeviceName}" />
                 <DataGridTextColumn Header="类别"
                                     Binding="{Binding Category}" />
                 <DataGridTextColumn Header="数量"