Просмотр исходного кода

优化产能图标,增加相机检测数据,增加屏幕偏移检测

zly 3 месяцев назад
Родитель
Сommit
93891224a5

+ 158 - 78
TeamAAS-VM/Core/Management.cs

@@ -88,6 +88,8 @@ namespace TeamAAS_VP.Core
         /// 当天是否上传过csv
         /// </summary>
         private bool _hasCompressedToday = false;
+
+
         #endregion
 
         #region 属性
@@ -239,12 +241,17 @@ namespace TeamAAS_VP.Core
             get { return _Yield; }
             set { SetProperty(ref _Yield, value); }
         }
+
+
         private ObservableCollection<ProductModel> _ProductList;
         public ObservableCollection<ProductModel> ProductList
         {
             get { return _ProductList; }
             set { SetProperty(ref _ProductList, value); }
         }
+
+        public static int uphhour=0;
+        public static int uphnumber=0;
         #endregion
 
         #region 命令
@@ -1099,7 +1106,7 @@ namespace TeamAAS_VP.Core
         /// </summary>
         /// <returns></returns>
         public async Task WritePositionToPlcAsync()
-        {
+        {            
             var addressConfig = _configService.GetPlcAddresses();
             var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
             if (plc == null || !plc.IsConnected) return;
@@ -1155,6 +1162,7 @@ namespace TeamAAS_VP.Core
                 {
                     await item.WriteToPlcAddress(addressConfig.Out_RemovePos, plc);
                 }
+                await plc.WriteNodeAsync(addressConfig.Out_RemovePos_Num.Address, (Int16)current.RemovePoints.Count);
             }
             if (current.PressurePlacePoints != null && current.PressurePlacePoints.Count > 0)
             {
@@ -1277,6 +1285,8 @@ namespace TeamAAS_VP.Core
         /// 复检结果
         /// </summary>
         Dictionary<string, string> gapDict = new Dictionary<string, string>();
+
+        List<string> fileresult { get; set; } = new List<string>();
         /// <summary>
         /// 二次定位相机的工具坐标
         /// </summary>
@@ -1371,6 +1381,7 @@ namespace TeamAAS_VP.Core
 
                             }
                             var currentPosition = robot.GetRobotPos();
+                            var current = _productService.GetCurrentProduct();
 
                             //如果下相机拍照的次数是1,则判断用时拍照的相机是几个
                             int downCameraNum = _configService.GetSystemConfiguration().DowmCameraNum;
@@ -1452,6 +1463,13 @@ namespace TeamAAS_VP.Core
                                     {
                                         float toolX = (float)(visionResults[0].X + visionResults[1].X) / 2;
                                         float toolY = (float)(visionResults[0].Y + visionResults[1].Y) / 2;
+
+                                        float deltaX = (float)(visionResults[1].X - visionResults[0].X);
+                                        float deltaY = (float)(visionResults[1].Y - visionResults[0].Y);
+                                        float distance = (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
+
+                                        SendTaskMessage($"屏幕长度: {distance:F4}", MessageLevel.Info);
+
                                         _FixedUpCameraAngle = Common.CalculateAngleBetweenPointsDegrees(new PointF((float)visionResults[0].X, (float)visionResults[0].Y), new PointF((float)visionResults[1].X, (float)visionResults[1].Y));
                                         SendTaskMessage($"屏幕中心点位:{toolX:F3},{toolY:F4},{_FixedUpCameraAngle:F4}", MessageLevel.Info);
                                         SendTaskMessage($"当前机器人点位:{currentPosition.X:F3},{currentPosition.Y:F4},{currentPosition.U:F4}", MessageLevel.Info);
@@ -1460,6 +1478,22 @@ namespace TeamAAS_VP.Core
                                         //以LCD的的上边中心创建工具坐标
                                         DownTool.ComputeTool(currentPosition.X, currentPosition.Y, currentPosition.U, DowmCameraResults[0].X, DowmCameraResults[0].Y);
                                         SendTaskMessage($"屏幕Tool:{DownTool.X:F3},{DownTool.Y:F4}", MessageLevel.Info);
+
+                                        if (current.Reference_coordinate.Count > 0)
+                                        {
+                                            float offsetX = toolX - current.Reference_coordinate[0].X_Position;
+                                            float offsetY = toolY - current.Reference_coordinate[0].Y_Position;
+                                            float offsetAngle = (float)_FixedUpCameraAngle - current.Reference_coordinate[0].U_Position;
+                                            SendTaskMessage($"屏幕偏移量:X:{offsetX},Y:{offsetY},U:{offsetAngle}", MessageLevel.Debug);
+
+                                            foreach (var item in current.RemovePoints)
+                                            {
+                                                item.X_Position += offsetX;
+                                                item.Y_Position += offsetY;
+                                                item.U_Position += offsetAngle;
+                                            }
+                                        }                                                                               
+
                                         await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1);
                                         return;
                                     }
@@ -1601,45 +1635,6 @@ namespace TeamAAS_VP.Core
                             //相机的编号
                             Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_DowmCameraNum.Address);
                             SendTaskMessage($"编号:{cameraIndex}", MessageLevel.Info);
-
-                            // 获取流程
-                            //var process = _productService.GetCurrentProductProcedureModelById(currentProduct.FixedUpCamera1ProcedureId);
-                            //if (process == null)
-                            //{
-                            //    SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.FixedUpCamera1ProcedureId}", MessageLevel.Alarm);
-                            //    await plc.WriteNodeAsync(addressConfig.Out_ScrewTeachOk.Address, (Int16)2);
-                            //    return;
-                            //}
-
-                            //// 获取机器人当前坐标
-                            //var robot = _robotService.GetAllRobots().FirstOrDefault();
-                            //if (robot == null)
-                            //{
-                            //    SendTaskMessage($"未找到机器人,无法执行上相机锁附/组装拍照视觉任务", MessageLevel.Alarm);
-                            //    await plc.WriteNodeAsync(addressConfig.Out_ScrewTeachOk.Address, (Int16)2);
-                            //    return;
-
-                            //}
-                            //var currentPosition = robot.GetRobotPos();
-
-                            //// 执行视觉任务
-                            //var _cts = new CancellationTokenSource();
-                            //var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, null, _cts.Token);
-                            //if (visionResult.IsSucceed)
-                            //{
-                            //    //披头偏差
-                            //    float offsetX = (float)(visionResult.X - currentPosition.X);
-                            //    float offsetY = (float)(visionResult.Y - currentPosition.Y);
-
-                            //    DowmCameraResults[cameraIndex] = new PointF(offsetX, offsetY);
-                            //    await plc.WriteNodeAsync(addressConfig.Out_ScrewTeachOk.Address, (Int16)1);
-                            //    return;
-                            //}
-                            //else
-                            //{
-                            //    await plc.WriteNodeAsync(addressConfig.Out_ScrewTeachOk.Address, (Int16)2);
-                            //    return;
-                            //}
                         }
                         else
                         {
@@ -2336,35 +2331,29 @@ namespace TeamAAS_VP.Core
                                 gapDict.Add("Gap2", visionResults[1].distance.Split(',')[1]);
                                 gapDict.Add("Gap3", visionResults[2].distance.Split(',')[1]);
                                 gapDict.Add("Gap4", visionResults[3].distance.Split(',')[1]);
+                                gapDict.Add("Gap5", visionResults[1].distance.Split(',')[2]);
+                                gapDict.Add("Gap6", visionResults[3].distance.Split(',')[2]);
                                 //gapString = $"{visionResults[0].distance.Split(',')[1]},{visionResults[1].distance.Split(',')[1]},{visionResults[2].distance.Split(',')[1]},{visionResults[3].distance.Split(',')[1]}";
                                 await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)1);
                                 return;
                             }
                             else
                             {
-                                string res1 = "", res2 = "", res3 = "", res4 = "";
-                                if (visionResults[0].IsSucceed)
-                                {
-                                    res1 = visionResults[0].distance.Split(',')[1];
-                                }
-                                if (visionResults[1].IsSucceed)
-                                {
-                                    res2 = visionResults[1].distance.Split(',')[1];
-                                }
-                                if (visionResults[2].IsSucceed)
-                                {
-                                    res3 = visionResults[2].distance.Split(',')[1];
-                                }
-                                if (visionResults[3].IsSucceed)
-                                {
-                                    res4 = visionResults[3].distance.Split(',')[1];
-                                }
+                                string res1 = "", res2 = "", res3 = "", res4 = "", res5 = "", res6 = "";
+                                res1 = visionResults[0].distance.Split(',')[1];
+                                res2 = visionResults[1].distance.Split(',')[1];
+                                res3 = visionResults[2].distance.Split(',')[1];
+                                res4 = visionResults[3].distance.Split(',')[1];
+                                res5 = visionResults[1].distance.Split(',')[2];
+                                res6 = visionResults[3].distance.Split(',')[2];
                                 SendTaskMessage($"固定相机检测NG", MessageLevel.Error);
                                 gapDict.Clear();
                                 gapDict.Add("Gap1", res1);
                                 gapDict.Add("Gap2", res2);
                                 gapDict.Add("Gap3", res3);
                                 gapDict.Add("Gap4", res4);
+                                gapDict.Add("Gap5", res5);
+                                gapDict.Add("Gap6", res6);
                                 //gapString = $"{res1},{res2},{res3},{res4}";
                                 await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)2);
                                 return;
@@ -2885,13 +2874,7 @@ namespace TeamAAS_VP.Core
                                             {
                                                 revision[i] = revisionItem.Replace("stage=", "").Trim();
                                                 SendTaskMessage($"stage:{revision[i]}", MessageLevel.Info);
-                                            }
-                                            //if (QRcode != camerasn)
-                                            //{
-                                            //    SendTaskMessage($"Camera_SN与相机获取sn不一致", MessageLevel.Alarm);
-                                            //    await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
-                                            //    return;
-                                            //}
+                                            }    
                                             LogHelper.WriteLogMes($"询问mes成功");
                                             SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
 
@@ -2976,7 +2959,7 @@ namespace TeamAAS_VP.Core
 
                                     //读取产品结果
                                     Int16 resultIndex = plc.ReadNode<Int16>(string.Format(addressConfig.In_Result.Address, i));
-                                    resultIndex = 1;
+                                    //resultIndex = 1;
 
                                     #region 组装结果显示列表信息
                                     AssemblyRecord assemblyRecord = new AssemblyRecord();
@@ -3023,6 +3006,7 @@ namespace TeamAAS_VP.Core
                                         });
                                     }
                                     await _systemDatabaseService.RecordAssemblyAsync(assemblyRecord);
+
                                     #endregion
 
                                     LogHelper.WriteLogMes($"【mes开始ADD】");
@@ -3037,21 +3021,24 @@ namespace TeamAAS_VP.Core
                                             assypress = currentPressure[0].ToString("F2");
                                             if (currentPressure[0] >= currentProduct.GlobalParamEx.AssemblyPressure.PressureDownLimit && currentPressure[0] <= currentProduct.GlobalParamEx.AssemblyPressure.PressureUpLimit)
                                             {
-                                                resultIndex = 1;
+                                                //resultIndex = 1;
                                             }
                                             else
                                             {
-                                                resultIndex = 2;
+                                                if (!fileresult.Contains("AssyPressure1_File"))
+                                                {
+                                                    fileresult.Add($"AssyPressure1_File");
+                                                }
+                                                //resultIndex = 2;
                                             }
                                         }
                                         catch (Exception ex)
                                         {
-                                            assypress = "0";
-                                            resultIndex = 2;
+
                                         }
                                         data = gapDict.Copy();
                                         data.Add("AssyPressure1", assypress);
-                                        SendTaskMessage($"Gap1:{gapDict["Gap1"]},Gap2:{gapDict["Gap2"]},Gap3:{gapDict["Gap3"]},Gap4:{gapDict["Gap4"]},组装压力:{assypress}", MessageLevel.Info);
+                                        SendTaskMessage($"Gap1:{gapDict["Gap1"]},Gap2:{gapDict["Gap2"]},Gap3:{gapDict["Gap3"]},Gap4:{gapDict["Gap4"]},Gap5:{gapDict["Gap5"]},Gap6:{gapDict["Gap6"]},组装压力:{assypress}", MessageLevel.Info);
                                         #region CSV数据
                                         bool assyPresRes;
                                         if (currentPressure[0] >= currentProduct.GlobalParamEx.AssemblyPressure.PressureDownLimit && currentPressure[0] <= currentProduct.GlobalParamEx.AssemblyPressure.PressureUpLimit)
@@ -3071,21 +3058,55 @@ namespace TeamAAS_VP.Core
                                                     upLimitValue = currentProduct.GlobalParamEx.Gap1UpLimit.ToString("F3");
                                                     downLimitValue = currentProduct.GlobalParamEx.Gap1DownLimit.ToString("F3");
                                                     midValue = ((currentProduct.GlobalParamEx.Gap1UpLimit + currentProduct.GlobalParamEx.Gap1DownLimit) / 2).ToString("F3");
+                                                    if (Convert.ToDouble(item.Value) > Convert.ToDouble(upLimitValue) && Convert.ToDouble(item.Value) < Convert.ToDouble(downLimitValue) && !fileresult.Contains("Gap1_File"))
+                                                    {
+                                                        fileresult.Add($"Gap1_File");
+                                                    }
                                                     break;
                                                 case "Gap2":
                                                     upLimitValue = currentProduct.GlobalParamEx.Gap2UpLimit.ToString("F3");
                                                     downLimitValue = currentProduct.GlobalParamEx.Gap2DownLimit.ToString("F3");
                                                     midValue = ((currentProduct.GlobalParamEx.Gap2UpLimit + currentProduct.GlobalParamEx.Gap2DownLimit) / 2).ToString("F3");
+                                                    if (Convert.ToDouble(item.Value) > Convert.ToDouble(upLimitValue) && Convert.ToDouble(item.Value) < Convert.ToDouble(downLimitValue) && !fileresult.Contains("Gap2_File"))
+                                                    {
+                                                        fileresult.Add($"Gap2_File");
+                                                    }
                                                     break;
                                                 case "Gap3":
                                                     upLimitValue = currentProduct.GlobalParamEx.Gap3UpLimit.ToString("F3");
                                                     downLimitValue = currentProduct.GlobalParamEx.Gap3DownLimit.ToString("F3");
                                                     midValue = ((currentProduct.GlobalParamEx.Gap3UpLimit + currentProduct.GlobalParamEx.Gap3DownLimit) / 2).ToString("F3");
+                                                    if (Convert.ToDouble(item.Value) > Convert.ToDouble(upLimitValue) && Convert.ToDouble(item.Value) < Convert.ToDouble(downLimitValue) && !fileresult.Contains("Gap3_File"))
+                                                    {
+                                                        fileresult.Add($"Gap3_File");
+                                                    }
                                                     break;
                                                 case "Gap4":
                                                     upLimitValue = currentProduct.GlobalParamEx.Gap4UpLimit.ToString("F3");
                                                     downLimitValue = currentProduct.GlobalParamEx.Gap4DownLimit.ToString("F3");
                                                     midValue = ((currentProduct.GlobalParamEx.Gap4UpLimit + currentProduct.GlobalParamEx.Gap4DownLimit) / 2).ToString("F3");
+                                                    if (Convert.ToDouble(item.Value) > Convert.ToDouble(upLimitValue) && Convert.ToDouble(item.Value) < Convert.ToDouble(downLimitValue) && !fileresult.Contains("Gap4_File"))
+                                                    {
+                                                        fileresult.Add($"Gap4_File");
+                                                    }
+                                                    break;
+                                                case "Gap5":
+                                                    upLimitValue = currentProduct.GlobalParamEx.Gap5UpLimit.ToString("F3");
+                                                    downLimitValue = currentProduct.GlobalParamEx.Gap5DownLimit.ToString("F3");
+                                                    midValue = ((currentProduct.GlobalParamEx.Gap5UpLimit + currentProduct.GlobalParamEx.Gap5DownLimit) / 2).ToString("F3");
+                                                    if (Convert.ToDouble(item.Value) > Convert.ToDouble(upLimitValue) && Convert.ToDouble(item.Value) < Convert.ToDouble(downLimitValue) && !fileresult.Contains("Gap5_File"))
+                                                    {
+                                                        fileresult.Add($"Gap5_File");
+                                                    }
+                                                    break;
+                                                case "Gap6":
+                                                    upLimitValue = currentProduct.GlobalParamEx.Gap6UpLimit.ToString("F3");
+                                                    downLimitValue = currentProduct.GlobalParamEx.Gap6DownLimit.ToString("F3");
+                                                    midValue = ((currentProduct.GlobalParamEx.Gap6UpLimit + currentProduct.GlobalParamEx.Gap6DownLimit) / 2).ToString("F3");
+                                                    if (Convert.ToDouble(item.Value) > Convert.ToDouble(upLimitValue) && Convert.ToDouble(item.Value) < Convert.ToDouble(downLimitValue) && !fileresult.Contains("Gap6_File"))
+                                                    {
+                                                        fileresult.Add($"Gap6_File");
+                                                    }
                                                     break;
                                             }
                                             DFC.Add(new DataFormCtq
@@ -3152,17 +3173,20 @@ namespace TeamAAS_VP.Core
                                             press = currentPressure[1].ToString("F2");
                                             if (currentPressure[1] >= currentProduct.GlobalParamEx.DownPressure.PressureDownLimit && currentPressure[1] <= currentProduct.GlobalParamEx.DownPressure.PressureUpLimit)
                                             {
-                                                resultIndex = 1;
+                                                //resultIndex = 1;
                                             }
                                             else
                                             {
-                                                resultIndex = 2;
+                                                if (!fileresult.Contains("Pressure1_File"))
+                                                {
+                                                    fileresult.Add($"Pressure1_File");
+                                                }
+                                               // resultIndex = 2;
                                             }
                                         }
                                         catch (Exception ex)
                                         {
-                                            press = "0";
-                                            resultIndex = 2;
+
                                         }
                                         presstime = currentProduct.GlobalParamEx.DownPressure.PressureTime.ToString();
                                         data.Add("Pressure1", press);
@@ -3235,7 +3259,7 @@ namespace TeamAAS_VP.Core
                                         #endregion
                                     }
                                     //gap-复检gap参数, press-保压站实际压力值, presstime-保压站保压时间, assypress-组装站实际压力值
-                                    (bool isSuccess, string response) = await _mesService.SubmitGetAsync(i, ProductMainSN[i], comp, resultIndex == 1 ? true : false, data, _LastMesCheckTime[i], serverTimeResult.Now);
+                                    (bool isSuccess, string response) = await _mesService.SubmitGetAsync(i, ProductMainSN[i], comp, resultIndex == 1 ? true : false, data,fileresult, _LastMesCheckTime[i], serverTimeResult.Now);
                                     if (!stationConfig.EnableMES || isSuccess)
                                     {
                                         LogHelper.WriteLogMes($"上传mes成功");
@@ -3289,7 +3313,7 @@ namespace TeamAAS_VP.Core
                                                             string target_log = Path.Combine(filePath, Path.GetFileName(source_log));
                                                             File.Copy(source_log, target_log, true);
                                                             //判断是否保存锁付曲线
-                                                            if (pressureSensorConfig.IsSavePressureCurve)
+                                                            if (pressureSensorConfig.IsSavePressureCurve&&i!=0)
                                                             {
                                                                 string target_Directory = filePath + "\\压力曲线\\";
                                                                 string source_Torque_Image = pressureSensorConfig.PressureCurvePath + $"\\{dt.ToString("yyyy-MM")}\\{dt.ToString("yyyy-MM-dd")}\\{currentProduct.Name}\\{ProductMainSN[i]}";
@@ -3367,6 +3391,7 @@ namespace TeamAAS_VP.Core
                                     }
                                     LogHelper.WriteLogMes($"【mes结束ADD】");
                                     SendTaskMessage($"mes结束ADD", MessageLevel.Info);
+                                    fileresult.Clear();
                                     await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
                                     return;
                                 }
@@ -3436,9 +3461,20 @@ namespace TeamAAS_VP.Core
 
                                     //await Task.Delay(60);
                                     await plc.WriteNodeAsync(string.Format(addressConfig.In_WorkDone.Address, i), (Int16)0);
-                                    var code = plc.ReadNode<string>(string.Format(addressConfig.In_Code.Address, i));
                                     // 停止采集压力并保存
                                     await StopAndSavePressureCollectionAsync(currentProduct.Name, ProductMainSN[i], i);
+
+                                    if (i == 1)
+                                    {
+                                        if (pressure <= currentProduct.GlobalParamEx.DownPressure.PressureDownLimit || pressure >= currentProduct.GlobalParamEx.DownPressure.PressureUpLimit)
+                                        {
+                                            await plc.WriteNodeAsync(string.Format(addressConfig.In_WorkDone.Address, i), (Int16)4);//NG
+                                        }
+                                        else
+                                        {
+                                            await plc.WriteNodeAsync(string.Format(addressConfig.In_WorkDone.Address, i), (Int16)3);//OK
+                                        }
+                                    }                                    
                                 }
                                 else
                                 {
@@ -3858,10 +3894,54 @@ namespace TeamAAS_VP.Core
                 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;
+                return false;                
             }
         }
 
+        public async Task<string> CheckConsecutiveLowCapacityAsync(string productName, int consecutiveHours, int targetPerHour)
+        {
+            // 1. 获取你已有的按小时统计的产能数据
+            var records = await _systemDatabaseService.GetDailyProductionByHourAndCategoryAsync(productName);
+
+            // 连续计数初始化
+            int consecutiveFail = 0;
+            int firstFailHour = -1;
+
+            // 2. 遍历 0~23 小时,检查每小时总产能
+            for (int hour = 0; hour < 24; hour++)
+            {
+                // 当前小时总产能 = 该小时所有分类数量相加,没有数据 = 0
+                int currentCapacity = 0;
+                if (records.TryGetValue(hour, out var categoryDict))
+                {
+                    currentCapacity = categoryDict.Sum(kv => kv.Value);
+                }
+
+                // 3. 判断是否不达标
+                if (currentCapacity < targetPerHour)
+                {
+                    consecutiveFail++;
+                    if (firstFailHour == -1)
+                        firstFailHour = hour;
+
+                    if (consecutiveFail >= consecutiveHours)
+                    {
+                        int endHour = firstFailHour + consecutiveHours - 1;
+                        return $"⚠️ 报警:{productName} 自 {firstFailHour:D2}:00 至 {endHour:D2}:59 连续{consecutiveHours}小时产能未达到{targetPerHour},请关注!";
+                    }
+                }
+                else
+                {
+                    // 达标,重置连续计数
+                    consecutiveFail = 0;
+                    firstFailHour = -1;
+                }
+            }
+
+            // 无报警
+            return string.Empty;
+        }
+
         #region 压缩图片文件夹
         /// <summary>
         /// 压缩文件

+ 51 - 0
TeamAAS-VM/Data/DatabaseHelper.cs

@@ -182,6 +182,57 @@ namespace TeamAAS_VP.Data
             }
         }
 
+        /// <summary>
+        /// 获取最近20条相机拍照记录(按时间倒序)
+        /// </summary>
+        /// <param name="ProductName">产品名</param>
+        /// <param name="ProcedureName">工序名</param>
+        /// <returns>最近20条记录集合</returns>
+        public static List<Dictionary<string, object>> GetLast20CameraRecords(string ProductName, string ProcedureName)
+        {
+            var records = new List<Dictionary<string, object>>();
+            try
+            {
+                var dbpath = GetProductDbPath(ProductName);
+                if (!File.Exists(dbpath))
+                    return records;
+
+                var tableName = SanitizeIdentifier(ProcedureName);
+                if (!IsHaveTable(tableName, dbpath))
+                    return records;
+
+                using (var conn = new SQLiteConnection(string.Format("Data Source={0};Version=3;", dbpath)))
+                {
+                    conn.Open();
+                    using (var cmd = new SQLiteCommand(conn))
+                    {
+                        // 按时间倒序取最新20条
+                        cmd.CommandText = $"SELECT * FROM \"{tableName}\" ORDER BY _Time DESC LIMIT 20;";
+
+                        using (var reader = cmd.ExecuteReader())
+                        {
+                            while (reader.Read())
+                            {
+                                var row = new Dictionary<string, object>();
+                                for (int i = 0; i < reader.FieldCount; i++)
+                                {
+                                    var colName = reader.GetName(i);
+                                    var value = reader.IsDBNull(i) ? null : reader.GetValue(i);
+                                    row[colName] = value;
+                                }
+                                records.Add(row);
+                            }
+                        }
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("获取最近20条相机拍照记录时出错!", ex);
+            }
+            return records;
+        }
+
         /// <summary>
         /// 根据产品名、流程名与可选时间段查询相机记录,返回 DataTable(表不存在时返回空表)
         /// </summary>

+ 47 - 1
TeamAAS-VM/Data/SystemDatabaseService.cs

@@ -1,6 +1,7 @@
 using SqlSugar;
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Linq;
 using System.Threading.Tasks;
 using TeamAAS_VP.Interfaces;
@@ -417,6 +418,8 @@ namespace TeamAAS_VP.Data
         {
                         var lastHourStart = DateTime.UtcNow.AddHours(-1);
             var lastHourEnd = DateTime.UtcNow;
+
+
             //按照产品名称和时间范围查询生产记录的总数
             var total = await _db.Queryable<ProductionRecord>()
                 .Where(r => r.ProductName == productName && r.Timestamp >= lastHourStart && r.Timestamp < lastHourEnd)
@@ -704,6 +707,50 @@ namespace TeamAAS_VP.Data
             await _db.Insertable(record).ExecuteCommandAsync();
         }
 
+        /// <summary>
+        /// 查询最近20条记录 [SN|压力] 格式
+        /// </summary>
+        public async Task<List<string>> GetLatest20AssemblyPressureAsync(int deviceId)
+        {
+            var records = await _db.Queryable<AssemblyRecord>()
+                .Where(r => r.DeviceId == deviceId)
+                .OrderByDescending(r => r.Timestamp)
+                .Take(20)
+                .Select(r => new { r.AssemblyPressure, r.ProductSN }) // 只取需要的字段
+                .ToListAsync();
+
+            // 格式:SN + "|" + 压力
+            return records.Select(r => $"{r.AssemblyPressure}|{r.ProductSN}").ToList();
+        }
+
+        /// <summary>
+        /// 查询最近20条相机拍照检测记录结果值
+        /// </summary>
+        /// <param name="ProductName">产品型号</param>
+        /// <param name="ProcedureName">检测工序</param>
+        /// <param name="resultFieldName">拍照结果字段名(例:_MeasureValue)</param>
+        /// <returns>最近20条记录检测数值列表(按时间倒序,最新在前)</returns>
+        public async Task<List<double>> GetLatest20CameraResultAsync(string ProductName, string ProcedureName)
+        {
+
+            const string gapFieldName = "_gap";
+
+            var records = DatabaseHelper.GetLast20CameraRecords(ProductName, ProcedureName);
+
+            var gapList = new List<double>();
+            foreach (var record in records)
+            {
+                // 自动从字典里取 _gap 字段并转 double
+                if (record.TryGetValue(gapFieldName, out var valObj)
+                    && double.TryParse(valObj?.ToString(), out double gapValue))
+                {
+                    gapList.Add(gapValue);
+                }
+            }
+            // 保持和你方法一致的异步返回格式
+            return await Task.FromResult(gapList);
+        }
+
         /// <summary>
         /// 查询组装记录,支持按配方名、产品序列号和时间范围过滤
         /// </summary>
@@ -755,7 +802,6 @@ namespace TeamAAS_VP.Data
 
             }
         }
-
         /// <summary>
         /// 分页查询锁付结果
         /// </summary>

+ 3 - 2
TeamAAS-VM/Interfaces/IMesService.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
 using TeamAAS_VP.Models;
+using TouchSocket.Core;
 
 namespace TeamAAS_VP.Interfaces
 {
@@ -93,7 +94,7 @@ namespace TeamAAS_VP.Interfaces
         /// - 若 <paramref name="start_time"/> 晚于 <paramref name="stop_time"/>,实现应记录并视情况返回失败或抛出异常。
         /// - 实现应尊重 <paramref name="cancellationToken"/>,并在取消时尽早中止请求。
         /// </remarks>
-        Task<(bool IsSuccess, string Response)> SubmitGetAsync(int deviceNo, string sn, string comp, bool isSuccess, Dictionary<string, string> data, DateTime start_time, DateTime stop_time, CancellationToken cancellationToken = default);
+        Task<(bool IsSuccess, string Response)> SubmitGetAsync(int deviceNo, string sn, string comp, bool isSuccess, Dictionary<string, string> data, List<string> fieresult, DateTime start_time, DateTime stop_time, CancellationToken cancellationToken = default);
 
         /// <summary>
         /// 使用配置的提交(Submit)URL 发起 GET 请求,提交过站结果及时间信息,并使用指定的超时时间(秒)。
@@ -112,7 +113,7 @@ namespace TeamAAS_VP.Interfaces
         /// - 此重载用于在不传入 <see cref="CancellationToken"/> 的场景中指定超时时间。
         /// - 实现应确保对时间参数与布尔参数进行适当的 URL 编码或格式化。
         /// </remarks>
-        Task<(bool IsSuccess, string Response)> SubmitGetAsync(int deviceNo, string sn, string comp, bool isSuccess, Dictionary<string, string> data, DateTime start_time, DateTime stop_time, double timeoutInSeconds);
+        Task<(bool IsSuccess, string Response)> SubmitGetAsync(int deviceNo, string sn, string comp, bool isSuccess, Dictionary<string, string> data, List<string> fieresult, DateTime start_time, DateTime stop_time, double timeoutInSeconds);
 
         /// <summary>
         /// 获取服务器当前时间

+ 8 - 0
TeamAAS-VM/Interfaces/ISystemDatabaseService.cs

@@ -303,6 +303,14 @@ namespace TeamAAS_VP.Interfaces
         /// <returns></returns>
         Task<Dictionary<int, Dictionary<string, int>>> GetDailyProductionByHourAndCategoryAsync(string productName);
 
+
+        /// <summary>
+        /// 获取指定产品名称的一个星期内按照每天统计对应的类别和数量
+        /// </summary>
+        /// <param name="productName"></param>
+        /// <returns></returns>
+        Task <List<string>> GetLatest20AssemblyPressureAsync(int devicid);
+
         /// <summary>
         /// 分页查询生产记录。
         /// </summary>

+ 16 - 1
TeamAAS-VM/Models/PLC/PlcAddressConfig.cs

@@ -367,6 +367,16 @@ namespace TeamAAS_VP.Models.PLC
             get { return _Out_PartCode; }
             set { SetProperty(ref _Out_PartCode, value); }
         }
+
+        private PlcAddress _Out_RemovePos_Num = new PlcAddress();
+        /// <summary>
+        ///撕膜点位个数
+        /// </summary>
+        public PlcAddress Out_RemovePos_Num
+        {
+            get { return _Out_RemovePos_Num; }
+            set { SetProperty(ref _Out_RemovePos_Num, value); }
+        }
         #endregion
 
         #region 从PLC读取的地址
@@ -1125,7 +1135,12 @@ namespace TeamAAS_VP.Models.PLC
             Out_ScrewFeederIsChanged.Address = "ns=4;s=DB_Communication|FromPC.PickNumChange";
             Out_ScrewFeederIsChanged.DataType = "int16";
             Out_ScrewFeederIsChanged.Description = "供料器发生改变";
-            
+
+            Out_RemovePos_Num = Ensure(Out_RemovePos_Num);
+            Out_RemovePos_Num.Address = "ns=4;s=DB_Communication|FromPC.Point_Remove_Num";
+            Out_RemovePos_Num.DataType = "int16";
+            Out_RemovePos_Num.Description = "撕膜点位个数";
+
 
 
             // 4. In_* 默认(假定从 PLC 读到 PC,使用 ToPC 前缀)

+ 40 - 0
TeamAAS-VM/Models/Product/ProductGlobalParamEx.cs

@@ -103,5 +103,45 @@ namespace TeamAAS_VP.Models.Product
             get { return _Gap4DownLimit; }
             set { SetProperty(ref _Gap4DownLimit, value); }
         }
+
+        private float _Gap5UpLimit = 0;
+        /// <summary>
+        /// gap4上限
+        /// </summary>
+        public float Gap5UpLimit
+        {
+            get { return _Gap5UpLimit; }
+            set { SetProperty(ref _Gap5UpLimit, value); }
+        }
+
+        private float _Gap5DownLimit = 0;
+        /// <summary>
+        /// gap4下限
+        /// </summary>
+        public float Gap5DownLimit
+        {
+            get { return _Gap5DownLimit; }
+            set { SetProperty(ref _Gap5DownLimit, value); }
+        }
+
+        private float _Gap6UpLimit = 0;
+        /// <summary>
+        /// gap4上限
+        /// </summary>
+        public float Gap6UpLimit
+        {
+            get { return _Gap6UpLimit; }
+            set { SetProperty(ref _Gap6UpLimit, value); }
+        }
+
+        private float _Gap6DownLimit = 0;
+        /// <summary>
+        /// gap4下限
+        /// </summary>
+        public float Gap6DownLimit
+        {
+            get { return _Gap6DownLimit; }
+            set { SetProperty(ref _Gap6DownLimit, value); }
+        }
     }
 }

+ 12 - 0
TeamAAS-VM/Models/Product/ProductModel.cs

@@ -515,6 +515,17 @@ namespace TeamAAS_VP.Models
             set { SetProperty(ref _PressurePlacePoints, value); }
         }
 
+        private ObservableCollection<PlcPoint> _Reference_coordinate = new ObservableCollection<PlcPoint>();
+        /// <summary>
+        /// 屏幕中心基准点位集合
+        /// </summary>
+        public ObservableCollection<PlcPoint> Reference_coordinate
+        {
+            get { return _Reference_coordinate; }
+            set { SetProperty(ref _Reference_coordinate, value); }
+        }
+
+
         private bool _IsLoad;
         /// <summary>
         /// 是否已加载
@@ -632,6 +643,7 @@ namespace TeamAAS_VP.Models
                 IndependentCamerPoints = this.IndependentCamerPoints,
                 RemovePoints = this.RemovePoints,
                 PressurePlacePoints = this.PressurePlacePoints,
+                Reference_coordinate = this.Reference_coordinate,
                 IsLoad = this.IsLoad,
                 IsCreate = this.IsCreate,
                 QrCodePoints = this.QrCodePoints,

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

@@ -9230,5 +9230,71 @@ namespace TeamAAS_VP.Resources.Languages {
                 return ResourceManager.GetString("高功率", resourceCulture);
             }
         }
+
+        /// <summary>
+        ///   查找类似 组装压力SPC控制图 的本地化字符串。
+        /// </summary>
+        public static string 组装压力SPC控制图
+        {
+            get
+            {
+                return ResourceManager.GetString("组装压力SPC控制图", resourceCulture);
+            }
+        }
+
+        /// <summary>
+        ///   查找类似 压力值 的本地化字符串。
+        /// </summary>
+        public static string 压力值
+        {
+            get
+            {
+                return ResourceManager.GetString("压力值", resourceCulture);
+            }
+        }
+
+        /// <summary>
+        ///   查找类似 实际压力值 的本地化字符串。
+        /// </summary>
+        public static string 实际压力值
+        {
+            get
+            {
+                return ResourceManager.GetString("实际压力值", resourceCulture);
+            }
+        }
+
+        /// <summary>
+        ///   查找类似 UCL上管制限 的本地化字符串。
+        /// </summary>
+        public static string UCL上管制限
+        {
+            get
+            {
+                return ResourceManager.GetString("UCL上管制限", resourceCulture);
+            }
+        }
+
+        /// <summary>
+        ///   查找类似 CL中心线 的本地化字符串。
+        /// </summary>
+        public static string CL中心线
+        {
+            get
+            {
+                return ResourceManager.GetString("CL中心线", resourceCulture);
+            }
+        }
+
+        /// <summary>
+        ///   查找类似 LCL下管制限 的本地化字符串。
+        /// </summary>
+        public static string LCL下管制限
+        {
+            get
+            {
+                return ResourceManager.GetString("LCL下管制限", resourceCulture);
+            }
+        }
     }
 }

+ 7 - 3
TeamAAS-VM/Services/MesService.cs

@@ -359,7 +359,7 @@ namespace TeamAAS_VP.Services
         /// - 捕获 <see cref="OperationCanceledException"/> 返回 "Canceled",捕获其它异常则返回异常消息。
         /// - 若实例已被释放则抛出 <see cref="ObjectDisposedException"/>。
         /// </remarks>
-        public async Task<(bool IsSuccess, string Response)> SubmitGetAsync(int deviceNo, string sn, string comp, bool isSuccess, Dictionary<string, string> data, DateTime start_time, DateTime stop_time, CancellationToken cancellationToken = default)
+        public async Task<(bool IsSuccess, string Response)> SubmitGetAsync(int deviceNo, string sn, string comp, bool isSuccess, Dictionary<string, string> data, List<string> fieresult, DateTime start_time, DateTime stop_time, CancellationToken cancellationToken = default)
         {
             // 如果已经释放,则抛出异常,防止在已释放资源上继续操作。
             if (_disposed) throw new ObjectDisposedException(nameof(MesService));
@@ -382,6 +382,10 @@ namespace TeamAAS_VP.Services
             }
 
             string res = isSuccess ? "Pass" : "Fail";
+            if (res == "Fail")
+            {
+                res = string.Join(",", fieresult);
+            }
             StringBuilder sb = new StringBuilder();
             if (device.F == "PTL")
             {
@@ -532,11 +536,11 @@ namespace TeamAAS_VP.Services
         /// <param name="stop_time">结束时间。</param>
         /// <param name="timeoutInSeconds">请求超时,单位为秒。</param>
         /// <returns>与 <see cref="SubmitGetAsync(string, bool, DateTime, DateTime, CancellationToken)"/> 相同的返回语义。</returns>
-        public async Task<(bool IsSuccess, string Response)> SubmitGetAsync(int deviceNo, string sn, string comp, bool isSuccess, Dictionary<string, string> data, DateTime start_time, DateTime stop_time, double timeoutInSeconds)
+        public async Task<(bool IsSuccess, string Response)> SubmitGetAsync(int deviceNo, string sn, string comp, bool isSuccess, Dictionary<string, string> data, List<string> fieresult, DateTime start_time, DateTime stop_time, double timeoutInSeconds)
         {
             using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutInSeconds)))
             {
-                return await SubmitGetAsync(deviceNo, sn, comp, isSuccess, data, start_time, stop_time, cts.Token);
+                return await SubmitGetAsync(deviceNo, sn, comp, isSuccess, data,fieresult, start_time, stop_time, cts.Token);
             }
         }
 

+ 4 - 2
TeamAAS-VM/Services/RemoteCommandService.cs

@@ -1996,7 +1996,7 @@ namespace TeamAAS_VP.Services
             await SetLightBeforePhoto(procedure);
             try
             {
-                var dis = "";
+                var dis = "0,0";
                 //拍照失败时,需要多次拍照
                 for (int i = 0; i < procedure.FeederFailLimit; i++)
                 {
@@ -2007,6 +2007,7 @@ namespace TeamAAS_VP.Services
                     if (!visionSucceed)
                     {
                         SendTaskMessage(Lang.视觉任务执行失败, MessageLevel.Error);
+                        dis = (string)(outputCollection["Gap"].Value);
                         continue;
                     }
 
@@ -2031,8 +2032,9 @@ namespace TeamAAS_VP.Services
                      dis = (string)(outputCollection["Gap"].Value);
                     return (true, dis);
                 }
+                //dis = (string)(outputCollection["Gap"].Value);
                 SendTaskMessage(Lang.多次拍照失败, MessageLevel.Alarm);
-                return (false, "NA");
+                return (false, dis);
             }
             catch (Exception ex)
             {

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

@@ -570,6 +570,7 @@
     <Compile Include="ViewModels\Statistics\LockResultRecoredQueryViewModel.cs" />
     <Compile Include="ViewModels\Statistics\PhotoCaptureQueryViewModel.cs" />
     <Compile Include="ViewModels\Statistics\ProductionRecordQueryViewModel.cs" />
+    <Compile Include="ViewModels\Statistics\UphNumber.cs" />
     <Compile Include="ViewModels\User\CardLoginWindowViewModel.cs" />
     <Compile Include="Views\Calibration\CalibIndependentCamera.xaml.cs">
       <DependentUpon>CalibIndependentCamera.xaml</DependentUpon>

+ 11 - 0
TeamAAS-VM/ViewModels/Product/PlcPointParamsViewModel.cs

@@ -354,6 +354,7 @@ namespace TeamAAS_VP.ViewModels.Product
             public List<PlcPoint> RemovePoints { get; set; }
             public List<PlcPoint> QrCodePoints { get; set; }
             public List<PlcPoint> PressurePlacePoints { get; set; }
+            public List<PlcPoint> Reference_coordinate { get; set; }
         }
         #endregion
 
@@ -961,6 +962,9 @@ namespace TeamAAS_VP.ViewModels.Product
                 case 9:
                     Points = SelectProduct.QrCodePoints;
                     break;
+                case 10:
+                    Points = SelectProduct.Reference_coordinate;
+                    break;
                 default:
                     break;
             }
@@ -2115,6 +2119,7 @@ namespace TeamAAS_VP.ViewModels.Product
                     IndependentCamerPoints = SelectProduct.IndependentCamerPoints?.Select(p => p.Clone()).ToList() ?? new List<PlcPoint>(),
                     RemovePoints = SelectProduct.RemovePoints?.Select(p => p.Clone()).ToList() ?? new List<PlcPoint>(),
                     PressurePlacePoints = SelectProduct.PressurePlacePoints?.Select(p => p.Clone()).ToList() ?? new List<PlcPoint>(),
+                    Reference_coordinate = SelectProduct.Reference_coordinate?.Select(p => p.Clone()).ToList() ?? new List<PlcPoint>(),
                 };
 
                 var serializer = new XmlSerializer(typeof(ProductPointsExport));
@@ -2186,6 +2191,7 @@ namespace TeamAAS_VP.ViewModels.Product
                 SelectProduct.IndependentCamerPoints = new ObservableCollection<PlcPoint>(import.IndependentCamerPoints ?? new List<PlcPoint>());
                 SelectProduct.RemovePoints = new ObservableCollection<PlcPoint>(import.RemovePoints ?? new List<PlcPoint>());
                 SelectProduct.PressurePlacePoints = new ObservableCollection<PlcPoint>(import.PressurePlacePoints ?? new List<PlcPoint>());
+                SelectProduct.Reference_coordinate = new ObservableCollection<PlcPoint>(import.Reference_coordinate ?? new List<PlcPoint>());
 
                 // 重新编号每个集合
                 Action<ObservableCollection<PlcPoint>> renumber = (col) =>
@@ -2193,6 +2199,7 @@ namespace TeamAAS_VP.ViewModels.Product
                     for (int i = 0; i < col.Count; i++) col[i].Number = i + 1;
                 };
                 renumber(SelectProduct.PickCameraPoints);
+                renumber(SelectProduct.Reference_coordinate);
                 renumber(SelectProduct.PickPoints);
                 renumber(SelectProduct.SecPosCameraPoints);
                 renumber(SelectProduct.ScrewCameraPoints);
@@ -2305,6 +2312,10 @@ namespace TeamAAS_VP.ViewModels.Product
                 {
                     SelectProduct.PressurePlacePoints = new ObservableCollection<PlcPoint>();
                 }
+                if (SelectProduct.Reference_coordinate == null)
+                {
+                    SelectProduct.Reference_coordinate = new ObservableCollection<PlcPoint>();
+                }
 
             }
             catch (Exception ex)

+ 1 - 0
TeamAAS-VM/ViewModels/Product/ProductHomeViewModel.cs

@@ -207,6 +207,7 @@ namespace TeamAAS_VP.ViewModels.Product
                         CheckCameraPoints=new ObservableCollection<Models.PLC.PlcPoint>(),
                         RemovePoints = new ObservableCollection<Models.PLC.PlcPoint>(),
                         PressurePlacePoints = new ObservableCollection<Models.PLC.PlcPoint>(),
+                        Reference_coordinate = new ObservableCollection<Models.PLC.PlcPoint>(),
                         IndependentCamerPoints = new ObservableCollection<Models.PLC.PlcPoint>(),
                         QrCodePoints = new ObservableCollection<Models.PLC.PlcPoint>(),
                     };

+ 592 - 5
TeamAAS-VM/ViewModels/Statistics/ProductionStatementViewModel.cs

@@ -1,16 +1,22 @@
 using OxyPlot;
+using OxyPlot.Annotations;
 using OxyPlot.Axes;
 using OxyPlot.Legends;
 using OxyPlot.Series;
 using Prism.Commands;
+using Prism.Events;
 using Prism.Ioc;
 using Prism.Mvvm;
 using System;
 using System.Collections.Generic;
+using System.IO;
 using System.Linq;
+using System.Threading.Tasks;
 using System.Windows.Documents;
 using TeamAAS_VP.Core;
 using TeamAAS_VP.Data;
+using TeamAAS_VP.Enums;
+using TeamAAS_VP.Events;
 using TeamAAS_VP.Interfaces;
 using TeamAAS_VP.Models;
 using TeamAAS_VP.Resources.Languages;
@@ -24,6 +30,7 @@ namespace TeamAAS_VP.ViewModels.Statistics
         IContainerProvider _container;
         ISystemDatabaseService _systemDatabaseService;
         IProductService _productService;
+        IEventAggregator _eventAggregator;
 
         #region 属性
         private Int64 _TotalCount;
@@ -105,6 +112,67 @@ namespace TeamAAS_VP.ViewModels.Statistics
             get { return _DayChartModel; }
             set { SetProperty(ref _DayChartModel, value); }
         }
+
+        private string _NowYieldt;
+        /// <summary>
+        /// 实时良率
+        /// </summary>
+        public string NowYieldt
+        {
+            get { return _NowYieldt; }
+            set { SetProperty(ref _NowYieldt, value); }
+        }
+
+        private string _NowNumbers;
+        /// <summary>
+        /// 总检测数
+        /// </summary>
+        public string NowNumbers
+        {
+            get { return _NowNumbers; }
+            set { SetProperty(ref _NowNumbers, value); }
+        }
+
+        private string _NowOkNumbers;
+        /// <summary>
+        /// 合格数量
+        /// </summary>
+        public string NowOkNumbers
+        {
+            get { return _NowOkNumbers; }
+            set { SetProperty(ref _NowOkNumbers, value); }
+        }
+
+        private string _NowNgNumbers;
+        /// <summary>
+        /// 不良数量
+        /// </summary>
+        public string NowNgNumbers
+        {
+            get { return _NowNgNumbers; }
+            set { SetProperty(ref _NowNgNumbers, value); }
+        }
+
+
+        private string _UphNumber;
+        /// <summary>
+        /// uph合格数量
+        /// </summary>
+        public string UphNumber
+        {
+            get { return _UphNumber; }
+            set { SetProperty(ref _UphNumber, value); }
+        }
+
+        private string _HourNumber;
+        /// <summary>
+        /// uph连续小时数
+        /// </summary>
+        public string HourNumber
+        {
+            get { return _HourNumber; }
+            set { SetProperty(ref _HourNumber, value); }
+        }
         #endregion
 
         #region 命令
@@ -112,7 +180,9 @@ namespace TeamAAS_VP.ViewModels.Statistics
         public DelegateCommand LoadedCommand =>
             _LoadedCommand ?? (_LoadedCommand = new DelegateCommand(ExecuteLoadedCommand));
 
-        
+        private DelegateCommand _SaveConfigCommand;
+        public DelegateCommand SaveConfigCommand =>
+            _SaveConfigCommand ?? (_SaveConfigCommand = new DelegateCommand(SaveConfig));
         #endregion
 
         #region 事件
@@ -129,6 +199,7 @@ namespace TeamAAS_VP.ViewModels.Statistics
         #region 方法
         async void ExecuteLoadedCommand()
         {
+            //var spcvlaue = Management.GetLatest20PressureSPCAsync();
             var currentproduct = _productService.GetCurrentProduct();
             if (currentproduct == null) return;
             await  App.Current.Dispatcher.InvokeAsync(new Action(async () => {
@@ -137,13 +208,106 @@ namespace TeamAAS_VP.ViewModels.Statistics
                 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));
+                WeekChartModel = CreateAssemblyPressureSPC(await GetLatest20PressureSPCAsync());
+                MonthChartModel = CreateMonth(await _systemDatabaseService.GetMonthlyProductionByDayAndCategoryAsync(currentproduct.Name));
+                //WeekChartModel = CreateWeek(await _systemDatabaseService.GetWeeklyProductionByDayAndCategoryAsync(currentproduct.Name));
                 DayChartModel = CreateDay(await _systemDatabaseService.GetDailyProductionByHourAndCategoryAsync(currentproduct.Name));
+                await GetGapYieldStatisticsAsync();
+                await ReadUphAlarmConfig();
             }));
             
         }
 
+        // 保存配置方法
+        private void SaveConfig()
+        {
+            try
+            {
+                UPhAlarm data = new UPhAlarm
+                {
+                    Hour = HourNumber,
+                    Number = UphNumber
+                };
+
+                string path = @"..\Config\UphAlarm.cfg";
+                Directory.CreateDirectory(Path.GetDirectoryName(path));
+                FileHelper.WriteJsonFile(data, path);
+                //_eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"{Lang.成功}!", Duration = 0.4 });
+            }
+            catch (Exception ex)
+            {
+            }
+        }
+        public Task ReadUphAlarmConfig()
+        {
+            try
+            {
+                string path = @"..\Config\UphAlarm.cfg";
+                if (File.Exists(path))
+                {
+                    // 读取JSON文件
+                    var data =  FileHelper.ReadJsonFile<UPhAlarm>(path);
+                    if (data != null)
+                    {
+                        // 把配置值加载到属性
+                        HourNumber = data.Hour;
+                        UphNumber = data.Number;
+                        Management.uphhour = int.Parse(HourNumber);
+                        Management.uphnumber = int.Parse(UphNumber);
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine($"读取配置失败:{ex.Message}");
+            }
+
+            return Task.CompletedTask;
+        }
+        public class UPhAlarm
+        {
+            public string Hour { get; set; }
+            public string Number { get; set; }
+        }
+
+        /// <summary>
+        /// 实时监控 GAP 检测良率(最近20条生产拍照记录)
+        /// </summary>
+        /// <returns>总数量、合格数、良率(%)、状态</returns>
+        public async Task  GetGapYieldStatisticsAsync()
+        {
+            try
+            {
+                var currentproduct = _productService.GetCurrentProduct();
+                if (currentproduct == null)
+                {
+                    return ;
+                }
+
+                var records = await _systemDatabaseService.GetLatest20AssemblyPressureAsync(1);
+                if (records == null || records.Count == 0)
+                    return ;
+
+                double min = currentproduct.GlobalParamEx.DownPressure.PressureDownLimit;
+                double max = currentproduct.GlobalParamEx.DownPressure.PressureUpLimit;
+
+                // 3. 统计良率
+                double total = records.Count;
+                double okCount = records.Count(g =>Convert.ToDouble(g.Split('|')[0]) >= min && Convert.ToDouble(g.Split('|')[0]) <= max);
+                double yieldRate = Math.Round((double)okCount / total * 100, 2);
+
+                NowYieldt = yieldRate.ToString("F2")+"%";//良率
+                NowNumbers = total.ToString();//总数
+                NowOkNumbers = okCount.ToString();//OK数
+                NowNgNumbers=(total-okCount).ToString();
+                return ;
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("GAP良率统计异常", ex);
+                return ;
+            }
+        }
         /// <summary>
         /// 创建产量图表模型
         /// </summary>
@@ -175,13 +339,280 @@ namespace TeamAAS_VP.ViewModels.Statistics
             
             return model;
         }
+        /// <summary>
+        /// 创建Spc压力控制图
+        /// </summary>
+        /// <param name="spcPoints"></param>
+        /// <returns></returns>
+        private PlotModel CreateAssemblyPressureSPC(List<SpcPoint> spcPoints)
+        {
+            if (spcPoints == null || spcPoints.Count == 0)
+                return new PlotModel { Title = "暂无数据" };
+
+            var model = new PlotModel { Title = "组装压力SPC控制图", TitleFontSize = 14 };
+
+            model.Legends.Add(new Legend
+            {
+                LegendPlacement = LegendPlacement.Outside,
+                LegendPosition = LegendPosition.RightMiddle,
+                LegendOrientation = LegendOrientation.Vertical,
+                LegendBorderThickness = 0,
+                LegendTextColor = OxyColors.LightGray
+            });
+
+            double ucl = spcPoints.First().UCL;
+            double cl = spcPoints.First().CL;
+            double lcl = spcPoints.First().LCL;
+            string sn = spcPoints.First().SN;
+
+            double maxValue = spcPoints.Max(p => p.Value);
+            double minValue = spcPoints.Min(p => p.Value);
+
+            double yx = spcPoints.Count;
+            double allMax = new[] { maxValue, ucl }.Max();
+            double allMin = new[] { minValue, lcl }.Min();
+
+            double yMax = allMax > 0 ? allMax * 1.05 : allMax * 0.95;
+            double yMin = allMin < 0 ? allMin * 1.05 : allMin * 0.95;
+
+            yMin = Math.Min(yMin, allMin - 3);
+            yMax = Math.Max(yMax, allMax + 3);
+
+            var xAxis = new LinearAxis
+            {
+                Key = "X",
+                Title = "序号",
+                Minimum = 0,
+                Maximum = yx + 1,
+                Position = AxisPosition.Bottom,
+                IsZoomEnabled = false,
+                IsPanEnabled = false,
+                TickStyle = TickStyle.Outside,
+                MajorGridlineStyle = LineStyle.Solid,
+                MajorGridlineColor = OxyColor.Parse("#F3F3F3")
+            };
+            model.Axes.Add(xAxis);
+
+            var yAxis = new LinearAxis
+            {
+                Key = "Y",
+                Title = "压力值",
+                Minimum = yMin,
+                Maximum = yMax,
+                Position = AxisPosition.Left,
+                IsZoomEnabled = false,
+                IsPanEnabled = false,
+                TickStyle = TickStyle.Outside,
+                MajorGridlineStyle = LineStyle.Solid,
+                MajorGridlineColor = OxyColor.Parse("#F3F3F3")
+            };
+            model.Axes.Add(yAxis);
+
+            var valueSeries = new LineSeries
+            {
+                Title = "实际压力",
+                Color = OxyColors.Blue,
+                MarkerType = MarkerType.Circle,
+                MarkerSize = 4,
+                MarkerStroke = OxyColors.Blue,
+                MarkerFill = OxyColors.White,
+                YAxisKey = "Y",
+                XAxisKey = "X"
+            };
+
+            var uclSeries = new LineSeries
+            {
+                Title = $"上限 = {ucl:F2}",
+                Color = OxyColors.Red,
+                LineStyle = LineStyle.Dash,
+                YAxisKey = "Y",
+                XAxisKey = "X"
+            };
+
+            var clSeries = new LineSeries
+            {
+                Title = $"平均值 = {cl:F2}",
+                Color = OxyColors.Green,
+                LineStyle = LineStyle.Solid,
+                YAxisKey = "Y",
+                XAxisKey = "X"
+            };
+
+            var lclSeries = new LineSeries
+            {
+                Title = $"下限 = {lcl:F2}",
+                Color = OxyColors.Red,
+                LineStyle = LineStyle.Dash,
+                YAxisKey = "Y",
+                XAxisKey = "X"
+            };
+
+
+            foreach (var point in spcPoints)
+            {
+                valueSeries.Points.Add(new OxyPlot.DataPoint(point.Index, point.Value));
+
+                uclSeries.Points.Add(new OxyPlot.DataPoint(point.Index, point.UCL));
+                clSeries.Points.Add(new OxyPlot.DataPoint(point.Index, point.CL));
+                lclSeries.Points.Add(new OxyPlot.DataPoint(point.Index, point.LCL));
+            }
+
+
+            var uclAnnotation = new LineAnnotation
+            {
+                Type = LineAnnotationType.Horizontal,
+                Y = ucl,
+                Text = $"UCL {ucl:F2}",
+                TextColor = OxyColors.Red,
+                Color = OxyColors.Red,
+                LineStyle = LineStyle.None,
+                TextPosition = new OxyPlot.DataPoint(20.5, ucl)
+            };
+
+            var clAnnotation = new LineAnnotation
+            {
+                Type = LineAnnotationType.Horizontal,
+                Y = cl,
+                Text = $"CL {cl:F2}",
+                TextColor = OxyColors.Green,
+                Color = OxyColors.Green,
+                LineStyle = LineStyle.None,
+                TextPosition = new OxyPlot.DataPoint(20.5, cl)
+            };
+
+            var lclAnnotation = new LineAnnotation
+            {
+                Type = LineAnnotationType.Horizontal,
+                Y = lcl,
+                Text = $"LCL {lcl:F2}",
+                TextColor = OxyColors.Red,
+                Color = OxyColors.Red,
+                LineStyle = LineStyle.None,
+                TextPosition = new OxyPlot.DataPoint(20.5, lcl)
+            };
+
+            model.Annotations.Add(uclAnnotation);
+            model.Annotations.Add(clAnnotation);
+            model.Annotations.Add(lclAnnotation);
+
+            model.Series.Add(valueSeries);
+            model.Series.Add(uclSeries);
+            model.Series.Add(clSeries);
+            model.Series.Add(lclSeries);
+
+            return model;
+        }
+
+        /// <summary>
+        /// 创建月统计模型(总产能蓝色柱 + OK量绿色柱,Tooltip显示正确日期+标签)
+        /// </summary>
+        private PlotModel CreateMonth(Dictionary<DateTime, Dictionary<string, int>> Items)
+        {
+            var model = new PlotModel { Title = Lang.当前月产能统计 };
+
+            model.Legends.Add(new Legend
+            {
+                LegendPlacement = LegendPlacement.Outside,
+                LegendPosition = LegendPosition.RightMiddle,
+                LegendOrientation = LegendOrientation.Vertical,
+                LegendBorderThickness = 0,
+                LegendTextColor = OxyColors.LightGray
+            });
+
+            var ay1 = new LinearAxis()
+            {
+                Key = "y1",
+                Title = Lang.数量,
+                TitlePosition = 1,
+                Minimum = 0,
+                Position = AxisPosition.Left,
+                IsZoomEnabled = false,
+                IsPanEnabled = false,
+                TickStyle = TickStyle.None,
+                MinorTickSize = 0,
+                MajorGridlineStyle = LineStyle.Solid,
+                MajorGridlineColor = OxyColor.Parse("#F3F3F3"),
+            };
+
+            DateTime minDate, maxDate;
+            if (Items == null || Items.Count == 0)
+            {
+                var today = DateTime.Now.Date;
+                minDate = today.AddDays(-0.5);
+                maxDate = today.AddDays(0.5);
+            }
+            else
+            {
+                minDate = Items.Keys.First().AddDays(-0.5);
+                maxDate = Items.Keys.Last().AddDays(0.5);
+            }
+            var ax = new DateTimeAxis()
+            {
+                Minimum = DateTimeAxis.ToDouble(minDate),
+                Maximum = DateTimeAxis.ToDouble(maxDate),
+                StringFormat = $"dd {Lang.日}",
+                MajorStep = 1,
+                Position = AxisPosition.Bottom,
+                Angle = 45,
+                IsZoomEnabled = false,
+                IsPanEnabled = false,
+                TickStyle = TickStyle.None,
+                MinorTickSize = 0,
+                MajorGridlineStyle = LineStyle.Solid,
+                MajorGridlineColor = OxyColor.Parse("#F3F3F3"),
+            };         
+            model.Axes.Add(ay1);
+            model.Axes.Add(ax);
+
+
+
+            // 1. 总产能(蓝色柱)
+            var totalSeries = new LinearBarSeries
+            {
+                Title = "总产能",
+                YAxisKey = "y1",
+                BarWidth = 10,
+                FillColor = OxyColors.SteelBlue,
+                StrokeColor = OxyColors.SteelBlue,
+                TrackerFormatString = "总产能\n{2:MM月dd日}: {4:0}"
+            };
+
+            // 2. OK量(绿色柱)
+            var okSeries = new LinearBarSeries
+            {
+                Title = "OK量",
+                YAxisKey = "y1",
+                BarWidth = 10,
+                FillColor = OxyColors.Green,
+                StrokeColor = OxyColors.Green,
+                TrackerFormatString = "OK量\n{2:MM月dd日}: {4:0}"
+            };
+
+
+            foreach (var item in Items)
+            {
+                double baseX = DateTimeAxis.ToDouble(item.Key);
+                long totalCount = item.Value.Sum(kv => kv.Value);
+                int okCount = item.Value.ContainsKey("OK") ? item.Value["OK"] : 0;
+
+                totalSeries.Points.Add(new OxyPlot.DataPoint(baseX , totalCount));
+                okSeries.Points.Add(new OxyPlot.DataPoint(baseX + 0.2, okCount));
+            }
+
+            model.Series.Add(totalSeries);
+            model.Series.Add(okSeries);
+
+            return model;
+        }
+
+
 
         /// <summary>
         /// 创建月统计模型
         /// </summary>
         /// <param name="Items"></param>
         /// <returns></returns>
-        private PlotModel CreateMonth(Dictionary<DateTime, Dictionary<string, int>> Items)
+        private PlotModel CreateMonth2(Dictionary<DateTime, Dictionary<string, int>> Items)
         {
             var model = new PlotModel { Title = Lang.当前月产能统计 };
 
@@ -352,8 +783,9 @@ namespace TeamAAS_VP.ViewModels.Statistics
             return model;
         }
 
+
         /// <summary>
-        /// 创建日统计模型
+        /// 创建日统计模型(总产能蓝色柱 + OK量绿色柱)
         /// </summary>
         /// <param name="Items"></param>
         /// <returns></returns>
@@ -403,6 +835,104 @@ namespace TeamAAS_VP.ViewModels.Statistics
                 MajorGridlineColor = OxyColor.Parse("#F3F3F3"),
             };
 
+            model.Axes.Add(ay1);
+            model.Axes.Add(ax);
+
+            if (Items.Count == 0)
+            {
+                return model;
+            }
+
+            // 1. 总产能(蓝色柱)
+            var totalSeries = new LinearBarSeries
+            {
+                Title = "总产能",
+                YAxisKey = "y1",
+                BarWidth = 10,
+                FillColor = OxyColors.SteelBlue,
+                StrokeColor = OxyColors.SteelBlue,
+                TrackerFormatString = "总产能\n{2:0}时: {4:0}"
+            };
+
+            // 2. OK量(绿色柱)
+            var okSeries = new LinearBarSeries
+            {
+                Title = "OK量",
+                YAxisKey = "y1",
+                BarWidth = 10,
+                FillColor = OxyColors.Green,
+                StrokeColor = OxyColors.Green,
+                TrackerFormatString = "OK量\n{2:0}时: {4:0}"
+            };
+
+            // 遍历每小时数据
+            foreach (var item in Items)
+            {
+                double baseX = LinearAxis.ToDouble(item.Key);
+                long totalCount = item.Value.Sum(kv => kv.Value);
+                int okCount = item.Value.ContainsKey("OK") ? item.Value["OK"] : 0;
+
+                totalSeries.Points.Add(new OxyPlot.DataPoint(baseX , totalCount));
+                okSeries.Points.Add(new OxyPlot.DataPoint(baseX + 0.2, okCount));
+            }
+
+            model.Series.Add(totalSeries);
+            model.Series.Add(okSeries);
+
+            return model;
+        }
+
+        /// <summary>
+        /// 创建日统计模型
+        /// </summary>
+        /// <param name="Items"></param>
+        /// <returns></returns>
+        private PlotModel CreateDay2(Dictionary<int, Dictionary<string, int>> Items)
+        {
+            var model = new PlotModel { Title = Lang.当前日产能统计 };
+
+            // 添加图例说明
+            model.Legends.Add(new Legend
+            {
+                LegendPlacement = LegendPlacement.Outside,
+                LegendPosition = LegendPosition.RightMiddle,
+                LegendOrientation = LegendOrientation.Vertical,
+                LegendBorderThickness = 0,
+                LegendTextColor = OxyColors.LightGray
+            });
+
+            // 定义第一个Y轴y1,显示数量
+            var ay1 = new LinearAxis()
+            {
+                Key = "y1",
+                Title = $"{Lang.数量}",
+                Minimum = 0,
+                Position = AxisPosition.Left,
+                IsZoomEnabled = false,
+                IsPanEnabled = false,
+                TickStyle = TickStyle.None,
+                MinorTickSize = 0,
+                MajorGridlineStyle = LineStyle.Solid,
+                MajorGridlineColor = OxyColor.Parse("#F3F3F3"),
+            };
+
+            // 定义X轴为小时
+            var ax = new LinearAxis()
+            {
+                Minimum = 0,
+                Maximum = 24.5,
+                MajorStep = 1,
+                StringFormat = "0 H",
+                Position = AxisPosition.Bottom,
+                Angle = 0,
+                IsZoomEnabled = false,
+                IsPanEnabled = false,
+                TickStyle = TickStyle.None,
+                MinorTickSize = 0,
+                MajorGridlineStyle = LineStyle.Solid,
+                MajorGridlineColor = OxyColor.Parse("#F3F3F3"),
+            };
+
             model.Axes.Add(ay1);
             model.Axes.Add(ax);
             if (Items.Count==0)
@@ -443,5 +973,62 @@ namespace TeamAAS_VP.ViewModels.Statistics
             return model;
         }
         #endregion
+        #region Spc
+        public async Task<List<SpcPoint>> GetLatest20PressureSPCAsync()
+        {
+            //var pressureList = await _systemDatabaseService.GetLatest20CameraResultAsync();
+            var pressureList1 = await _systemDatabaseService.GetLatest20AssemblyPressureAsync(1);
+            return GenerateAssemblyPressureSPC(pressureList1);
+        }
+
+        /// <summary>
+        /// 根据20条组装压力值 → 生成SPC控制图数据
+        /// </summary>
+        public List<SpcPoint> GenerateAssemblyPressureSPC(List<string> values)
+        {
+            // 获取当前产品
+            var currentProduct = _productService.GetCurrentProduct();
+            List<SpcPoint> spcList = new List<SpcPoint>();
+
+            if (values == null || values.Count == 0)
+                return spcList;
+
+            double UCL = currentProduct.GlobalParamEx.DownPressure.PressureUpLimit;
+            double CL = (currentProduct.GlobalParamEx.DownPressure.PressureUpLimit + currentProduct.GlobalParamEx.DownPressure.PressureDownLimit) / 2;
+            double LCL = currentProduct.GlobalParamEx.DownPressure.PressureDownLimit;
+
+            // 生成每个点
+            for (int i = 0; i < values.Count; i++)
+            {
+                double val =Convert.ToDouble( values[i].Split('|')[0]);
+                string sn = values[i].Split('|')[1];
+                spcList.Add(new SpcPoint
+                {
+                    Index = i + 1,
+                    Value = val,
+                    UCL = UCL,
+                    CL = CL,
+                    LCL = LCL,
+                    SN =sn,
+                    IsOutOfControl = val > UCL || val < LCL
+                });
+            }
+
+            return spcList;
+        }
+        /// <summary>
+        /// SPC 控制图点实体
+        /// </summary>
+        public class SpcPoint
+        {
+            public string SN { get; set; }
+            public int Index { get; set; }
+            public double Value { get; set; }
+            public double UCL { get; set; }
+            public double CL { get; set; }
+            public double LCL { get; set; }
+            public bool IsOutOfControl { get; set; }
+        }
+        #endregion
     }
 }

+ 6 - 0
TeamAAS-VM/ViewModels/Statistics/UphNumber.cs

@@ -0,0 +1,6 @@
+namespace TeamAAS_VP.ViewModels.Statistics
+{
+    internal class UphNumber
+    {
+    }
+}

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

@@ -163,7 +163,8 @@
                                                     IsReadOnly="True" />
                                 <DataGridTemplateColumn Header="{lex:Loc 组装结果}"
                                                         Width="auto"
-                                                        IsReadOnly="True">
+                                                        IsReadOnly="True"
+                                                        SortMemberPath="AssemblyResult">
                                     <DataGridTemplateColumn.CellTemplate>
                                         <DataTemplate>
                                             <Border x:Name="bd"

+ 15 - 1
TeamAAS-VM/Views/Product/PlcPointParams.xaml

@@ -341,6 +341,19 @@
                                 </ListBoxItem.Style>
                                 <TextBlock Text="{lex:Loc 二维码拍照点位}" />
                             </ListBoxItem>
+                            <!--屏幕中心基准点位-->
+                            <ListBoxItem>
+                                <ListBoxItem.Style>
+                                    <Style TargetType="ListBoxItem"
+                                           BasedOn="{StaticResource MaterialDesign3.NavigationBarPrimaryListBoxItem}">
+                                        <Setter Property="materialDesign:NavigationBarAssist.SelectedIcon"
+                                                Value="Circle" />
+                                        <Setter Property="materialDesign:NavigationBarAssist.UnselectedIcon"
+                                                Value="CircleOutline" />
+                                    </Style>
+                                </ListBoxItem.Style>
+                                <TextBlock Text="{lex:Loc 屏幕中心基准点位}" />
+                            </ListBoxItem>
                         </ListBox>
                         <!--点位列表操作按钮面板-->
                         <StackPanel DockPanel.Dock="Bottom"
@@ -540,7 +553,8 @@
                                       ScrollViewer.CanContentScroll="True"
                                       ScrollViewer.HorizontalScrollBarVisibility="Auto"
                                       GridLinesVisibility="All"
-                                      VerticalGridLinesBrush="#D0D0D0">
+                                      VerticalGridLinesBrush="#D0D0D0"
+                                      SelectionChanged="dgPoints_SelectionChanged">
                                 <DataGrid.Resources>
                                     <Style TargetType="DataGridCell">
                                         <Style.Resources>

+ 5 - 0
TeamAAS-VM/Views/Product/PlcPointParams.xaml.cs

@@ -127,5 +127,10 @@ namespace TeamAAS_VP.Views.Product
                 }
             }
         }
+
+        private void dgPoints_SelectionChanged(object sender, SelectionChangedEventArgs e)
+        {
+
+        }
     }
 }

+ 52 - 0
TeamAAS-VM/Views/Product/ProductParams.xaml

@@ -294,6 +294,10 @@
                                         <RowDefinition Height="Auto" />
                                         <RowDefinition Height="Auto" />
                                         <RowDefinition Height="Auto" />
+                                        <RowDefinition Height="Auto" />
+                                        <RowDefinition Height="Auto" />
+                                        <RowDefinition Height="Auto" />
+                                        <RowDefinition Height="Auto" />
                                     </Grid.RowDefinitions>
                                     <TextBlock Text="Gap1上限"
                                                Grid.Row="0"
@@ -391,6 +395,54 @@
                                                Grid.Row="7"
                                                Grid.Column="2"
                                                HorizontalAlignment="Right" />
+                                    <TextBlock Text="Gap5上限"
+                                               Grid.Row="8"
+                                               Grid.Column="0"
+                                               HorizontalAlignment="Right" />
+                                    <TextBox Text="{Binding SelectProduct.GlobalParamEx.Gap5UpLimit,Mode=TwoWay}"
+                                             Grid.Row="8"
+                                             Grid.Column="1"
+                                             Margin="6,0" />
+                                    <TextBlock Text="mm"
+                                               Grid.Row="8"
+                                               Grid.Column="2"
+                                               HorizontalAlignment="Right" />
+                                    <TextBlock Text="Gap5下限"
+                                               Grid.Row="9"
+                                               Grid.Column="0"
+                                               HorizontalAlignment="Right" />
+                                    <TextBox Text="{Binding SelectProduct.GlobalParamEx.Gap5DownLimit,Mode=TwoWay}"
+                                             Grid.Row="9"
+                                             Grid.Column="1"
+                                             Margin="6,0" />
+                                    <TextBlock Text="mm"
+                                               Grid.Row="9"
+                                               Grid.Column="2"
+                                               HorizontalAlignment="Right" />
+                                    <TextBlock Text="Gap6上限"
+                                               Grid.Row="10"
+                                               Grid.Column="0"
+                                               HorizontalAlignment="Right" />
+                                    <TextBox Text="{Binding SelectProduct.GlobalParamEx.Gap6UpLimit,Mode=TwoWay}"
+                                             Grid.Row="10"
+                                             Grid.Column="1"
+                                             Margin="6,0" />
+                                    <TextBlock Text="mm"
+                                               Grid.Row="10"
+                                               Grid.Column="2"
+                                               HorizontalAlignment="Right" />
+                                    <TextBlock Text="Gap6下限"
+                                               Grid.Row="11"
+                                               Grid.Column="0"
+                                               HorizontalAlignment="Right" />
+                                    <TextBox Text="{Binding SelectProduct.GlobalParamEx.Gap6DownLimit,Mode=TwoWay}"
+                                             Grid.Row="11"
+                                             Grid.Column="1"
+                                             Margin="6,0" />
+                                    <TextBlock Text="mm"
+                                               Grid.Row="11"
+                                               Grid.Column="2"
+                                               HorizontalAlignment="Right" />
                                 </Grid>
                             </GroupBox>
                         </StackPanel>

+ 89 - 3
TeamAAS-VM/Views/Statistics/ProductionStatement.xaml

@@ -108,19 +108,105 @@
                     Margin="10"
                     materialDesign:ButtonAssist.CornerRadius="10"
                     Style="{StaticResource MaterialDesignRaisedButton}" />
+            <StackPanel Orientation="Horizontal"
+                        Margin="2,100,2,2">
+                <TextBlock Text="连续小时数:"
+                           Width="80"
+                           VerticalAlignment="Center" />
+                <TextBox x:Name="txtConsecutiveHours"
+                         Width="100"
+                         Text="{Binding HourNumber}" />
+            </StackPanel>
+
+            <StackPanel Orientation="Horizontal"
+                        Margin="2,5,2,2">
+                <TextBlock Text="目标产能:"
+                           Width="80"
+                           VerticalAlignment="Center" />
+                <TextBox x:Name="txtTargetCapacity"
+                         Width="100"
+                         Text="{Binding UphNumber}" />
+            </StackPanel>
+            <Button Content="保存配置"
+                    Width="120"
+                    Height="35"
+                    Margin="10"
+                    Command="{Binding SaveConfigCommand}" />
+
         </StackPanel>
 
         <ScrollViewer Grid.Column="1">
             <StackPanel Orientation="Vertical">
-                <oxy:PlotView Model="{Binding MonthChartModel}"
-                              Background="Transparent"
-                              Height="300" />
+                <Border Background="#FFFFFF"
+                        CornerRadius="8"
+                        Margin="0,0,0,0"
+                        Padding="1">
+                    <StackPanel Orientation="Horizontal"
+                                HorizontalAlignment="Center"
+                                Margin="0">
+                        <!-- 良率 -->
+                        <StackPanel>
+                            <TextBlock Text="良率"
+                                       Foreground="#2D2D30"
+                                       FontSize="12"
+                                       Margin="30,0,30,0"
+                                       Opacity="0.8" />
+                            <TextBlock x:Name="TxtYieldRate"
+                                       Foreground="#00FF00"
+                                       FontSize="24"
+                                       Margin="30,0,30,0"
+                                       Text="{Binding NowYieldt}" />
+                        </StackPanel>
+                        <!-- 总数 -->
+                        <StackPanel>
+                            <TextBlock Text="总检测数"
+                                       Foreground="#2D2D30"
+                                       FontSize="12"
+                                       Margin="30,0,30,0"
+                                       Opacity="0.8" />
+                            <TextBlock x:Name="TxtTotalCount"
+                                       Foreground="#2D2D30"
+                                       FontSize="24"
+                                       Margin="30,0,30,0"
+                                       Text="{Binding NowNumbers}" />
+                        </StackPanel>
+                        <!-- 合格数 -->
+                        <StackPanel>
+                            <TextBlock Text="合格数量"
+                                       Foreground="#2D2D30"
+                                       FontSize="12"
+                                       Margin="30,0,30,0"
+                                       Opacity="0.8" />
+                            <TextBlock x:Name="TxtOkCount"
+                                       Foreground="#00FF00"
+                                       FontSize="24"
+                                       Margin="30,0,30,0"
+                                       Text="{Binding NowOkNumbers}" />
+                        </StackPanel>
+                        <!-- 不合格数 -->
+                        <StackPanel>
+                            <TextBlock Text="不良数量"
+                                       Foreground="#2D2D30"
+                                       FontSize="12"
+                                       Margin="30,0,30,0"
+                                       Opacity="0.8" />
+                            <TextBlock x:Name="TxtNgCount"
+                                       Foreground="#FF4444"
+                                       FontSize="24"
+                                       Margin="30,0,30,0"
+                                       Text="{Binding NowNgNumbers}" />
+                        </StackPanel>
+                    </StackPanel>
+                </Border>
                 <oxy:PlotView Model="{Binding WeekChartModel}"
                               Background="Transparent"
                               Height="300" />
                 <oxy:PlotView Model="{Binding DayChartModel}"
                               Background="Transparent"
                               Height="300" />
+                <oxy:PlotView Model="{Binding MonthChartModel}"
+                              Background="Transparent"
+                              Height="300" />
             </StackPanel>
         </ScrollViewer>