Browse Source

更新装箱服务和库存管理功能

在多个文件中添加了新的属性和方法,以支持托盘装箱、库存状态管理和扭转值的处理。重构了相关逻辑,增强了代码的可读性和可维护性。具体更改包括:
- 在 `SxController.cs` 中添加命名空间和静态对象更新。
- 在 `CurtainProductionOrderRequest.cs` 中新增扭转目标值属性。
- 在 `StockTemp.cs` 中添加库存状态和生产信息属性。
- 在 `LayerPackingService.cs` 中实现层配装箱服务。
- 在 `LayerPackingBoxInfo.cs` 和 `LayerPackingConstraints.cs` 中支持装箱约束。
- 在 `LayerPackingLayerInfo.cs` 和 `LayerPackingProduct.cs` 中添加详细信息属性。
- 在 `LayerPackingResult.cs` 中管理装箱结果。
- 在 `SxServiceExtension.cs` 中重构获取机械臂和托盘的逻辑。
- 在 `ISXService.cs` 中新增库存改判和预赋扭转值方法。
- 在 `SXService.cs` 中重构与扭转值和库存管理相关的逻辑。
- 在 `BillPboxrule.cs` 和 `BillPboxruleHty.cs` 中添加扭转目标值属性。
林豪 左 2 months ago
parent
commit
e3c756b825

+ 124 - 121
wms.api/Controllers/SxController.cs

@@ -1,28 +1,23 @@
 using AutoMapper;
-using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
 using Newtonsoft.Json;
-using Org.BouncyCastle.Asn1.Ocsp;
 using wms.api.Job;
 using wms.dto;
 using wms.dto.request;
-using wms.dto.request.fj;
 using wms.dto.request.cp;
+using wms.dto.request.fj;
 using wms.dto.request.hj;
 using wms.dto.request.hj.dto;
 using wms.dto.request.share;
 using wms.dto.request.sx;
 using wms.dto.response;
 using wms.dto.response.cp;
+using wms.dto.response.sx;
 using wms.service.IService;
 using wms.sqlsugar.model.sx;
+using wms.util.Check;
 using wms.util.Ext;
 using wms.util.Http;
-using wms.dto.response.sx;
-using wms.service.Service;
-using Castle.Core.Internal;
-using wms.util.Check;
-using Autofac.Core;
 
 namespace wms.api.Controllers
 {
@@ -39,6 +34,7 @@ namespace wms.api.Controllers
         private static object lockerSynchronizeBoxStatus = new object();
 
         private static object lockerlockerStockChange = new object();
+        private static object lockerlockerPreSpecifiedTorsionValues = new object();
         private static object lockerManualBuildStockTrans = new object();
         private static object lockerBomInfoTrans = new object();
         private static object lockerCompleteTask = new object();
@@ -311,38 +307,38 @@ namespace wms.api.Controllers
             return ConcurrencyReqControl<PalletizingPackStockOutRequest, SRes>(lockerPalletizingLayerPackStockOut, "PalletizingLayerPackStockOut", request.Equip, request, _sxService.PalletizingLayerPackStockOut);
         }
 
-        /// <summary>
-        /// 码垛SPC非控制装箱(缓存托盘)
-        /// </summary>
-        /// <param name="request"></param>
-        /// <returns></returns>
-        [HttpPost]
-        public SRes PalletizingSpcNotPackStockOut2(PalletizingPackStockOutRequest request)
-        {
-            lock (lockerPalletizingSpcPackStockOut)
-            {
-                return _sxService.PalletizingSpcNotPackStockOut2(request);
-            }
-            //return ConcurrencyReqControl<PalletizingPackStockOutRequest, SRes>(lockerPalletizingSpcPackStockOut, "PalletizingSpcPackStockOut", request.Equip, request, _sxService.PalletizingSpcNotPackStockOut);
-        }
+        ///// <summary>
+        ///// 码垛SPC非控制装箱(缓存托盘)
+        ///// </summary>
+        ///// <param name="request"></param>
+        ///// <returns></returns>
+        //[HttpPost]
+        //public SRes PalletizingSpcNotPackStockOut2(PalletizingPackStockOutRequest request)
+        //{
+        //    lock (lockerPalletizingSpcPackStockOut)
+        //    {
+        //        return _sxService.PalletizingSpcNotPackStockOut2(request);
+        //    }
+        //    //return ConcurrencyReqControl<PalletizingPackStockOutRequest, SRes>(lockerPalletizingSpcPackStockOut, "PalletizingSpcPackStockOut", request.Equip, request, _sxService.PalletizingSpcNotPackStockOut);
+        //}
 
-        /// <summary>
-        /// 码垛SPC控制装箱(缓存托盘)
-        /// </summary>
-        /// <param name="request"></param>
-        /// <returns></returns>
-        [HttpPost]
-        public SRes PalletizingSpcPackStockOut2(PalletizingPackStockOutRequest request)
-        {
-            lock (lockerPalletizingSpcPackStockOut)
-            {
-                return _sxService.PalletizingSpcPackStockOut2(request);
-            }
-            //return ConcurrencyReqControl<PalletizingPackStockOutRequest, SRes>(lockerPalletizingSpcPackStockOut, "PalletizingSpcPackStockOut", request.Equip, request, _sxService.PalletizingSpcPackStockOut);
-        }
+        ///// <summary>
+        ///// 码垛SPC控制装箱(缓存托盘)
+        ///// </summary>
+        ///// <param name="request"></param>
+        ///// <returns></returns>
+        //[HttpPost]
+        //public SRes PalletizingSpcPackStockOut2(PalletizingPackStockOutRequest request)
+        //{
+        //    lock (lockerPalletizingSpcPackStockOut)
+        //    {
+        //        return _sxService.PalletizingSpcPackStockOut2(request);
+        //    }
+        //    //return ConcurrencyReqControl<PalletizingPackStockOutRequest, SRes>(lockerPalletizingSpcPackStockOut, "PalletizingSpcPackStockOut", request.Equip, request, _sxService.PalletizingSpcPackStockOut);
+        //}
 
         /// <summary>
-        /// 码垛SPC装箱(控制非控制一起执行)
+        /// 码垛SPC装箱(控制非控制一起执行) --在用
         /// </summary>
         /// <param name="request"></param>
         /// <returns></returns>
@@ -355,18 +351,15 @@ namespace wms.api.Controllers
 
                 var temp3 = _sxService.PalletizingPackBStockOut(request);
 
-                //var temp1 = _sxService.PalletizingSpcPackStockOut3(request);//控制箱根据新逻辑已经合并到非控制箱中
-
                 var temp2 = _sxService.PalletizingSpcNotPackStockOut3(request);
 
                 temp2.ResMsg = "非控制箱执行结果:" + temp2.ResMsg + ";B质量码垛:" + temp3.ResMsg;
                 return temp2;
             }
-            //return ConcurrencyReqControl<PalletizingPackStockOutRequest, SRes>(lockerPalletizingSpcPackStockOut, "PalletizingSpcPackStockOut", request.Equip, request, _sxService.PalletizingSpcPackStockOut);
         }
 
         /// <summary>
-        /// 码垛SPC装箱库存程序
+        /// 码垛SPC装箱库存程序 --在用
         /// </summary>
         /// <param name="request"></param>
         /// <returns></returns>
@@ -375,95 +368,92 @@ namespace wms.api.Controllers
         {
             lock (lockerPalletizingSpcPackPreALL)
             {
-                //var temp1 = _sxService.PalletizingSpcPackRulePre(request);
-                //var temp2 = _sxService.PalletizingSpcNotPackRulePre(request);//控制箱根据新逻辑已经合并到非控制箱中
                 var temp2 = _sxService.PalletizingSpcNotPackRulePre2(request);
                 temp2.ResMsg = "非控制箱预占库存执行结果:" + temp2.ResMsg; //+ ";控制箱预占库存执行结果:"+ temp1.ResMsg;
                 return temp2;
             }
-            //return ConcurrencyReqControl<PalletizingPackStockOutRequest, SRes>(lockerPalletizingSpcPackStockOut, "PalletizingSpcPackStockOut", request.Equip, request, _sxService.PalletizingSpcPackStockOut);
         }
 
-        /// <summary>
-        ///  码垛SPC非控制装箱计算预占箱号-去掉黑盘和批次限制
-        /// </summary>
-        /// <param name="request"></param>
-        /// <returns></returns>
-        [HttpPost]
-        public SRes PalletizingSpcNotPackRulePre2(PalletizingPackStockOutRequest request)
-        {
-            lock (lockerPalletizingSpcPackPreALL)
-            {
-                var temp2 = _sxService.PalletizingSpcNotPackRulePre2(request);
-                temp2.ResMsg = "非控制箱预占库存执行结果:" + temp2.ResMsg;
-                return temp2;
-            }
-            //return ConcurrencyReqControl<PalletizingPackStockOutRequest, SRes>(lockerPalletizingSpcPackStockOut, "PalletizingSpcPackStockOut", request.Equip, request, _sxService.PalletizingSpcPackStockOut);
-        }
+        ///// <summary>
+        /////  码垛SPC非控制装箱计算预占箱号-去掉黑盘和批次限制
+        ///// </summary>
+        ///// <param name="request"></param>
+        ///// <returns></returns>
+        //[HttpPost]
+        //public SRes PalletizingSpcNotPackRulePre2(PalletizingPackStockOutRequest request)
+        //{
+        //    lock (lockerPalletizingSpcPackPreALL)
+        //    {
+        //        var temp2 = _sxService.PalletizingSpcNotPackRulePre2(request);
+        //        temp2.ResMsg = "非控制箱预占库存执行结果:" + temp2.ResMsg;
+        //        return temp2;
+        //    }
+        //    //return ConcurrencyReqControl<PalletizingPackStockOutRequest, SRes>(lockerPalletizingSpcPackStockOut, "PalletizingSpcPackStockOut", request.Equip, request, _sxService.PalletizingSpcPackStockOut);
+        //}
 
-        /// <summary>
-        /// 码垛SPC装箱控制预占库存程序
-        /// </summary>
-        /// <param name="request"></param>
-        /// <returns></returns>
-        [HttpPost]
-        public SRes PalletizingSpcNotPackRulePre(PalletizingPackStockOutRequest request)
-        {
-            lock (lockerPalletizingSpcPackPreALL)
-            {
-                var temp2 = _sxService.PalletizingSpcNotPackRulePre(request);
-                return temp2;
-            }
-            //return ConcurrencyReqControl<PalletizingPackStockOutRequest, SRes>(lockerPalletizingSpcPackStockOut, "PalletizingSpcPackStockOut", request.Equip, request, _sxService.PalletizingSpcPackStockOut);
-        }
+        ///// <summary>
+        ///// 码垛SPC装箱控制预占库存程序
+        ///// </summary>
+        ///// <param name="request"></param>
+        ///// <returns></returns>
+        //[HttpPost]
+        //public SRes PalletizingSpcNotPackRulePre(PalletizingPackStockOutRequest request)
+        //{
+        //    lock (lockerPalletizingSpcPackPreALL)
+        //    {
+        //        var temp2 = _sxService.PalletizingSpcNotPackRulePre(request);
+        //        return temp2;
+        //    }
+        //    //return ConcurrencyReqControl<PalletizingPackStockOutRequest, SRes>(lockerPalletizingSpcPackStockOut, "PalletizingSpcPackStockOut", request.Equip, request, _sxService.PalletizingSpcPackStockOut);
+        //}
 
-        /// <summary>
-        /// 码垛SPC装箱控制预占库存程序
-        /// </summary>
-        /// <param name="request"></param>
-        /// <returns></returns>
-        [HttpPost]
-        public SRes PalletizingSpcPackRulePre(PalletizingPackStockOutRequest request)
-        {
-            lock (lockerPalletizingSpcPackPreALL)
-            {
-                var temp2 = _sxService.PalletizingSpcPackRulePre(request);
-                return temp2;
-            }
-            //return ConcurrencyReqControl<PalletizingPackStockOutRequest, SRes>(lockerPalletizingSpcPackStockOut, "PalletizingSpcPackStockOut", request.Equip, request, _sxService.PalletizingSpcPackStockOut);
-        }
+        ///// <summary>
+        ///// 码垛SPC装箱控制预占库存程序
+        ///// </summary>
+        ///// <param name="request"></param>
+        ///// <returns></returns>
+        //[HttpPost]
+        //public SRes PalletizingSpcPackRulePre(PalletizingPackStockOutRequest request)
+        //{
+        //    lock (lockerPalletizingSpcPackPreALL)
+        //    {
+        //        var temp2 = _sxService.PalletizingSpcPackRulePre(request);
+        //        return temp2;
+        //    }
+        //    //return ConcurrencyReqControl<PalletizingPackStockOutRequest, SRes>(lockerPalletizingSpcPackStockOut, "PalletizingSpcPackStockOut", request.Equip, request, _sxService.PalletizingSpcPackStockOut);
+        //}
 
-        /// <summary>
-        /// 码垛SPC非控制箱出预占库存
-        /// </summary>
-        /// <param name="request"></param>
-        /// <returns></returns>
-        [HttpPost]
-        public SRes PalletizingSpcNotPackStockOut3(PalletizingPackStockOutRequest request)
-        {
-            lock (lockerPalletizingSpcPackPreALL)
-            {
-                var temp2 = _sxService.PalletizingSpcNotPackStockOut3(request);
-                return temp2;
-            }
-            //return ConcurrencyReqControl<PalletizingPackStockOutRequest, SRes>(lockerPalletizingSpcPackStockOut, "PalletizingSpcPackStockOut", request.Equip, request, _sxService.PalletizingSpcPackStockOut);
-        }
+        ///// <summary>
+        ///// 码垛SPC非控制箱出预占库存
+        ///// </summary>
+        ///// <param name="request"></param>
+        ///// <returns></returns>
+        //[HttpPost]
+        //public SRes PalletizingSpcNotPackStockOut3(PalletizingPackStockOutRequest request)
+        //{
+        //    lock (lockerPalletizingSpcPackPreALL)
+        //    {
+        //        var temp2 = _sxService.PalletizingSpcNotPackStockOut3(request);
+        //        return temp2;
+        //    }
+        //    //return ConcurrencyReqControl<PalletizingPackStockOutRequest, SRes>(lockerPalletizingSpcPackStockOut, "PalletizingSpcPackStockOut", request.Equip, request, _sxService.PalletizingSpcPackStockOut);
+        //}
 
-        /// <summary>
-        /// 码垛SPC控制箱出预占库存
-        /// </summary>
-        /// <param name="request"></param>
-        /// <returns></returns>
-        [HttpPost]
-        public SRes PalletizingSpcPackStockOut3(PalletizingPackStockOutRequest request)
-        {
-            lock (lockerPalletizingSpcPackPreALL)
-            {
-                var temp2 = _sxService.PalletizingSpcPackStockOut3(request);
-                return temp2;
-            }
-            //return ConcurrencyReqControl<PalletizingPackStockOutRequest, SRes>(lockerPalletizingSpcPackStockOut, "PalletizingSpcPackStockOut", request.Equip, request, _sxService.PalletizingSpcPackStockOut);
-        }
+        ///// <summary>
+        ///// 码垛SPC控制箱出预占库存
+        ///// </summary>
+        ///// <param name="request"></param>
+        ///// <returns></returns>
+        //[HttpPost]
+        //public SRes PalletizingSpcPackStockOut3(PalletizingPackStockOutRequest request)
+        //{
+        //    lock (lockerPalletizingSpcPackPreALL)
+        //    {
+        //        var temp2 = _sxService.PalletizingSpcPackStockOut3(request);
+        //        return temp2;
+        //    }
+        //    //return ConcurrencyReqControl<PalletizingPackStockOutRequest, SRes>(lockerPalletizingSpcPackStockOut, "PalletizingSpcPackStockOut", request.Equip, request, _sxService.PalletizingSpcPackStockOut);
+        //}
 
         /// <summary>
         /// B质量码垛装箱
@@ -1007,7 +997,20 @@ namespace wms.api.Controllers
             {
                 return _sxService.StockChange(reqDto);
             }
-            //return ConcurrencyReqControl<SxStockChangeRequest, SRes>(lockerlockerStockChange, "SxStockChange", reqDto.BillCode, reqDto, _sxService.StockChange);
+        }
+
+        /// <summary>
+        /// 预赋扭转值
+        /// </summary>
+        /// <param name="reqDto"></param>
+        /// <returns></returns>
+        [HttpPost]
+        public SRes PreSpecifiedTorsionValues(SxStockChangeRequest reqDto)
+        {
+            lock (lockerlockerPreSpecifiedTorsionValues)
+            {
+                return _sxService.PreSpecifiedTorsionValues(reqDto);
+            }
         }
 
         /// <summary>

+ 8 - 8
wms.dto/request/fj/CurtainProductionOrderRequest.cs

@@ -86,14 +86,14 @@ namespace wms.dto.request.fj
         /// </summary>
         public decimal SolderJointMax { get; set; }
 
-        ///// <summary>
-        ///// 扭转目标值
-        ///// </summary>
-        //public string TorsionTargetval { get; set; }
-        ///// <summary>
-        ///// 扭转目标值范围
-        ///// </summary>
-        //public string TorsionTargetscope { get; set; }
+        /// <summary>
+        /// 扭转目标值
+        /// </summary>
+        public string TorsionTargetval { get; set; }
+        /// <summary>
+        /// 扭转目标值范围
+        /// </summary>
+        public string TorsionTargetscope { get; set; }
 
         /// <summary>
         /// 每箱焊点总数

+ 102 - 0
wms.dto/response/sx/StockTemp.cs

@@ -6,39 +6,141 @@ namespace wms.dto.response.sx
 {
     public class StockTemp
     {
+        /// <summary>
+        /// 物料编码
+        /// </summary>
         public string MatCode { get; set; }
+        /// <summary>
+        /// 条码号
+        /// </summary>
         public string InvBarCode { get; set; }
+        /// <summary>
+        /// 等级
+        /// </summary>
         public string Grade { get; set; }
+        /// <summary>
+        /// 库存状态
+        /// </summary>
         public string InvStateCode { get; set; }
+        /// <summary>
+        /// 生产时间
+        /// </summary>
         public DateTime ProductTime { get; set; }
+        /// <summary>
+        /// 第一次入库时间
+        /// </summary>
         public DateTime OneInTime { get; set; }
+        /// <summary>
+        /// 机台组
+        /// </summary>
         public string WbGroupCode { get; set; }
+        /// <summary>
+        /// 是否扭转检测
+        /// </summary>
         public bool IsTorsChk { get; set; }
+        /// <summary>
+        /// 扭转次数
+        /// </summary>
         public int TorsChkQty { get; set; }
+        /// <summary>
+        /// 扭转值
+        /// </summary>
         public decimal? TorsChkValue { get; set; }
+        /// <summary>
+        /// 扭转时长
+        /// </summary>
         public decimal HoldTime { get; set; }
+        /// <summary>
+        /// 生产机台号
+        /// </summary>
         public string ProductMachCode { get; set; }
+        /// <summary>
+        /// 是否控制盘
+        /// </summary>
         public bool IsControlpanel { get; set; }
+        /// <summary>
+        /// 工字轮类型
+        /// </summary>
         public string HWTypeCode { get; set; }
+        /// <summary>
+        /// 焊点数量
+        /// </summary>
         public decimal SolderCount { get; set; }
+        /// <summary>
+        /// 返工标记
+        /// </summary>
         public bool IsRework { get; set; }
+        /// <summary>
+        /// 是否黑盘
+        /// </summary>
         public bool IsBlack { get; set; }
+        /// <summary>
+        /// 列
+        /// </summary>
         public int Col { get; set; }
+        /// <summary>
+        /// 层
+        /// </summary>
         public int Layer { get; set; }
+        /// <summary>
+        /// 货架
+        /// </summary>
         public string Shelf { get; set; }
+        /// <summary>
+        /// 深度
+        /// </summary>
         public int Depth { get; set; }
+        /// <summary>
+        /// 货位号
+        /// </summary>
         public string Code { get; set; }
+        /// <summary>
+        /// 巷道
+        /// </summary>
         public int Tunnel { get; set; }
+        /// <summary>
+        /// 堆垛机
+        /// </summary>
         public string SCRel { get; set; }
+        /// <summary>
+        /// 楼层
+        /// </summary>
         public int Floor { get; set; }
+        /// <summary>
+        /// 仓库编码
+        /// </summary>
         public string WarehouseCode { get; set; }
+        /// <summary>
+        /// 容器编码
+        /// </summary>
         public string ContGrpBarCode { get; set; }
+        /// <summary>
+        /// 容器ID
+        /// </summary>
         public long? ContGrpId { get; set; }
+        /// <summary>
+        /// 货位ID
+        /// </summary>
         public  long Id { get; set; }
+        /// <summary>
+        /// 货位状态
+        /// </summary>
         public LocationState StateNum { get; set; }
+        /// <summary>
+        /// SKU编码
+        /// </summary>
         public string SkuCode { get; set; }
+        /// <summary>
+        /// 绕向
+        /// </summary>
         public string Wind { get; set; }
+        /// <summary>
+        /// 单号
+        /// </summary>
         public string InDocsNo { get; set; }
+        /// <summary>
+        /// 批次号
+        /// </summary>
         public string BatchNo { get; set; }
     }
 

+ 2225 - 0
wms.service/Extensions/LayerPacking/LayerPackingService.cs

@@ -0,0 +1,2225 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using wms.service.Extensions.LayerPacking.model;
+
+namespace wms.service.Extensions.LayerPacking
+{
+    /// <summary>
+    /// 层配装箱服务
+    /// </summary>
+    public class LayerPackingService
+    {
+        /// <summary>
+        /// 每层产品数
+        /// </summary>
+        private const int PRODUCTS_PER_LAYER = 12;
+
+        /// <summary>
+        /// 每箱层数
+        /// </summary>
+        private readonly int LAYERS_PER_BOX;
+
+        /// <summary>
+        /// 每箱产品数
+        /// </summary>
+        private readonly int PRODUCTS_PER_BOX;
+
+        /// <summary>
+        /// 可用于装箱的产品
+        /// </summary>
+        private readonly List<LayerPackingProduct> _productPool;
+
+        /// <summary>
+        /// 约束条件
+        /// </summary>
+        private readonly LayerPackingConstraints _constraints;
+
+        /// <summary>
+        /// 随机数
+        /// </summary>
+        private readonly Random _random = new Random();
+
+        /// <summary>
+        /// 动态大极值阈值
+        /// </summary>
+        private decimal _dynamicHighThreshold;
+
+        /// <summary>
+        /// 动态小极值阈值
+        /// </summary>
+        private decimal _dynamicLowThreshold;
+
+        /// <summary>
+        /// 取前后15%作为极值
+        /// </summary>
+        private const decimal EXTREME_PERCENTAGE = 0.15m;
+
+        /// <summary>
+        /// 最少取5%
+        /// </summary>
+        private const decimal MIN_EXTREME_PERCENTAGE = 0.05m;
+
+        /// <summary>
+        /// 最多取25%
+        /// </summary>
+        private const decimal MAX_EXTREME_PERCENTAGE = 0.25m;
+
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        /// <param name="products">产品数</param>
+        /// <param name="constraints">约束条件</param>
+        public LayerPackingService(List<LayerPackingProduct> products, LayerPackingConstraints constraints)
+        {
+            _productPool = products.Where(p => !p.IsUsed).ToList();
+            _constraints = constraints;
+
+            // 从约束中获取每箱/层产品数
+            PRODUCTS_PER_BOX = constraints.ProductsPerBox;
+            LAYERS_PER_BOX = constraints.LayersPerBox;
+
+            // 根据产品池动态更新目标值
+            _constraints.UpdateTargetValues(_productPool);
+
+            // 计算动态极值阈值
+            CalculateDynamicExtremeThresholds();
+        }
+
+        /// <summary>
+        /// 动态计算极值阈值
+        /// </summary>
+        private void CalculateDynamicExtremeThresholds()
+        {
+            var availableProducts = _productPool.Where(p => !p.IsUsed).ToList();
+
+            // 按扭转值排序
+            var sortedValues = availableProducts.Select(p => p.TorsChkValue).OrderBy(v => v).ToList();
+
+            // 计算要取的极值数量
+            int totalCount = sortedValues.Count;
+            int extremeCount = (int)(totalCount * EXTREME_PERCENTAGE);
+
+            // 确保至少有一定数量的极值
+            extremeCount = Math.Max((int)(totalCount * MIN_EXTREME_PERCENTAGE), extremeCount);
+            extremeCount = Math.Min((int)(totalCount * MAX_EXTREME_PERCENTAGE), extremeCount);
+            extremeCount = Math.Max(1, extremeCount); // 至少1个
+
+            // 计算阈值
+            if (extremeCount < totalCount)
+            {
+                _dynamicLowThreshold = sortedValues[extremeCount - 1];
+                _dynamicHighThreshold = sortedValues[totalCount - extremeCount];
+            }
+            else
+            {
+                // 如果产品太少,使用中位数划分
+                decimal median = sortedValues[totalCount / 2];
+                _dynamicLowThreshold = sortedValues.First();
+                _dynamicHighThreshold = sortedValues.Last();
+            }
+
+            // 根据约束条件进行调整
+            AdjustThresholdsBasedOnConstraints(availableProducts);
+        }
+
+        /// <summary>
+        /// 根据约束条件调整阈值(基于正态分布特性)
+        /// </summary>
+        /// <param name="availableProducts">有效产品池</param>
+        private void AdjustThresholdsBasedOnConstraints(List<LayerPackingProduct> availableProducts)
+        {
+            if (availableProducts == null || !availableProducts.Any()) return;
+
+            // 计算产品池的统计信息
+            var sortedValues = availableProducts.Select(p => p.TorsChkValue).OrderBy(v => v).ToList();
+            decimal avgValue = sortedValues.Average();
+            int count = sortedValues.Count;
+
+            // 计算标准差
+            double variance = sortedValues.Sum(v => Math.Pow((double)(v - avgValue), 2)) / count;
+            decimal stdDev = (decimal)Math.Sqrt(variance);
+
+            // 计算分位数(用于理解分布)
+            decimal p10 = GetPercentile(sortedValues, 0.10m); // 第10百分位
+            decimal p25 = GetPercentile(sortedValues, 0.25m); // 第一四分位数
+            decimal p50 = GetPercentile(sortedValues, 0.50m); // 中位数
+            decimal p75 = GetPercentile(sortedValues, 0.75m); // 第三四分位数
+            decimal p90 = GetPercentile(sortedValues, 0.90m); // 第90百分位
+
+            // 使用单个产品目标值
+            decimal idealProductValue = _constraints.P;
+
+            // 基于正态分布的特性调整阈值
+            // 在正态分布中:
+            // - 约68%的数据在均值±1σ范围内
+            // - 约95%的数据在均值±2σ范围内
+            // - 约99.7%的数据在均值±3σ范围内
+
+            // 根据产品池平均值与目标值的偏差来调整
+            decimal avgToTargetDiff = avgValue - idealProductValue;
+
+            if (Math.Abs(avgToTargetDiff) > stdDev * 0.5m)
+            {
+                // 产品池显著偏离目标值
+                if (avgToTargetDiff > 0)
+                {
+                    // 产品池偏高,需要更多使用高值产品(降低高阈值)
+                    // 使用1.5σ作为基准(约86.6%的数据在此范围内)
+                    _dynamicHighThreshold = avgValue + stdDev * 1.5m;
+
+                    // 低阈值可以相对严格一些,使用2σ
+                    _dynamicLowThreshold = avgValue - stdDev * 2.0m;
+                }
+                else
+                {
+                    // 产品池偏低,需要更多使用低值产品(提高低阈值)
+                    _dynamicLowThreshold = avgValue - stdDev * 1.5m;
+
+                    // 高阈值可以相对严格一些
+                    _dynamicHighThreshold = avgValue + stdDev * 2.0m;
+                }
+            }
+            else
+            {
+                // 产品池接近目标值,使用标准的正态分布阈值
+                // 使用2σ原则,这样约95%的产品在正常范围内,5%为极值
+                _dynamicHighThreshold = avgValue + stdDev * 2.0m;
+                _dynamicLowThreshold = avgValue - stdDev * 2.0m;
+
+                // 如果标准差太小(数据过于集中),适当放宽
+                if (stdDev < 0.2m)
+                {
+                    _dynamicHighThreshold = avgValue + 0.4m;
+                    _dynamicLowThreshold = avgValue - 0.4m;
+                }
+            }
+
+            // 根据实际分布的百分位数进行微调
+            // 确保至少有5-15%的产品被识别为极值
+            int currentHighCount = availableProducts.Count(p => p.TorsChkValue >= _dynamicHighThreshold);
+            int currentLowCount = availableProducts.Count(p => p.TorsChkValue <= _dynamicLowThreshold);
+
+            // 目标:每端有5-15%的极值
+            int minExtremeCount = Math.Max(1, (int)(count * 0.05m));
+            int maxExtremeCount = Math.Max(2, (int)(count * 0.15m));
+
+            // 如果极高值太少,使用百分位数调整
+            if (currentHighCount < minExtremeCount)
+            {
+                // 使用第85-90百分位作为高阈值
+                _dynamicHighThreshold = p90;
+            }
+            else if (currentHighCount > maxExtremeCount)
+            {
+                // 极高值太多,收紧标准,使用2.5σ
+                _dynamicHighThreshold = avgValue + stdDev * 2.5m;
+            }
+
+            // 如果极低值太少,使用百分位数调整
+            if (currentLowCount < minExtremeCount)
+            {
+                // 使用第10-15百分位作为低阈值
+                _dynamicLowThreshold = p10;
+            }
+            else if (currentLowCount > maxExtremeCount)
+            {
+                // 极低值太多,收紧标准
+                _dynamicLowThreshold = avgValue - stdDev * 2.5m;
+            }
+
+            // 基于正态分布特性确保阈值间隔
+            // 正态分布中,极值应该分布在两端,中间应有足够间隔
+            decimal minGap = Math.Max(stdDev * 3.0m, 1.0m); // 至少3个标准差或1.0
+
+            if (_dynamicHighThreshold - _dynamicLowThreshold < minGap)
+            {
+                // 基于均值对称调整
+                decimal halfGap = minGap / 2;
+                _dynamicHighThreshold = avgValue + halfGap;
+                _dynamicLowThreshold = avgValue - halfGap;
+            }
+
+            // 最终边界检查(基于正态分布的3σ原则)
+            decimal maxBound = avgValue + stdDev * 3.0m;
+            decimal minBound = avgValue - stdDev * 3.0m;
+
+            _dynamicHighThreshold = Math.Min(_dynamicHighThreshold, maxBound);
+            _dynamicLowThreshold = Math.Max(_dynamicLowThreshold, minBound);
+        }
+
+        /// <summary>
+        /// 计算百分位数
+        /// </summary>
+        private decimal GetPercentile(List<decimal> sortedValues, decimal percentile)
+        {
+            if (sortedValues.Count == 0) return 0;
+
+            if (sortedValues.Count == 1) return sortedValues[0];
+
+            decimal index = percentile * (sortedValues.Count - 1);
+            int lower = (int)Math.Floor(index);
+            int upper = (int)Math.Ceiling(index);
+
+            if (lower == upper) return sortedValues[lower];
+
+            decimal weight = index - lower;
+            return sortedValues[lower] * (1 - weight) + sortedValues[upper] * weight;
+        }
+
+        /// <summary>
+        /// 更新极值阈值(在每次装箱后调用) --线上版本不会触发
+        /// </summary>
+        private void UpdateExtremeThresholds()
+        {
+            var availableProducts = _productPool.Where(p => !p.IsUsed).ToList();
+
+            // 如果剩余产品数量显著减少,重新计算阈值
+            if (availableProducts.Count < _productPool.Count * 0.7 || availableProducts.Count < 200)
+            {
+                CalculateDynamicExtremeThresholds();
+            }
+        }
+
+        /// <summary>
+        /// 装箱
+        /// </summary>
+        public LayerPackingResult PackBoxe()
+        {
+            var result = new LayerPackingResult();
+            var startTime = DateTime.Now;
+            int boxNumber = 1;
+
+            var availableProducts = _productPool.Where(p => !p.IsUsed).ToList();
+
+            // 检查是否有足够的产品
+            if (availableProducts.Count < PRODUCTS_PER_BOX)
+            {
+                result.RemainingProducts = availableProducts.Count;
+                result.RemainingSolderProducts = availableProducts.Count(p => p.IsSolderProduct);
+            }
+
+            // 每次装箱前更新目标值(基于当前可用产品)
+            _constraints.UpdateTargetValues(availableProducts);
+
+            // 记录当前产品池平均值
+            decimal currentPoolAverage = availableProducts.Average(p => p.TorsChkValue);
+
+            // 尝试装箱
+            var box = FindOptimalBox();
+
+            if (box != null)
+            {
+                box.BoxId = $"BOX{boxNumber:D4}";
+
+                // 记录装箱时的约束信息
+                box.MinBoxTarget = _constraints.MinBoxTotal;
+                box.MaxBoxTarget = _constraints.MaxBoxTotal;
+                box.BoxTarget = _constraints.X;
+                box.ProductPoolAverage = currentPoolAverage;
+
+                // 为每层记录约束信息
+                foreach (var layer in box.Layers)
+                {
+                    layer.MinLayerTarget = _constraints.MinLayerTotal;
+                    layer.MaxLayerTarget = _constraints.MaxLayerTotal;
+                    layer.LayerTarget = _constraints.C;
+                }
+
+                result.Boxes.Add(box);
+
+                // 标记产品为已使用
+                MarkProductsAsUsed(box);
+
+                Console.WriteLine($"  ✓ 第 {boxNumber} 箱装配成功,扭转值总和: {box.TotalTorsion:F2},焊点盘数: {box.SolderProductCount},焊点总数: {box.TotalSolderJoints}");
+                boxNumber++;
+
+                // 更新极值阈值
+                if (boxNumber % 5 == 0) // 每5箱更新一次
+                {
+                    UpdateExtremeThresholds();
+                }
+            }
+            else
+            {
+                Console.WriteLine($"  ✗ 无法继续装箱,剩余产品不满足约束条件");
+                result.RemainingProducts = availableProducts.Count;
+                result.RemainingSolderProducts = availableProducts.Count(p => p.IsSolderProduct);
+            }
+
+            result.TotalProductsUsed = result.Boxes.Sum(b => b.GetTotalProductCount());
+            result.TotalSolderProductsUsed = result.Boxes.Sum(b => b.SolderProductCount);
+            result.TotalProcessingTime = DateTime.Now - startTime;
+
+            return result;
+        }
+
+        /// <summary>
+        /// 标记箱中的产品为已使用
+        /// </summary>
+        private void MarkProductsAsUsed(LayerPackingBoxInfo box)
+        {
+            foreach (var layer in box.Layers)
+            {
+                foreach (var product in layer.Products)
+                {
+                    var originalProduct = _productPool.First(p => p.Id == product.Id);
+                    originalProduct.IsUsed = true;
+                }
+            }
+        }
+
+        /// <summary>
+        /// 主优化方法 - 找到最优装箱方案(极值必须成对+尽量靠近目标值)
+        /// </summary>
+        public LayerPackingBoxInfo FindOptimalBox()
+        {
+            LayerPackingBoxInfo bestBox = null;
+            decimal bestScore = decimal.MaxValue;
+
+            // 获取当前可用产品
+            var currentAvailableProducts = _productPool.Where(p => !p.IsUsed).ToList();
+            if (currentAvailableProducts.Count < PRODUCTS_PER_BOX) return null;
+
+            // 策略1:目标导向装箱策略 - 最高优先级
+            var targetOrientedBox = TargetOrientedPacking();
+            if (targetOrientedBox != null && targetOrientedBox.IsValid(_constraints) && targetOrientedBox.HasValidExtremePairs(_dynamicHighThreshold, _dynamicLowThreshold))
+            {
+                var score = CalculateBoxScore(targetOrientedBox);
+                score *= 0.6m; // 最高优先级
+                if (score < bestScore)
+                {
+                    bestScore = score;
+                    bestBox = targetOrientedBox;
+                }
+            }
+
+            // 策略2:极值优先成箱策略(同时考虑目标值)
+            var extremeBox = ExtremeValuePackingWithGuarantee();
+            if (extremeBox != null && extremeBox.IsValid(_constraints) && extremeBox.HasValidExtremePairs(_dynamicHighThreshold, _dynamicLowThreshold))
+            {
+                var score = CalculateBoxScore(extremeBox);
+                score *= 0.65m;
+                if (score < bestScore)
+                {
+                    bestScore = score;
+                    bestBox = extremeBox;
+                }
+            }
+
+            // 策略3:智能极值策略
+            var smartExtremeBox = SmartExtremeValuePacking();
+            if (smartExtremeBox != null && smartExtremeBox.IsValid(_constraints) && smartExtremeBox.HasValidExtremePairs(_dynamicHighThreshold, _dynamicLowThreshold))
+            {
+                var score = CalculateBoxScore(smartExtremeBox);
+                score *= 0.7m;
+                if (score < bestScore)
+                {
+                    bestScore = score;
+                    bestBox = smartExtremeBox;
+                }
+            }
+
+            // 策略4:精确贪心策略
+            var preciseGreedyBox = PreciseGreedyPacking();
+            if (preciseGreedyBox != null && preciseGreedyBox.IsValid(_constraints) && preciseGreedyBox.HasValidExtremePairs(_dynamicHighThreshold, _dynamicLowThreshold))
+            {
+                var score = CalculateBoxScore(preciseGreedyBox);
+                score *= 0.7m;
+                if (score < bestScore)
+                {
+                    bestScore = score;
+                    bestBox = preciseGreedyBox;
+                }
+            }
+
+            // 策略5:快速随机尝试(目标导向)
+            int maxAttempts = Math.Min(500, Math.Max(200, currentAvailableProducts.Count / 3));
+            var lockObj = new object();
+
+            Parallel.For(0, maxAttempts, i =>
+            {
+                try
+                {
+                    var box = i % 3 == 0 ? RandomTargetOrientedPacking() : i % 3 == 1 ? RandomExtremeValuePacking() : RandomQuickPacking();
+
+                    if (box != null && box.IsValid(_constraints) && box.HasValidExtremePairs(_dynamicHighThreshold, _dynamicLowThreshold))
+                    {
+                        var score = CalculateBoxScore(box);
+                        lock (lockObj)
+                        {
+                            if (score < bestScore)
+                            {
+                                bestScore = score;
+                                bestBox = box;
+                            }
+                        }
+                    }
+                }
+                catch (Exception ex)
+                {
+                    // 记录错误但继续执行
+                }
+            });
+
+            // 如果仍未找到解,使用激进搜索
+            if (bestBox == null)
+            {
+                bestBox = AggressiveSearch();
+            }
+
+            return bestBox;
+        }
+
+        /// <summary>
+        /// 目标导向装箱策略 - 优先考虑接近目标值
+        /// </summary>
+        private LayerPackingBoxInfo TargetOrientedPacking()
+        {
+            var availableProducts = _productPool.Where(p => !p.IsUsed).ToList();
+            if (availableProducts.Count < PRODUCTS_PER_BOX) return null;
+
+            // 分类产品
+            var extremeHigh = availableProducts.Where(p => p.TorsChkValue >= _dynamicHighThreshold).ToList();
+            var extremeLow = availableProducts.Where(p => p.TorsChkValue <= _dynamicLowThreshold).ToList();
+            var normal = availableProducts.Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold)
+                .OrderBy(p => Math.Abs(p.TorsChkValue - _constraints.P)) // 按接近单个产品目标值排序
+                .ToList();
+
+            var box = new LayerPackingBoxInfo();
+            var usedProducts = new HashSet<long>();
+            decimal boxTargetSum = _constraints.X;
+            decimal currentBoxSum = 0;
+
+            // 构建每一层
+            for (int layerIndex = 0; layerIndex < LAYERS_PER_BOX; layerIndex++)
+            {
+                // 计算剩余层数和每层的理想贡献
+                int remainingLayers = LAYERS_PER_BOX - layerIndex;
+                decimal remainingTarget = boxTargetSum - currentBoxSum;
+                decimal idealLayerSum = remainingTarget / remainingLayers;
+
+                // 如果理想层和接近层目标值,使用层目标值
+                if (Math.Abs(idealLayerSum - _constraints.C) < _constraints.C1 * 0.3m)
+                {
+                    idealLayerSum = _constraints.C;
+                }
+
+                var layer = BuildTargetOrientedLayer(
+                    extremeHigh.Where(p => !usedProducts.Contains(p.Id)).ToList(),
+                    extremeLow.Where(p => !usedProducts.Contains(p.Id)).ToList(),
+                    normal.Where(p => !usedProducts.Contains(p.Id)).ToList(),
+                    idealLayerSum,
+                    usedProducts
+                );
+
+                if (layer == null || layer.Products.Count != PRODUCTS_PER_LAYER) return null;
+
+                box.Layers.Add(layer);
+                currentBoxSum += layer.TotalTorsion;
+            }
+
+            // 检查并修复焊点约束
+            if (!CheckAndFixSolderConstraints(box, availableProducts)) return null;
+
+            return box;
+        }
+
+        /// <summary>
+        /// 构建目标导向的层
+        /// </summary>
+        /// <param name="extremeHigh">极高值产品列表</param>
+        /// <param name="extremeLow">极低值产品列表</param>
+        /// <param name="normal">普通产品列表</param>
+        /// <param name="targetSum">本层目标总值</param>
+        /// <param name="usedProducts">已用过的产品ID</param>
+        /// <returns></returns>
+        private LayerPackingLayerInfo BuildTargetOrientedLayer(List<LayerPackingProduct> extremeHigh, List<LayerPackingProduct> extremeLow, List<LayerPackingProduct> normal, decimal targetSum, HashSet<long> usedProducts)
+        {
+            var layer = new LayerPackingLayerInfo();
+            decimal currentSum = 0;
+
+            // 决定是否使用极值对
+            bool useExtremePair = false;
+            LayerPackingProduct selectedHigh = null;
+            LayerPackingProduct selectedLow = null;
+
+            if (extremeHigh.Any() && extremeLow.Any())
+            {
+                // 评估所有可能的极值对
+                decimal bestPairScore = decimal.MaxValue;
+
+                foreach (var high in extremeHigh.Take(Math.Min(5, extremeHigh.Count)))
+                {
+                    foreach (var low in extremeLow.Take(Math.Min(5, extremeLow.Count)))
+                    {
+                        decimal pairSum = high.TorsChkValue + low.TorsChkValue;
+
+                        // 计算使用这对极值后,剩余产品是否能达到目标
+                        decimal remainingTarget = targetSum - pairSum;
+                        decimal avgNeeded = remainingTarget / (PRODUCTS_PER_LAYER - 2);
+
+                        // 检查是否有足够的普通产品能满足需求
+                        var viableNormal = normal.Where(p => !usedProducts.Contains(p.Id))
+                            .Where(p => Math.Abs(p.TorsChkValue - avgNeeded) < 1.0m)
+                            .ToList();
+
+                        if (viableNormal.Count >= PRODUCTS_PER_LAYER - 2)
+                        {
+                            decimal score = Math.Abs(pairSum - targetSum * 2 / PRODUCTS_PER_LAYER); // 极值对占层的比例评分
+                            if (score < bestPairScore)
+                            {
+                                bestPairScore = score;
+                                selectedHigh = high;
+                                selectedLow = low;
+                                useExtremePair = true;
+                            }
+                        }
+                    }
+                }
+            }
+
+            // 如果决定使用极值对
+            if (useExtremePair && selectedHigh != null && selectedLow != null)
+            {
+                layer.Products.Add(selectedHigh);
+                layer.Products.Add(selectedLow);
+                usedProducts.Add(selectedHigh.Id);
+                usedProducts.Add(selectedLow.Id);
+                currentSum += selectedHigh.TorsChkValue + selectedLow.TorsChkValue;
+            }
+
+            // 使用动态规划思想填充剩余位置
+            int remainingSlots = PRODUCTS_PER_LAYER - layer.Products.Count;
+            var candidates = normal.Where(p => !usedProducts.Contains(p.Id)).ToList();
+
+            // 贪心选择最接近理想值的产品
+            while (remainingSlots > 0 && candidates.Any())
+            {
+                decimal remainingTarget = targetSum - currentSum;
+                decimal idealValue = remainingTarget / remainingSlots;
+
+                // 选择最接近理想值的产品
+                var best = candidates
+                    .OrderBy(p => Math.Abs(p.TorsChkValue - idealValue))
+                    .ThenBy(p => Math.Abs(p.TorsChkValue - _constraints.P)) // 其次考虑接近单个产品目标值
+                    .FirstOrDefault();
+
+                if (best != null)
+                {
+                    layer.Products.Add(best);
+                    usedProducts.Add(best.Id);
+                    currentSum += best.TorsChkValue;
+                    candidates.Remove(best);
+                    remainingSlots--;
+                }
+                else
+                {
+                    break;
+                }
+            }
+
+            // 如果还有空位,从所有可用产品中填充
+            if (remainingSlots > 0)
+            {
+                var allAvailable = extremeHigh.Concat(extremeLow).Concat(normal)
+                    .Where(p => !usedProducts.Contains(p.Id))
+                    .Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold)
+                    .ToList();
+
+                while (remainingSlots > 0 && allAvailable.Any())
+                {
+                    decimal remainingTarget = targetSum - currentSum;
+                    decimal idealValue = remainingTarget / remainingSlots;
+
+                    var best = allAvailable.OrderBy(p => Math.Abs(p.TorsChkValue - idealValue)).First();
+                    layer.Products.Add(best);
+                    usedProducts.Add(best.Id);
+                    currentSum += best.TorsChkValue;
+                    allAvailable.Remove(best);
+                    remainingSlots--;
+                }
+            }
+
+            return layer;
+        }
+
+        /// <summary>
+        /// 精确贪心装箱策略
+        /// </summary>
+        private LayerPackingBoxInfo PreciseGreedyPacking()
+        {
+            var availableProducts = _productPool.Where(p => !p.IsUsed).ToList();
+            if (availableProducts.Count < PRODUCTS_PER_BOX) return null;
+
+            var box = new LayerPackingBoxInfo();
+            var usedProducts = new HashSet<long>();
+
+            // 分类产品
+            var extremeHigh = availableProducts.Where(p => p.TorsChkValue >= _dynamicHighThreshold).ToList();
+            var extremeLow = availableProducts.Where(p => p.TorsChkValue <= _dynamicLowThreshold).ToList();
+            var normal = availableProducts.Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold).ToList();
+
+            decimal boxTargetSum = _constraints.X;
+            decimal currentBoxSum = 0;
+
+            // 构建每一层
+            for (int layerIndex = 0; layerIndex < LAYERS_PER_BOX; layerIndex++)
+            {
+                var layer = new LayerPackingLayerInfo();
+                decimal layerTargetSum = _constraints.C;
+
+                // 动态调整层目标,使整箱更接近目标
+                if (layerIndex > 0)
+                {
+                    decimal remainingBoxTarget = boxTargetSum - currentBoxSum;
+                    int remainingLayers = LAYERS_PER_BOX - layerIndex;
+                    decimal adjustedLayerTarget = remainingBoxTarget / remainingLayers;
+
+                    // 如果调整后的目标在允许范围内,使用它
+                    if (adjustedLayerTarget >= _constraints.MinLayerTotal && adjustedLayerTarget <= _constraints.MaxLayerTotal)
+
+                    {
+                        layerTargetSum = adjustedLayerTarget;
+                    }
+                }
+
+                // 构建层
+                decimal currentLayerSum = 0;
+
+                // 考虑是否使用极值对
+                bool useExtremePair = extremeHigh.Any(p => !usedProducts.Contains(p.Id)) && extremeLow.Any(p => !usedProducts.Contains(p.Id)) && (layerIndex < 3 || _random.NextDouble() < 0.3);
+
+                if (useExtremePair)
+                {
+                    // 选择能使层和最接近目标的极值对
+                    var availableHigh = extremeHigh.Where(p => !usedProducts.Contains(p.Id)).ToList();
+                    var availableLow = extremeLow.Where(p => !usedProducts.Contains(p.Id)).ToList();
+
+                    LayerPackingProduct bestHigh = null;
+                    LayerPackingProduct bestLow = null;
+                    decimal bestScore = decimal.MaxValue;
+
+                    foreach (var high in availableHigh.Take(3))
+                    {
+                        foreach (var low in availableLow.Take(3))
+                        {
+                            decimal pairSum = high.TorsChkValue + low.TorsChkValue;
+                            decimal remainingSlots = PRODUCTS_PER_LAYER - 2;
+                            decimal avgNeeded = (layerTargetSum - pairSum) / remainingSlots;
+
+                            // 评分:考虑平均值是否在合理范围内
+                            decimal score = Math.Abs(avgNeeded - _constraints.P);
+                            if (score < bestScore)
+                            {
+                                bestScore = score;
+                                bestHigh = high;
+                                bestLow = low;
+                            }
+                        }
+                    }
+
+                    if (bestHigh != null && bestLow != null)
+                    {
+                        layer.Products.Add(bestHigh);
+                        layer.Products.Add(bestLow);
+                        usedProducts.Add(bestHigh.Id);
+                        usedProducts.Add(bestLow.Id);
+                        currentLayerSum += bestHigh.TorsChkValue + bestLow.TorsChkValue;
+                    }
+                }
+
+                // 贪心填充剩余位置
+                var normalAvailable = normal.Where(p => !usedProducts.Contains(p.Id)).ToList();
+
+                for (int i = layer.Products.Count; i < PRODUCTS_PER_LAYER; i++)
+                {
+                    decimal remaining = layerTargetSum - currentLayerSum;
+                    decimal slotsLeft = PRODUCTS_PER_LAYER - i;
+                    decimal idealValue = remaining / slotsLeft;
+
+                    var candidates = normalAvailable.OrderBy(p => Math.Abs(p.TorsChkValue - idealValue)).Take(20).ToList(); // 考虑前20个最接近的
+
+                    if (!candidates.Any())
+                    {
+                        // 如果普通产品不够,从所有产品中选择
+                        candidates = availableProducts
+                            .Where(p => !usedProducts.Contains(p.Id))
+                            .Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold)
+                            .OrderBy(p => Math.Abs(p.TorsChkValue - idealValue))
+                            .Take(10)
+                            .ToList();
+                    }
+
+                    if (!candidates.Any()) return null;
+
+                    // 选择最优的产品(考虑焊点约束)
+                    var selected = candidates.First();
+                    if (_constraints.SolderMaxCount > 0 &&
+                        box.SolderProductCount + layer.SolderProductCount >= _constraints.SolderMaxCount - (LAYERS_PER_BOX - layerIndex - 1))
+                    {
+                        var nonSolder = candidates.FirstOrDefault(p => !p.IsSolderProduct);
+                        if (nonSolder != null)
+                            selected = nonSolder;
+                    }
+
+                    layer.Products.Add(selected);
+                    usedProducts.Add(selected.Id);
+                    normalAvailable.Remove(selected);
+                    currentLayerSum += selected.TorsChkValue;
+                }
+
+                // 验证层
+                if (!layer.IsValid(_constraints) || !layer.HasValidExtremePair(_dynamicHighThreshold, _dynamicLowThreshold))
+                {
+                    // 尝试微调
+                    if (!OptimizeLayerForTarget(layer, normalAvailable, usedProducts, layerTargetSum)) return null;
+                }
+
+                box.Layers.Add(layer);
+                currentBoxSum += layer.TotalTorsion;
+            }
+
+            return box;
+        }
+
+        /// <summary>
+        /// 优化层使其更接近目标值
+        /// </summary>
+        /// <param name="layer">需优化的层</param>
+        /// <param name="availableProducts">可用的产品</param>
+        /// <param name="usedProducts">已使用的产品</param>
+        /// <param name="targetSum">层目标值</param>
+        /// <returns></returns>
+        private bool OptimizeLayerForTarget(LayerPackingLayerInfo layer, List<LayerPackingProduct> availableProducts, HashSet<long> usedProducts, decimal targetSum)
+        {
+            var currentSum = layer.TotalTorsion;
+            var deviation = Math.Abs(currentSum - targetSum);
+
+            if (deviation <= _constraints.C1) return true;
+
+            // 获取层中的非极值产品
+            var nonExtremeInLayer = layer.Products
+                .Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold)
+                .ToList();
+
+            // 尝试替换产品以接近目标
+            int maxAttempts = Math.Min(5, nonExtremeInLayer.Count);
+            for (int attempt = 0; attempt < maxAttempts; attempt++)
+            {
+                LayerPackingProduct worstProduct = null;
+
+                if (currentSum > targetSum)
+                {
+                    // 找最大的非极值产品
+                    worstProduct = nonExtremeInLayer.OrderByDescending(p => p.TorsChkValue).FirstOrDefault();
+                }
+                else
+                {
+                    // 找最小的非极值产品
+                    worstProduct = nonExtremeInLayer.OrderBy(p => p.TorsChkValue).FirstOrDefault();
+                }
+
+                if (worstProduct == null) break;
+
+                decimal idealReplacement = targetSum - (currentSum - worstProduct.TorsChkValue);
+
+                var replacement = availableProducts
+                    .Where(p => !usedProducts.Contains(p.Id))
+                    .Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold)
+                    .OrderBy(p => Math.Abs(currentSum - worstProduct.TorsChkValue + p.TorsChkValue - targetSum))
+                    .FirstOrDefault();
+
+                if (replacement != null)
+                {
+                    decimal newSum = currentSum - worstProduct.TorsChkValue + replacement.TorsChkValue;
+                    decimal newDeviation = Math.Abs(newSum - targetSum);
+
+                    if (newDeviation < deviation)
+                    {
+                        int index = layer.Products.IndexOf(worstProduct);
+                        layer.Products[index] = replacement;
+                        usedProducts.Remove(worstProduct.Id);
+                        usedProducts.Add(replacement.Id);
+                        availableProducts.Add(worstProduct);
+                        availableProducts.Remove(replacement);
+                        nonExtremeInLayer.Remove(worstProduct);
+                        currentSum = newSum;
+                        deviation = newDeviation;
+
+                        if (deviation <= _constraints.C1) return true;
+                    }
+                }
+            }
+
+            return layer.IsValid(_constraints);
+        }
+
+        /// <summary>
+        /// 极值优先装箱(保证成箱)- 严格的极值成对策略
+        /// </summary>
+        private LayerPackingBoxInfo ExtremeValuePackingWithGuarantee()
+        {
+            var availableProducts = _productPool.Where(p => !p.IsUsed).ToList();
+            if (availableProducts.Count < PRODUCTS_PER_BOX) return null;
+
+            // 分类产品
+            var extremeHigh = availableProducts.Where(p => p.TorsChkValue >= _dynamicHighThreshold).ToList();
+            var extremeLow = availableProducts.Where(p => p.TorsChkValue <= _dynamicLowThreshold).ToList();
+            var normal = availableProducts.Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold).ToList();
+
+            var box = new LayerPackingBoxInfo();
+            var usedProducts = new HashSet<long>();
+
+            // 计算可以使用的极值对数量
+            int maxExtremePairs = Math.Min(extremeHigh.Count, extremeLow.Count);
+            int extremePairsToUse = Math.Min(maxExtremePairs, LAYERS_PER_BOX); // 最多每层一对
+
+            // 构建每一层
+            for (int layerIndex = 0; layerIndex < LAYERS_PER_BOX; layerIndex++)
+            {
+                // 决定这一层是否使用极值对
+                int remainingPairs = Math.Min(
+                    extremeHigh.Where(p => !usedProducts.Contains(p.Id)).Count(),
+                    extremeLow.Where(p => !usedProducts.Contains(p.Id)).Count()
+                );
+
+                bool useExtremePairInThisLayer = remainingPairs > 0 &&
+                    (layerIndex < extremePairsToUse || _random.NextDouble() < 0.5);
+
+                var layer = BuildLayerWithStrictExtremePair(
+                    extremeHigh.Where(p => !usedProducts.Contains(p.Id)).ToList(),
+                    extremeLow.Where(p => !usedProducts.Contains(p.Id)).ToList(),
+                    normal.Where(p => !usedProducts.Contains(p.Id)).ToList(),
+                    useExtremePairInThisLayer,
+                    usedProducts
+                );
+
+                if (layer == null || layer.Products.Count != PRODUCTS_PER_LAYER) return null;
+                box.Layers.Add(layer);
+            }
+
+            // 检查并修复焊点约束
+            if (!CheckAndFixSolderConstraints(box, availableProducts)) return null;
+
+            return box;
+        }
+
+        /// <summary>
+        /// 构建严格遵循极值成对约束的层
+        /// </summary>
+        /// <param name="extremeHigh">极高值产品列表</param>
+        /// <param name="extremeLow">极低值产品列表</param>
+        /// <param name="normal">普通产品列表</param>
+        /// <param name="useExtremePair">是否本层必须用极值对</param>
+        /// <param name="usedProducts">已用过的产品ID</param>
+        /// <returns></returns>
+        private LayerPackingLayerInfo BuildLayerWithStrictExtremePair(List<LayerPackingProduct> extremeHigh, List<LayerPackingProduct> extremeLow, List<LayerPackingProduct> normal, bool useExtremePair, HashSet<long> usedProducts)
+        {
+            var layer = new LayerPackingLayerInfo();
+            decimal targetSum = _constraints.C;
+            decimal currentSum = 0;
+
+            // 如果使用极值对,必须添加一对高低极值
+            if (useExtremePair && extremeHigh.Any() && extremeLow.Any())
+            {
+                // 选择最合适的极值对
+                LayerPackingProduct selectedHigh = null;
+                LayerPackingProduct selectedLow = null;
+                decimal bestPairScore = decimal.MaxValue;
+
+                // 尝试找到最佳的高低极值对
+                foreach (var high in extremeHigh.Take(Math.Min(3, extremeHigh.Count)))
+                {
+                    foreach (var low in extremeLow.Take(Math.Min(3, extremeLow.Count)))
+                    {
+                        decimal pairSum = high.TorsChkValue + low.TorsChkValue;
+                        decimal pairScore = Math.Abs(pairSum - targetSum / 6); // 期望一对占层的1/6
+
+                        if (pairScore < bestPairScore)
+                        {
+                            bestPairScore = pairScore;
+                            selectedHigh = high;
+                            selectedLow = low;
+                        }
+                    }
+                }
+
+                if (selectedHigh != null && selectedLow != null)
+                {
+                    layer.Products.Add(selectedHigh);
+                    layer.Products.Add(selectedLow);
+                    usedProducts.Add(selectedHigh.Id);
+                    usedProducts.Add(selectedLow.Id);
+                    currentSum += selectedHigh.TorsChkValue + selectedLow.TorsChkValue;
+                }
+            }
+
+            // 填充剩余位置,只使用普通产品
+            int remainingSlots = PRODUCTS_PER_LAYER - layer.Products.Count;
+            var allAvailable = normal.Where(p => !usedProducts.Contains(p.Id)).ToList();
+
+            // 如果普通产品不够,则从所有非极值产品中选择
+            if (allAvailable.Count < remainingSlots)
+            {
+                // 添加其他可用产品,但要确保不会单独添加极值
+                var otherProducts = extremeHigh.Concat(extremeLow)
+                    .Where(p => !usedProducts.Contains(p.Id))
+                    .Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold)
+                    .ToList();
+                allAvailable.AddRange(otherProducts);
+            }
+
+            while (remainingSlots > 0 && allAvailable.Any())
+            {
+                decimal idealValue = (targetSum - currentSum) / remainingSlots;
+
+                // 选择最接近理想值的产品
+                var candidate = allAvailable
+                    .OrderBy(p => Math.Abs(p.TorsChkValue - idealValue))
+                    .FirstOrDefault();
+
+                if (candidate != null)
+                {
+                    layer.Products.Add(candidate);
+                    usedProducts.Add(candidate.Id);
+                    currentSum += candidate.TorsChkValue;
+                    allAvailable.Remove(candidate);
+                    remainingSlots--;
+                }
+                else
+                {
+                    break;
+                }
+            }
+
+            // 最终验证:确保层满足极值成对约束
+            if (!layer.HasValidExtremePair(_dynamicHighThreshold, _dynamicLowThreshold)) return null;
+
+            return layer;
+        }
+
+        /// <summary>
+        /// 智能极值装箱策略(严格的极值成对)
+        /// </summary>
+        private LayerPackingBoxInfo SmartExtremeValuePacking()
+        {
+            var availableProducts = _productPool.Where(p => !p.IsUsed).ToList();
+            if (availableProducts.Count < PRODUCTS_PER_BOX) return null;
+
+            // 智能分组:考虑极值产品的互补性
+            var extremeHigh = availableProducts.Where(p => p.TorsChkValue >= _dynamicHighThreshold).OrderByDescending(p => p.TorsChkValue).ToList();
+            var extremeLow = availableProducts.Where(p => p.TorsChkValue <= _dynamicLowThreshold).OrderBy(p => p.TorsChkValue).ToList();
+            var normalHigh = availableProducts.Where(p => p.TorsChkValue > 0 && p.TorsChkValue < _dynamicHighThreshold).ToList();
+            var normalLow = availableProducts.Where(p => p.TorsChkValue <= 0 && p.TorsChkValue > _dynamicLowThreshold).ToList();
+
+            var box = new LayerPackingBoxInfo();
+            var usedProducts = new HashSet<long>();
+
+            // 构建每一层,使用不同的极值配比策略
+            for (int layerIndex = 0; layerIndex < LAYERS_PER_BOX; layerIndex++)
+            {
+                LayerPackingLayerInfo layer = null;
+
+                // 根据层索引使用不同策略
+                if (layerIndex % 3 == 0)
+                {
+                    // 策略1:高低极值平衡
+                    layer = BuildBalancedExtremeLayer(extremeHigh, extremeLow, normalHigh, normalLow, usedProducts);
+                }
+                else if (layerIndex % 3 == 1)
+                {
+                    // 策略2:极值集中
+                    layer = BuildConcentratedExtremeLayer(extremeHigh, extremeLow, normalHigh, normalLow, usedProducts);
+                }
+                else
+                {
+                    // 策略3:混合策略
+                    layer = BuildMixedExtremeLayer(extremeHigh, extremeLow, normalHigh, normalLow, usedProducts);
+                }
+
+                if (layer == null || layer.Products.Count != PRODUCTS_PER_LAYER) return null;
+
+                // 验证层的极值成对约束
+                if (!layer.HasValidExtremePair(_dynamicHighThreshold, _dynamicLowThreshold)) return null;
+
+                box.Layers.Add(layer);
+            }
+
+            return box;
+        }
+
+        /// <summary>
+        /// 构建平衡的极值层(严格的极值成对)
+        /// </summary>
+        /// <param name="extremeHigh">极高值产品列表</param>
+        /// <param name="extremeLow">极低值产品列表</param>
+        /// <param name="normalHigh">普通高值产品列表</param>
+        /// <param name="normalLow">普通低值产品列表</param>
+        /// <param name="usedProducts">已用过的产品ID</param>
+        /// <returns></returns>
+        private LayerPackingLayerInfo BuildBalancedExtremeLayer(List<LayerPackingProduct> extremeHigh, List<LayerPackingProduct> extremeLow, List<LayerPackingProduct> normalHigh, List<LayerPackingProduct> normalLow, HashSet<long> usedProducts)
+        {
+            var layer = new LayerPackingLayerInfo();
+            decimal targetSum = _constraints.C;
+
+            // 平衡策略:只添加一对高低极值
+            var availableHigh = extremeHigh.Where(p => !usedProducts.Contains(p.Id)).ToList();
+            var availableLow = extremeLow.Where(p => !usedProducts.Contains(p.Id)).ToList();
+
+            if (availableHigh.Any() && availableLow.Any())
+            {
+                // 选择最平衡的一对
+                var high = availableHigh.OrderBy(p => Math.Abs(p.TorsChkValue)).First();
+                var low = availableLow.OrderByDescending(p => Math.Abs(p.TorsChkValue)).First();
+
+                layer.Products.Add(high);
+                layer.Products.Add(low);
+                usedProducts.Add(high.Id);
+                usedProducts.Add(low.Id);
+            }
+
+            // 填充剩余位置,只使用普通产品
+            FillRemainingSlots(layer, normalHigh.Concat(normalLow).Where(p => !usedProducts.Contains(p.Id)).ToList(), targetSum, usedProducts);
+
+            return layer;
+        }
+
+        /// <summary>
+        /// 构建集中的极值层(严格的极值成对)
+        /// </summary>
+        /// <param name="extremeHigh">极高值产品列表</param>
+        /// <param name="extremeLow">极低值产品列表</param>
+        /// <param name="normalHigh">普通高值产品列表</param>
+        /// <param name="normalLow">普通低值产品列表</param>
+        /// <param name="usedProducts">已用过的产品ID</param>
+        /// <returns></returns>
+        private LayerPackingLayerInfo BuildConcentratedExtremeLayer(List<LayerPackingProduct> extremeHigh, List<LayerPackingProduct> extremeLow, List<LayerPackingProduct> normalHigh, List<LayerPackingProduct> normalLow, HashSet<long> usedProducts)
+
+        {
+            var layer = new LayerPackingLayerInfo();
+            decimal targetSum = _constraints.C;
+            decimal currentSum = 0;
+
+            // 集中策略:添加一对影响力最大的极值
+            var availableHigh = extremeHigh.Where(p => !usedProducts.Contains(p.Id)).OrderByDescending(p => p.TorsChkValue).ToList();
+
+            var availableLow = extremeLow.Where(p => !usedProducts.Contains(p.Id)).OrderBy(p => p.TorsChkValue).ToList();
+
+            if (availableHigh.Any() && availableLow.Any())
+            {
+                // 选择绝对值最大的一对
+                var high = availableHigh.First();
+                var low = availableLow.First();
+
+                layer.Products.Add(high);
+                layer.Products.Add(low);
+                usedProducts.Add(high.Id);
+                usedProducts.Add(low.Id);
+                currentSum += high.TorsChkValue + low.TorsChkValue;
+            }
+
+            // 用普通产品平衡
+            FillRemainingSlots(layer, normalHigh.Concat(normalLow).Where(p => !usedProducts.Contains(p.Id)).ToList(), targetSum, usedProducts);
+
+            return layer;
+        }
+
+        /// <summary>
+        /// 构建混合的极值层(严格的极值成对)
+        /// </summary>
+        /// <param name="extremeHigh">极高值产品列表</param>
+        /// <param name="extremeLow">极低值产品列表</param>
+        /// <param name="normalHigh">普通高值产品列表</param>
+        /// <param name="normalLow">普通低值产品列表</param>
+        /// <param name="usedProducts">已用过的产品ID</param>x
+        /// <returns></returns>
+        private LayerPackingLayerInfo BuildMixedExtremeLayer(List<LayerPackingProduct> extremeHigh, List<LayerPackingProduct> extremeLow, List<LayerPackingProduct> normalHigh, List<LayerPackingProduct> normalLow, HashSet<long> usedProducts)
+        {
+            var layer = new LayerPackingLayerInfo();
+            decimal targetSum = _constraints.C;
+
+            // 混合策略:50%概率使用一对极值
+            var availableHigh = extremeHigh.Where(p => !usedProducts.Contains(p.Id)).ToList();
+            var availableLow = extremeLow.Where(p => !usedProducts.Contains(p.Id)).ToList();
+
+            if (availableHigh.Any() && availableLow.Any() && _random.NextDouble() < 0.5)
+            {
+                // 随机选择一对
+                var high = availableHigh[_random.Next(availableHigh.Count)];
+                var low = availableLow[_random.Next(availableLow.Count)];
+
+                layer.Products.Add(high);
+                layer.Products.Add(low);
+                usedProducts.Add(high.Id);
+                usedProducts.Add(low.Id);
+            }
+
+            // 添加普通产品
+            var availableNormal = normalHigh.Concat(normalLow).Where(p => !usedProducts.Contains(p.Id)).ToList();
+            FillRemainingSlots(layer, availableNormal, targetSum, usedProducts);
+
+            return layer;
+        }
+
+        /// <summary>
+        /// 填充剩余位置(确保不会单独添加极值)
+        /// </summary>
+        /// <param name="layer">当前正在填充的分层对象</param>
+        /// <param name="candidates">可选产品列表</param>
+        /// <param name="targetSum">本层目标的总目标值</param>
+        /// <param name="usedProducts">已使用过的产品</param>
+        private void FillRemainingSlots(LayerPackingLayerInfo layer, List<LayerPackingProduct> candidates, decimal targetSum, HashSet<long> usedProducts)
+        {
+            decimal currentSum = layer.TotalTorsion;
+            int remainingSlots = PRODUCTS_PER_LAYER - layer.Products.Count;
+
+            // 只选择非极值产品
+            var normalCandidates = candidates.Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold).ToList();
+
+            while (remainingSlots > 0 && normalCandidates.Any())
+            {
+                decimal idealValue = (targetSum - currentSum) / remainingSlots;
+                var best = normalCandidates.OrderBy(p => Math.Abs(p.TorsChkValue - idealValue)).First();
+
+                layer.Products.Add(best);
+                usedProducts.Add(best.Id);
+                currentSum += best.TorsChkValue;
+                normalCandidates.Remove(best);
+                remainingSlots--;
+            }
+
+            // 如果普通产品不够,尝试从其他产品中选择(但仍要避免单独的极值)
+            if (remainingSlots > 0)
+            {
+                var otherCandidates = candidates
+                    .Where(p => !usedProducts.Contains(p.Id))
+                    .Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold)
+                    .ToList();
+
+                while (remainingSlots > 0 && otherCandidates.Any())
+                {
+                    decimal idealValue = (targetSum - currentSum) / remainingSlots;
+                    var best = otherCandidates.OrderBy(p => Math.Abs(p.TorsChkValue - idealValue)).First();
+
+                    layer.Products.Add(best);
+                    usedProducts.Add(best.Id);
+                    currentSum += best.TorsChkValue;
+                    otherCandidates.Remove(best);
+                    remainingSlots--;
+                }
+            }
+        }
+
+        /// <summary>
+        /// 随机目标导向装箱
+        /// </summary>
+        private LayerPackingBoxInfo RandomTargetOrientedPacking()
+        {
+            var availableProducts = _productPool.Where(p => !p.IsUsed).ToList();
+            if (availableProducts.Count < PRODUCTS_PER_BOX) return null;
+
+            // 分类并随机打乱
+            var extremeHigh = availableProducts
+                .Where(p => p.TorsChkValue >= _dynamicHighThreshold)
+                .OrderBy(x => _random.Next())
+                .ToList();
+
+            var extremeLow = availableProducts
+                .Where(p => p.TorsChkValue <= _dynamicLowThreshold)
+                .OrderBy(x => _random.Next())
+                .ToList();
+
+            var normal = availableProducts
+                .Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold)
+                .ToList();
+
+            var box = new LayerPackingBoxInfo();
+            var usedProducts = new HashSet<long>();
+            decimal boxTargetSum = _constraints.X;
+            decimal currentBoxSum = 0;
+
+            for (int layerIndex = 0; layerIndex < LAYERS_PER_BOX; layerIndex++)
+            {
+                var layer = new LayerPackingLayerInfo();
+
+                // 计算当前层的目标
+                decimal remainingTarget = boxTargetSum - currentBoxSum;
+                int remainingLayers = LAYERS_PER_BOX - layerIndex;
+                decimal layerTarget = remainingTarget / remainingLayers;
+
+                // 确保层目标在允许范围内
+                layerTarget = Math.Max(_constraints.MinLayerTotal, Math.Min(_constraints.MaxLayerTotal, layerTarget));
+
+                decimal currentLayerSum = 0;
+
+                // 随机决定是否使用极值对
+                if (extremeHigh.Any(p => !usedProducts.Contains(p.Id)) &&
+                    extremeLow.Any(p => !usedProducts.Contains(p.Id)) &&
+                    _random.NextDouble() < 0.4)
+                {
+                    var high = extremeHigh.FirstOrDefault(p => !usedProducts.Contains(p.Id));
+                    var low = extremeLow.FirstOrDefault(p => !usedProducts.Contains(p.Id));
+
+                    if (high != null && low != null)
+                    {
+                        layer.Products.Add(high);
+                        layer.Products.Add(low);
+                        usedProducts.Add(high.Id);
+                        usedProducts.Add(low.Id);
+                        currentLayerSum += high.TorsChkValue + low.TorsChkValue;
+                    }
+                }
+
+                // 随机但目标导向地填充剩余位置
+                var normalShuffled = normal
+                    .Where(p => !usedProducts.Contains(p.Id))
+                    .OrderBy(x => _random.Next())
+                    .ToList();
+
+                while (layer.Products.Count < PRODUCTS_PER_LAYER && normalShuffled.Any())
+                {
+                    decimal remaining = layerTarget - currentLayerSum;
+                    decimal slotsLeft = PRODUCTS_PER_LAYER - layer.Products.Count;
+                    decimal idealValue = remaining / slotsLeft;
+
+                    // 从打乱的列表中选择相对接近理想值的产品
+                    var candidates = normalShuffled
+                        .Take(Math.Min(30, normalShuffled.Count))
+                        .OrderBy(p => Math.Abs(p.TorsChkValue - idealValue))
+                        .Take(5)
+                        .ToList();
+
+                    if (candidates.Any())
+                    {
+                        var selected = candidates[_random.Next(candidates.Count)];
+                        layer.Products.Add(selected);
+                        usedProducts.Add(selected.Id);
+                        currentLayerSum += selected.TorsChkValue;
+                        normalShuffled.Remove(selected);
+                    }
+                    else
+                    {
+                        break;
+                    }
+                }
+
+                if (layer.Products.Count != PRODUCTS_PER_LAYER) return null;
+
+                if (!layer.HasValidExtremePair(_dynamicHighThreshold, _dynamicLowThreshold)) return null;
+
+                box.Layers.Add(layer);
+                currentBoxSum += layer.TotalTorsion;
+            }
+
+            return box;
+        }
+
+        /// <summary>
+        /// 随机极值装箱(严格的极值成对)
+        /// </summary>
+        private LayerPackingBoxInfo RandomExtremeValuePacking()
+        {
+            var availableProducts = _productPool.Where(p => !p.IsUsed).ToList();
+            if (availableProducts.Count < PRODUCTS_PER_BOX) return null;
+
+            // 分类并打乱
+            var extremeHigh = availableProducts
+                .Where(p => p.TorsChkValue >= _dynamicHighThreshold)
+                .OrderBy(x => _random.Next())
+                .ToList();
+
+            var extremeLow = availableProducts
+                .Where(p => p.TorsChkValue <= _dynamicLowThreshold)
+                .OrderBy(x => _random.Next())
+                .ToList();
+
+            var normalProducts = availableProducts
+                .Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold)
+                .OrderBy(x => _random.Next())
+                .ToList();
+
+            var box = new LayerPackingBoxInfo();
+            var usedProducts = new HashSet<long>();
+
+            // 计算极值对数量
+            int extremePairs = Math.Min(Math.Min(extremeHigh.Count, extremeLow.Count), _random.Next(0, LAYERS_PER_BOX + 1));
+
+            for (int layerIndex = 0; layerIndex < LAYERS_PER_BOX; layerIndex++)
+            {
+                var layer = new LayerPackingLayerInfo();
+
+                // 如果还有极值对要分配,且这一层被选中
+                if (extremePairs > 0 && _random.NextDouble() < (double)extremePairs / (LAYERS_PER_BOX - layerIndex))
+                {
+                    // 添加一对极值
+                    var high = extremeHigh.FirstOrDefault(p => !usedProducts.Contains(p.Id));
+                    var low = extremeLow.FirstOrDefault(p => !usedProducts.Contains(p.Id));
+
+                    if (high != null && low != null)
+                    {
+                        layer.Products.Add(high);
+                        layer.Products.Add(low);
+                        usedProducts.Add(high.Id);
+                        usedProducts.Add(low.Id);
+                        extremePairs--;
+                    }
+                }
+
+                // 填充剩余位置,只使用普通产品
+                var allAvailable = normalProducts
+                    .Where(p => !usedProducts.Contains(p.Id))
+                    .OrderBy(x => _random.Next())
+                    .ToList();
+
+                while (layer.Products.Count < PRODUCTS_PER_LAYER && allAvailable.Any())
+                {
+                    var product = allAvailable.First();
+                    layer.Products.Add(product);
+                    usedProducts.Add(product.Id);
+                    allAvailable.RemoveAt(0);
+                }
+
+                if (layer.Products.Count != PRODUCTS_PER_LAYER) return null;
+
+                // 验证层的极值成对约束
+                if (!layer.HasValidExtremePair(_dynamicHighThreshold, _dynamicLowThreshold)) return null;
+
+                box.Layers.Add(layer);
+            }
+
+            return box;
+        }
+
+        /// <summary>
+        /// 随机快速装箱
+        /// </summary>
+        private LayerPackingBoxInfo RandomQuickPacking()
+        {
+            var availableProducts = _productPool.Where(p => !p.IsUsed).ToList();
+            if (availableProducts.Count < PRODUCTS_PER_BOX) return null;
+
+            // 分类产品
+            var extremeHigh = availableProducts.Where(p => p.TorsChkValue >= _dynamicHighThreshold).ToList();
+            var extremeLow = availableProducts.Where(p => p.TorsChkValue <= _dynamicLowThreshold).ToList();
+            var normal = availableProducts.Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold).ToList();
+
+            var shuffled = normal.OrderBy(x => _random.Next()).ToList();
+
+            var box = new LayerPackingBoxInfo();
+            var usedIndices = new HashSet<int>();
+
+            for (int layerIndex = 0; layerIndex < LAYERS_PER_BOX; layerIndex++)
+            {
+                var layer = new LayerPackingLayerInfo();
+                decimal targetSum = _constraints.C;
+                decimal currentSum = 0;
+
+                // 随机决定是否使用极值对
+                if (extremeHigh.Any(p => !usedIndices.Contains(availableProducts.IndexOf(p))) &&
+                    extremeLow.Any(p => !usedIndices.Contains(availableProducts.IndexOf(p))) &&
+                    _random.NextDouble() < 0.3)
+                {
+                    // 添加一对极值
+                    var high = extremeHigh.FirstOrDefault(p => !usedIndices.Contains(availableProducts.IndexOf(p)));
+                    var low = extremeLow.FirstOrDefault(p => !usedIndices.Contains(availableProducts.IndexOf(p)));
+
+                    if (high != null && low != null)
+                    {
+                        layer.Products.Add(high);
+                        layer.Products.Add(low);
+                        usedIndices.Add(availableProducts.IndexOf(high));
+                        usedIndices.Add(availableProducts.IndexOf(low));
+                        currentSum += high.TorsChkValue + low.TorsChkValue;
+                    }
+                }
+
+                // 填充剩余位置
+                for (int i = layer.Products.Count; i < PRODUCTS_PER_LAYER; i++)
+                {
+                    decimal remaining = targetSum - currentSum;
+                    decimal slotsLeft = PRODUCTS_PER_LAYER - i;
+                    decimal idealValue = remaining / slotsLeft;
+
+                    int bestIndex = -1;
+                    decimal bestDiff = decimal.MaxValue;
+
+                    for (int j = 0; j < shuffled.Count; j++)
+                    {
+                        int actualIndex = availableProducts.IndexOf(shuffled[j]);
+                        if (usedIndices.Contains(actualIndex)) continue;
+
+                        decimal diff = Math.Abs(shuffled[j].TorsChkValue - idealValue);
+                        if (diff < bestDiff)
+                        {
+                            bestDiff = diff;
+                            bestIndex = j;
+                        }
+                    }
+
+                    if (bestIndex == -1) return null;
+
+                    layer.Products.Add(shuffled[bestIndex]);
+                    usedIndices.Add(availableProducts.IndexOf(shuffled[bestIndex]));
+                    currentSum += shuffled[bestIndex].TorsChkValue;
+                }
+
+                if (!layer.IsValid(_constraints))
+                {
+                    if (!QuickAdjustLayer(layer, shuffled, usedIndices)) return null;
+                }
+
+                // 验证层的极值成对约束
+                if (!layer.HasValidExtremePair(_dynamicHighThreshold, _dynamicLowThreshold)) return null;
+
+                box.Layers.Add(layer);
+            }
+
+            if (!CheckSolderConstraints(box)) return null;
+
+            return box;
+        }
+
+        /// <summary>
+        /// 快速调整层
+        /// </summary>
+        private bool QuickAdjustLayer(LayerPackingLayerInfo layer, List<LayerPackingProduct> allProducts, HashSet<int> usedIndices)
+        {
+            decimal targetSum = _constraints.C;
+            decimal currentSum = layer.TotalTorsion;
+            decimal diff = currentSum - targetSum;
+
+            if (Math.Abs(diff) <= _constraints.C1)
+                return true;
+
+            // 只调整非极值产品
+            var nonExtremeInLayer = layer.Products
+                .Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold)
+                .ToList();
+
+            if (!nonExtremeInLayer.Any())
+                return false;
+
+            var worstProduct = diff > 0
+                ? nonExtremeInLayer.OrderByDescending(p => p.TorsChkValue).First()
+                : nonExtremeInLayer.OrderBy(p => p.TorsChkValue).First();
+
+            decimal idealReplacement = targetSum - (currentSum - worstProduct.TorsChkValue);
+
+            for (int i = 0; i < allProducts.Count; i++)
+            {
+                if (usedIndices.Contains(i)) continue;
+
+                var candidate = allProducts[i];
+
+                // 确保替换品也是非极值产品
+                if (candidate.TorsChkValue <= _dynamicLowThreshold || candidate.TorsChkValue >= _dynamicHighThreshold)
+                    continue;
+
+                decimal newSum = currentSum - worstProduct.TorsChkValue + candidate.TorsChkValue;
+
+                if (Math.Abs(newSum - targetSum) < Math.Abs(diff))
+                {
+                    int layerIndex = layer.Products.IndexOf(worstProduct);
+                    int oldIndex = allProducts.IndexOf(worstProduct);
+
+                    layer.Products[layerIndex] = candidate;
+                    usedIndices.Remove(oldIndex);
+                    usedIndices.Add(i);
+
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        /// <summary>
+        /// 检查焊点约束
+        /// </summary>
+        private bool CheckSolderConstraints(LayerPackingBoxInfo box)
+        {
+            var constraintType = _constraints.GetSolderConstraintType();
+
+            switch (constraintType)
+            {
+                case SolderConstraintType.NoConstraint:
+                    return true;
+
+                case SolderConstraintType.OnlyProductCount:
+                    return box.SolderProductCount <= _constraints.SolderMaxCount;
+
+                case SolderConstraintType.BothConstraints:
+                    return box.SolderProductCount <= _constraints.SolderMaxCount &&
+                           box.TotalSolderJoints <= _constraints.PerSolderMaxCount;
+
+                default:
+                    return true;
+            }
+        }
+
+        /// <summary>
+        /// 检查并修复焊点约束
+        /// </summary>
+        /// <param name="box">箱信息</param>
+        /// <param name="allProducts">所有可用产品</param>
+        /// <returns></returns>
+        private bool CheckAndFixSolderConstraints(LayerPackingBoxInfo box, List<LayerPackingProduct> allProducts)
+        {
+            var constraintType = _constraints.GetSolderConstraintType();
+
+            if (constraintType == SolderConstraintType.NoConstraint) return true;
+
+            if (box.SolderProductCount > _constraints.SolderMaxCount)
+            {
+                return FixExcessSolderProducts(box, allProducts);
+            }
+
+            if (constraintType == SolderConstraintType.BothConstraints && box.TotalSolderJoints > _constraints.PerSolderMaxCount)
+            {
+                return FixExcessSolderJoints(box, allProducts);
+            }
+
+            return true;
+        }
+
+        /// <summary>
+        /// 修复过多的焊点盘
+        /// </summary>
+        /// <param name="box">箱信息</param>
+        /// <param name="allProducts">所有可用产品</param>
+        /// <returns></returns>
+        private bool FixExcessSolderProducts(LayerPackingBoxInfo box, List<LayerPackingProduct> allProducts)
+        {
+            var usedIds = box.Layers.SelectMany(l => l.Products.Select(p => p.Id)).ToHashSet();
+            int excess = box.SolderProductCount - _constraints.SolderMaxCount;
+
+            // 获取所有焊点盘,但排除极值产品
+            var solderProducts = box.Layers
+                .SelectMany((layer, layerIndex) => layer.Products
+                    .Select((product, productIndex) => new { Layer = layer, Product = product, LayerIndex = layerIndex, ProductIndex = productIndex }))
+                .Where(x => x.Product.IsSolderProduct)
+                .Where(x => x.Product.TorsChkValue > _dynamicLowThreshold && x.Product.TorsChkValue < _dynamicHighThreshold)
+                .ToList();
+
+            foreach (var item in solderProducts)
+            {
+                if (excess <= 0) break;
+
+                var replacement = allProducts
+                    .Where(p => !p.IsSolderProduct && !usedIds.Contains(p.Id))
+                    .Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold)
+                    .Where(p =>
+                    {
+                        var newSum = item.Layer.TotalTorsion - item.Product.TorsChkValue + p.TorsChkValue;
+                        return newSum >= _constraints.MinLayerTotal && newSum <= _constraints.MaxLayerTotal;
+                    })
+                    .OrderBy(p => Math.Abs(p.TorsChkValue - item.Product.TorsChkValue))
+                    .FirstOrDefault();
+
+                if (replacement != null)
+                {
+                    item.Layer.Products[item.ProductIndex] = replacement;
+                    usedIds.Remove(item.Product.Id);
+                    usedIds.Add(replacement.Id);
+                    excess--;
+                }
+            }
+
+            return excess <= 0;
+        }
+
+        /// <summary>
+        /// 修复过多的焊点总数
+        /// </summary>
+        /// <param name="box">箱信息</param>
+        /// <param name="allProducts">所有可用产品</param>
+        /// <returns></returns>
+        private bool FixExcessSolderJoints(LayerPackingBoxInfo box, List<LayerPackingProduct> allProducts)
+        {
+            var usedIds = box.Layers.SelectMany(l => l.Products.Select(p => p.Id)).ToHashSet();
+            decimal excessJoints = box.TotalSolderJoints - _constraints.PerSolderMaxCount;
+
+            // 获取所有2焊点产品,但排除极值产品
+            var twoJointProducts = box.Layers
+                .SelectMany((layer, layerIndex) => layer.Products
+                    .Select((product, productIndex) => new { Layer = layer, Product = product, LayerIndex = layerIndex, ProductIndex = productIndex }))
+                .Where(x => x.Product.SolderCount == 2)
+                .Where(x => x.Product.TorsChkValue > _dynamicLowThreshold && x.Product.TorsChkValue < _dynamicHighThreshold)
+                .ToList();
+
+            foreach (var item in twoJointProducts)
+            {
+                if (excessJoints <= 0) break;
+
+                var replacement = allProducts
+                    .Where(p => p.SolderCount < 2 && !usedIds.Contains(p.Id))
+                    .Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold)
+                    .Where(p =>
+                    {
+                        var newSum = item.Layer.TotalTorsion - item.Product.TorsChkValue + p.TorsChkValue;
+                        return newSum >= _constraints.MinLayerTotal && newSum <= _constraints.MaxLayerTotal;
+                    })
+                    .OrderBy(p => Math.Abs(p.TorsChkValue - item.Product.TorsChkValue))
+                    .FirstOrDefault();
+
+                if (replacement != null)
+                {
+                    item.Layer.Products[item.ProductIndex] = replacement;
+                    usedIds.Remove(item.Product.Id);
+                    usedIds.Add(replacement.Id);
+                    excessJoints -= (item.Product.SolderCount - replacement.SolderCount);
+                }
+            }
+
+            return excessJoints <= 0;
+        }
+
+        /// <summary>
+        /// 激进搜索策略
+        /// </summary>
+        private LayerPackingBoxInfo AggressiveSearch()
+        {
+            var availableProducts = _productPool.Where(p => !p.IsUsed).ToList();
+            if (availableProducts.Count < PRODUCTS_PER_BOX) return null;
+
+            // 放宽约束进行搜索
+            var relaxedConstraints = new LayerPackingConstraints
+            {
+                P = _constraints.P,
+                P1 = _constraints.P1,
+                X = _constraints.X,
+                X1 = _constraints.X1 * 1.2m, // 放宽20%
+                C = _constraints.C,
+                C1 = _constraints.C1 * 1.2m, // 放宽20%
+                SolderMaxCount = _constraints.SolderMaxCount,
+                PerSolderMaxCount = _constraints.PerSolderMaxCount,
+                ProductsPerBox = _constraints.ProductsPerBox
+            };
+
+            // 尝试多种组合
+            for (int attempt = 0; attempt < 10; attempt++)
+            {
+                var candidates = availableProducts.OrderBy(x => _random.Next()).Take(Math.Min(150, availableProducts.Count)).ToList();
+
+                var box = TryBuildBox(candidates, relaxedConstraints);
+
+                if (box != null && box.IsValid(_constraints) && box.HasValidExtremePairs(_dynamicHighThreshold, _dynamicLowThreshold)) return box;
+            }
+
+            // 最后尝试:使用模拟退火
+            return LastResortSimulatedAnnealing(availableProducts);
+        }
+
+        /// <summary>
+        /// 尝试构建箱子
+        /// </summary>
+        /// <param name="candidates">当前可用产品</param>
+        /// <param name="constraints">约束规则</param>
+        /// <returns></returns>
+        private LayerPackingBoxInfo TryBuildBox(List<LayerPackingProduct> candidates, LayerPackingConstraints constraints)
+        {
+            if (candidates.Count < PRODUCTS_PER_BOX) return null;
+
+            var box = new LayerPackingBoxInfo();
+            var used = new HashSet<long>();
+
+            // 分类产品
+            var extremeHigh = candidates.Where(p => p.TorsChkValue >= _dynamicHighThreshold).ToList();
+            var extremeLow = candidates.Where(p => p.TorsChkValue <= _dynamicLowThreshold).ToList();
+            var normal = candidates.Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold).ToList();
+
+            for (int i = 0; i < LAYERS_PER_BOX; i++)
+            {
+                var layer = new LayerPackingLayerInfo();
+
+                // 随机决定是否使用极值对
+                if (extremeHigh.Any(p => !used.Contains(p.Id)) && extremeLow.Any(p => !used.Contains(p.Id)) && _random.NextDouble() < 0.5)
+                {
+                    var high = extremeHigh.FirstOrDefault(p => !used.Contains(p.Id));
+                    var low = extremeLow.FirstOrDefault(p => !used.Contains(p.Id));
+
+                    if (high != null && low != null)
+                    {
+                        layer.Products.Add(high);
+                        layer.Products.Add(low);
+                        used.Add(high.Id);
+                        used.Add(low.Id);
+                    }
+                }
+
+                // 填充剩余位置
+                for (int j = layer.Products.Count; j < PRODUCTS_PER_LAYER; j++)
+                {
+                    var available = normal.Where(c => !used.Contains(c.Id)).ToList();
+                    if (!available.Any()) return null;
+
+                    decimal currentSum = layer.TotalTorsion;
+                    decimal targetValue = (constraints.C - currentSum) / (PRODUCTS_PER_LAYER - j);
+
+                    var selected = available.OrderBy(p => Math.Abs(p.TorsChkValue - targetValue)).First();
+
+                    layer.Products.Add(selected);
+                    used.Add(selected.Id);
+                }
+
+                if (!layer.IsValid(constraints)) return null;
+
+                box.Layers.Add(layer);
+            }
+
+            return box.IsValid(constraints) ? box : null;
+        }
+
+        /// <summary>
+        /// 最后手段:模拟退火
+        /// </summary>
+        /// <param name="candidates">有效产品</param>
+        /// <returns></returns>
+        private LayerPackingBoxInfo LastResortSimulatedAnnealing(List<LayerPackingProduct> candidates)
+        {
+            if (candidates.Count < PRODUCTS_PER_BOX) return null;
+
+            // 生成初始解
+            var currentBox = GenerateInitialSolution(candidates);
+            if (currentBox == null) return null;
+
+            var bestBox = currentBox;
+            var bestScore = CalculateBoxScore(currentBox);
+
+            double temperature = 200.0; // 提高初始温度
+            double coolingRate = 0.98; // 减缓冷却速度
+            int iterations = 2000; // 增加迭代次数
+
+            for (int i = 0; i < iterations; i++)
+            {
+                var neighborBox = GenerateNeighbor(currentBox, candidates);
+                if (neighborBox == null) continue;
+
+                // 验证极值成对约束
+                if (!neighborBox.HasValidExtremePairs(_dynamicHighThreshold, _dynamicLowThreshold)) continue;
+
+                var neighborScore = CalculateBoxScore(neighborBox);
+
+                if (neighborScore < bestScore || AcceptanceProbability(bestScore, neighborScore, temperature) > _random.NextDouble())
+                {
+                    currentBox = neighborBox;
+
+                    if (neighborScore < bestScore && neighborBox.IsValid(_constraints))
+                    {
+                        bestScore = neighborScore;
+                        bestBox = neighborBox;
+                    }
+                }
+
+                temperature *= coolingRate;
+            }
+
+            return bestBox.IsValid(_constraints) && bestBox.HasValidExtremePairs(_dynamicHighThreshold, _dynamicLowThreshold) ? bestBox : null;
+        }
+
+        /// <summary>
+        /// 生成初始解
+        /// </summary>
+        /// <param name="candidates">有效产品</param>
+        /// <returns></returns>
+        private LayerPackingBoxInfo GenerateInitialSolution(List<LayerPackingProduct> candidates)
+        {
+            var box = new LayerPackingBoxInfo();
+            var used = new HashSet<long>();
+
+            // 分类产品
+            var extremeHigh = candidates.Where(p => p.TorsChkValue >= _dynamicHighThreshold).ToList();
+            var extremeLow = candidates.Where(p => p.TorsChkValue <= _dynamicLowThreshold).ToList();
+            var normal = candidates.Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold)
+                .OrderBy(p => Math.Abs(p.TorsChkValue - _constraints.C / PRODUCTS_PER_LAYER))
+                .ToList();
+
+            for (int i = 0; i < LAYERS_PER_BOX; i++)
+            {
+                var layer = new LayerPackingLayerInfo();
+
+                // 每隔一层使用极值对
+                if (i % 2 == 0 && extremeHigh.Any(p => !used.Contains(p.Id)) && extremeLow.Any(p => !used.Contains(p.Id)))
+                {
+                    var high = extremeHigh.FirstOrDefault(p => !used.Contains(p.Id));
+                    var low = extremeLow.FirstOrDefault(p => !used.Contains(p.Id));
+
+                    if (high != null && low != null)
+                    {
+                        layer.Products.Add(high);
+                        layer.Products.Add(low);
+                        used.Add(high.Id);
+                        used.Add(low.Id);
+                    }
+                }
+
+                // 选择普通产品
+                var selected = normal
+                    .Where(p => !used.Contains(p.Id))
+                    .Take(PRODUCTS_PER_LAYER - layer.Products.Count)
+                    .ToList();
+
+                if (selected.Count < PRODUCTS_PER_LAYER - layer.Products.Count) return null;
+
+                foreach (var product in selected)
+                {
+                    layer.Products.Add(product);
+                    used.Add(product.Id);
+                }
+
+                box.Layers.Add(layer);
+            }
+
+            return box;
+        }
+
+        /// <summary>
+        /// 生成邻域解
+        /// </summary>
+        /// <param name="currentBox">当前箱信息</param>
+        /// <param name="allCandidates">所有产品</param>
+        /// <returns></returns>
+        private LayerPackingBoxInfo GenerateNeighbor(LayerPackingBoxInfo currentBox, List<LayerPackingProduct> allCandidates)
+        {
+            var newBox = new LayerPackingBoxInfo();
+
+            // 深拷贝
+            foreach (var layer in currentBox.Layers)
+            {
+                var newLayer = new LayerPackingLayerInfo();
+                newLayer.Products.AddRange(layer.Products);
+                newBox.Layers.Add(newLayer);
+            }
+
+            // 执行随机操作
+            int operation = _random.Next(4);
+
+            switch (operation)
+            {
+                case 0: // 层内交换(保持极值成对)
+                    SwapWithinLayerSafe(newBox);
+                    break;
+
+                case 1: // 层间交换(保持极值成对)
+                    SwapBetweenLayersSafe(newBox);
+                    break;
+
+                case 2: // 替换产品(保持极值成对)
+                    ReplaceProductSafe(newBox, allCandidates);
+                    break;
+
+                case 3: // 批量调整
+                    BatchAdjustSafe(newBox, allCandidates);
+                    break;
+            }
+
+            return newBox;
+        }
+
+        /// <summary>
+        /// 安全的层内交换(保持极值成对) --已无实际意义
+        /// </summary>
+        /// <param name="box">箱信息</param>
+        [Obsolete]
+        private void SwapWithinLayerSafe(LayerPackingBoxInfo box)
+        {
+            int layerIndex = _random.Next(LAYERS_PER_BOX);
+            var layer = box.Layers[layerIndex];
+
+            if (layer.Products.Count >= 2)
+            {
+                // 只交换非极值产品
+                var nonExtremeProducts = layer.Products
+                    .Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold)
+                    .ToList();
+
+                if (nonExtremeProducts.Count >= 2)
+                {
+                    var indices = nonExtremeProducts.Select(p => layer.Products.IndexOf(p)).ToList();
+                    int i = indices[_random.Next(indices.Count)];
+                    int j = indices[_random.Next(indices.Count)];
+
+                    if (i != j)
+                    {
+                        var temp = layer.Products[i];
+                        layer.Products[i] = layer.Products[j];
+                        layer.Products[j] = temp;
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// 安全的层间交换(保持极值成对)
+        /// </summary>
+        /// <param name="box">箱信息</param>
+        private void SwapBetweenLayersSafe(LayerPackingBoxInfo box)
+        {
+            int layer1 = _random.Next(LAYERS_PER_BOX);
+            int layer2 = _random.Next(LAYERS_PER_BOX);
+
+            if (layer1 != layer2)
+            {
+                // 获取两层的非极值产品
+                var nonExtreme1 = box.Layers[layer1].Products
+                    .Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold)
+                    .ToList();
+                var nonExtreme2 = box.Layers[layer2].Products
+                    .Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold)
+                    .ToList();
+
+                if (nonExtreme1.Any() && nonExtreme2.Any())
+                {
+                    var product1 = nonExtreme1[_random.Next(nonExtreme1.Count)];
+                    var product2 = nonExtreme2[_random.Next(nonExtreme2.Count)];
+
+                    int pos1 = box.Layers[layer1].Products.IndexOf(product1);
+                    int pos2 = box.Layers[layer2].Products.IndexOf(product2);
+
+                    box.Layers[layer1].Products[pos1] = product2;
+                    box.Layers[layer2].Products[pos2] = product1;
+                }
+            }
+        }
+
+        /// <summary>
+        /// 安全的产品替换(保持极值成对)
+        /// </summary>
+        /// <param name="box">箱信息 </param>
+        /// <param name="allCandidates">所有产品</param>
+        private void ReplaceProductSafe(LayerPackingBoxInfo box, List<LayerPackingProduct> allCandidates)
+        {
+            var usedIds = box.Layers.SelectMany(l => l.Products.Select(p => p.Id)).ToHashSet();
+            var unused = allCandidates
+                .Where(c => !usedIds.Contains(c.Id))
+                .Where(c => c.TorsChkValue > _dynamicLowThreshold && c.TorsChkValue < _dynamicHighThreshold)
+                .ToList();
+
+            if (unused.Any())
+            {
+                int layerIndex = _random.Next(LAYERS_PER_BOX);
+                var nonExtremeInLayer = box.Layers[layerIndex].Products
+                    .Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold)
+                    .ToList();
+
+                if (nonExtremeInLayer.Any())
+                {
+                    var oldProduct = nonExtremeInLayer[_random.Next(nonExtremeInLayer.Count)];
+                    var newProduct = unused[_random.Next(unused.Count)];
+                    int productIndex = box.Layers[layerIndex].Products.IndexOf(oldProduct);
+
+                    box.Layers[layerIndex].Products[productIndex] = newProduct;
+                }
+            }
+        }
+
+        /// <summary>
+        /// 安全的批量调整(保持极值成对)
+        /// </summary>
+        /// <param name="box">箱</param>
+        /// <param name="allCandidates"></param>
+        private void BatchAdjustSafe(LayerPackingBoxInfo box, List<LayerPackingProduct> allCandidates)
+        {
+            // 找出偏差最大的层
+            LayerPackingLayerInfo worstLayer = null;
+            decimal maxDeviation = 0;
+
+            foreach (var layer in box.Layers)
+            {
+                var deviation = layer.GetDeviationFromTarget(_constraints.C);
+                if (deviation > maxDeviation)
+                {
+                    maxDeviation = deviation;
+                    worstLayer = layer;
+                }
+            }
+
+            if (worstLayer != null && maxDeviation > _constraints.C1 * 0.5m)
+            {
+                // 尝试改善这一层,只替换非极值产品
+                var usedIds = box.Layers.SelectMany(l => l.Products.Select(p => p.Id)).ToHashSet();
+                var unused = allCandidates
+                    .Where(c => !usedIds.Contains(c.Id))
+                    .Where(c => c.TorsChkValue > _dynamicLowThreshold && c.TorsChkValue < _dynamicHighThreshold)
+                    .ToList();
+
+                var nonExtremeInLayer = worstLayer.Products
+                    .Where(p => p.TorsChkValue > _dynamicLowThreshold && p.TorsChkValue < _dynamicHighThreshold)
+                    .ToList();
+
+                // 替换2-3个非极值产品
+                int replacements = Math.Min(3, Math.Min(unused.Count, nonExtremeInLayer.Count));
+                for (int i = 0; i < replacements; i++)
+                {
+                    if (unused.Any() && nonExtremeInLayer.Any())
+                    {
+                        var oldProduct = nonExtremeInLayer[_random.Next(nonExtremeInLayer.Count)];
+                        int index = worstLayer.Products.IndexOf(oldProduct);
+
+                        // 选择能改善偏差的产品
+                        var currentSum = worstLayer.TotalTorsion;
+                        var targetSum = _constraints.C;
+
+                        var replacement = unused
+                            .Where(p => Math.Abs(currentSum - oldProduct.TorsChkValue + p.TorsChkValue - targetSum) < maxDeviation)
+                            .OrderBy(x => _random.Next())
+                            .FirstOrDefault();
+
+                        if (replacement != null)
+                        {
+                            worstLayer.Products[index] = replacement;
+                            unused.Remove(replacement);
+                            unused.Add(oldProduct);
+                            nonExtremeInLayer.Remove(oldProduct);
+                        }
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// 接受概率计算
+        /// </summary>
+        private double AcceptanceProbability(decimal currentScore, decimal newScore, double temperature)
+        {
+            if (newScore < currentScore) return 1.0;
+
+            return Math.Exp(-((double)(newScore - currentScore) / temperature));
+        }
+
+        /// <summary>
+        /// 计算箱子的评分(强调接近目标值)
+        /// </summary>
+        /// <param name="box">需计算的箱信息</param>
+        /// <returns></returns>
+        private decimal CalculateBoxScore(LayerPackingBoxInfo box)
+        {
+            decimal score = 0;
+
+            // 基础分:箱子是否满足约束
+            if (!box.IsValid(_constraints))
+            {
+                score += 10000; // 不满足约束的严重惩罚
+            }
+
+            // 极值成对约束检查
+            if (!box.HasValidExtremePairs(_dynamicHighThreshold, _dynamicLowThreshold))
+            {
+                score += 5000; // 不满足极值成对约束的惩罚
+            }
+
+            // 整箱偏差(提高权重,强调接近目标值)
+            var boxDiff = box.GetDeviationFromTarget(_constraints.X);
+            score += boxDiff * 5; // 提高权重
+
+            // 整箱偏差百分比惩罚
+            decimal boxDeviationPercent = boxDiff / _constraints.X * 100;
+            if (boxDeviationPercent > 5) // 偏差超过5%
+            {
+                score += boxDeviationPercent * 10;
+            }
+
+            // 每层偏差(提高权重)
+            foreach (var layer in box.Layers)
+            {
+                var layerDiff = layer.GetDeviationFromTarget(_constraints.C);
+                score += layerDiff * 2; // 提高权重
+
+                // 层偏差百分比惩罚
+                decimal layerDeviationPercent = layerDiff / _constraints.C * 100;
+                if (layerDeviationPercent > 5) // 偏差超过5%
+                {
+                    score += layerDeviationPercent * 5;
+                }
+
+                // 层约束违反惩罚
+                if (!layer.IsValid(_constraints))
+                {
+                    score += 50;
+                }
+
+                // 层的极值成对约束违反惩罚
+                if (!layer.HasValidExtremePair(_dynamicHighThreshold, _dynamicLowThreshold))
+                {
+                    score += 100;
+                }
+            }
+
+            // 焊点约束检查
+            var constraintType = _constraints.GetSolderConstraintType();
+            switch (constraintType)
+            {
+                case SolderConstraintType.OnlyProductCount:
+                    if (box.SolderProductCount > _constraints.SolderMaxCount)
+                    {
+                        score += 100 * (box.SolderProductCount - _constraints.SolderMaxCount);
+                    }
+                    break;
+
+                case SolderConstraintType.BothConstraints:
+                    if (box.SolderProductCount > _constraints.SolderMaxCount)
+                    {
+                        score += 100 * (box.SolderProductCount - _constraints.SolderMaxCount);
+                    }
+                    if (box.TotalSolderJoints > _constraints.PerSolderMaxCount)
+                    {
+                        score += 50 * (box.TotalSolderJoints - _constraints.PerSolderMaxCount);
+                    }
+                    break;
+            }
+
+            // 层间均衡性(降低权重,因为现在更关注接近目标值)
+            var layerTotals = box.Layers.Select(l => l.TotalTorsion).ToList();
+            var avgLayer = layerTotals.Average();
+            var variance = layerTotals.Sum(t => Math.Pow((double)(t - avgLayer), 2)) / LAYERS_PER_BOX;
+            score += (decimal)variance * 0.05m; // 降低权重
+
+            // 极值产品使用奖励(成对使用)
+            var extremePairCount = box.Layers.Count(l =>
+            {
+                var highCount = l.Products.Count(p => p.TorsChkValue >= _dynamicHighThreshold);
+                var lowCount = l.Products.Count(p => p.TorsChkValue <= _dynamicLowThreshold);
+                return highCount == 1 && lowCount == 1;
+            });
+
+            // 根据极值对数量给予奖励
+            if (extremePairCount > 0)
+            {
+                score -= extremePairCount * 20; // 每对极值给予奖励
+            }
+
+            // 成箱奖励:完全满足约束且接近目标值给予巨大奖励
+            if (box.IsValid(_constraints) && box.HasValidExtremePairs(_dynamicHighThreshold, _dynamicLowThreshold))
+            {
+                score *= 0.1m; // 满足所有约束的箱子评分大幅降低
+
+                // 额外奖励:非常接近目标值
+                if (boxDiff < _constraints.X1 * 0.1m) // 在10%偏差范围内
+                {
+                    score *= 0.5m; // 大幅奖励
+                }
+                else if (boxDiff < _constraints.X1 * 0.3m) // 在30%偏差范围内
+                {
+                    score *= 0.7m;
+                }
+
+                // 所有层都接近目标值的额外奖励
+                bool allLayersClose = box.Layers.All(l => l.GetDeviationFromTarget(_constraints.C) < _constraints.C1 * 0.3m);
+                if (allLayersClose)
+                {
+                    score *= 0.8m;
+                }
+
+                // 极值对使用率额外奖励
+                if (extremePairCount >= 3) // 至少3层使用了极值对
+                {
+                    score *= 0.95m;
+                }
+            }
+
+            return score;
+        }
+    }
+}

+ 123 - 0
wms.service/Extensions/LayerPacking/model/LayerPackingBoxInfo.cs

@@ -0,0 +1,123 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace wms.service.Extensions.LayerPacking.model
+{
+    /// <summary>
+    /// 箱信息
+    /// </summary>
+    public class LayerPackingBoxInfo
+    {
+        /// <summary>
+        /// 箱ID
+        /// </summary>
+        [Obsolete]
+        public string BoxId { get; set; }
+
+        /// <summary>
+        /// 箱产品
+        /// </summary>
+        public List<LayerPackingLayerInfo> Layers { get; set; }
+
+        /// <summary>
+        /// 箱总扭转值
+        /// </summary>
+        public decimal TotalTorsion => Layers.Sum(l => l.TotalTorsion);
+
+        /// <summary>
+        /// 箱总焊点盘数
+        /// </summary>
+        public int SolderProductCount => Layers.Sum(l => l.SolderProductCount);
+
+        /// <summary>
+        /// 箱总焊点数
+        /// </summary>
+        public decimal TotalSolderJoints => Layers.Sum(l => l.TotalSolderJoints);
+
+        /// <summary>
+        /// 成箱时间
+        /// </summary>
+        public DateTime PackingTime { get; set; }
+
+        /// <summary>
+        /// 最小箱目标
+        /// </summary>
+        public decimal MinBoxTarget { get; set; }
+
+        /// <summary>
+        /// 最大箱目标
+        /// </summary>
+        public decimal MaxBoxTarget { get; set; }
+
+        /// <summary>
+        /// 箱目标
+        /// </summary>
+        public decimal BoxTarget { get; set; }
+
+        /// <summary>
+        /// 装箱时的产品池平均值
+        /// </summary>
+        public decimal ProductPoolAverage { get; set; }
+
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        public LayerPackingBoxInfo()
+        {
+            Layers = new List<LayerPackingLayerInfo>();
+            PackingTime = DateTime.Now;
+        }
+
+        /// <summary>
+        /// 判断当前箱是否有效
+        /// </summary>
+        /// <param name="constraints"></param>
+        /// <returns></returns>
+        public bool IsValid(LayerPackingConstraints constraints)
+        {
+            var total = TotalTorsion;
+            bool torsionValid = total >= constraints.MinBoxTotal && total <= constraints.MaxBoxTotal && Layers.All(l => l.IsValid(constraints));
+
+            if (!torsionValid) return false;
+            var constraintType = constraints.GetSolderConstraintType();
+            return constraintType switch
+            {
+                SolderConstraintType.NoConstraint => true,
+                SolderConstraintType.OnlyProductCount => SolderProductCount <= constraints.SolderMaxCount,
+                SolderConstraintType.BothConstraints => SolderProductCount <= constraints.SolderMaxCount && TotalSolderJoints <= constraints.PerSolderMaxCount,
+                _ => true,
+            };
+        }
+
+        /// <summary>
+        /// 箱极值对是否有效
+        /// </summary>
+        /// <param name="highThreshold">大极值阈值</param>
+        /// <param name="lowThreshold">小极值阈值</param>
+        /// <returns></returns>
+        public bool HasValidExtremePairs(decimal highThreshold, decimal lowThreshold)
+        {
+            return Layers.All(l => l.HasValidExtremePair(highThreshold, lowThreshold));
+        }
+
+        /// <summary>
+        /// 获取偏差
+        /// </summary>
+        /// <param name="target"></param>
+        /// <returns></returns>
+        public decimal GetDeviationFromTarget(decimal target)
+        {
+            return Math.Abs(TotalTorsion - target);
+        }
+
+        /// <summary>
+        /// 获取产品总数
+        /// </summary>
+        /// <returns></returns>
+        public int GetTotalProductCount()
+        {
+            return Layers.Sum(l => l.Products.Count);
+        }
+    }
+}

+ 252 - 0
wms.service/Extensions/LayerPacking/model/LayerPackingConstraints.cs

@@ -0,0 +1,252 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace wms.service.Extensions.LayerPacking.model
+{
+    /// <summary>
+    /// 层配装箱约束条件
+    /// </summary>
+    public class LayerPackingConstraints
+    {
+        /// <summary>
+        /// 产品目标值
+        /// </summary>
+        public decimal P { get; set; }
+
+        /// <summary>
+        /// 产品偏差范围
+        /// </summary>
+        public decimal P1 { get; set; }
+
+        /// <summary>
+        /// 箱总扭转目标值
+        /// </summary>
+        private decimal? _x;
+
+        /// <summary>
+        /// 层总扭转目标值
+        /// </summary>
+        private decimal? _c;
+
+        /// <summary>
+        /// 箱总产品数
+        /// </summary>
+        private int _productsPerBox;
+
+        /// <summary>
+        /// 箱总产品数
+        /// </summary>
+        public int ProductsPerBox
+        {
+            get => _productsPerBox;
+            set
+            {
+                _productsPerBox = value;
+                // 重新计算相关值
+                _x = null;
+                _c = null;
+            }
+        }
+
+        /// <summary>
+        /// 层总产品数
+        /// </summary>
+        private const int PRODUCTS_PER_LAYER = 12;
+
+        /// <summary>
+        /// 层总产品数
+        /// </summary>
+        public int ProductsPerLayer => PRODUCTS_PER_LAYER;
+
+        /// <summary>
+        /// 箱总层数
+        /// </summary>
+        public int LayersPerBox => ProductsPerBox / PRODUCTS_PER_LAYER;
+
+        /// <summary>
+        /// 箱总扭转目标值
+        /// </summary>
+        public decimal X
+        {
+            get => _x ?? P * ProductsPerBox;
+            set => _x = value;
+        }
+
+        /// <summary>
+        /// 箱总扭转目标偏差范围
+        /// </summary>
+        public decimal X1 { get; set; }
+
+        /// <summary>
+        /// 层总扭转目标值
+        /// </summary>
+        public decimal C
+        {
+            get => _c ?? P * PRODUCTS_PER_LAYER;
+            set => _c = value;
+        }
+
+        /// <summary>
+        /// 层总扭转目标偏差范围
+        /// </summary>
+        public decimal C1 { get; set; }
+
+        /// <summary>
+        /// 最大焊点盘数
+        /// </summary>
+        public int SolderMaxCount { get; set; }
+
+        /// <summary>
+        /// 最大焊点数
+        /// </summary>
+        public int PerSolderMaxCount { get; set; }
+
+        /// <summary>
+        /// 最小产品扭转目标值
+        /// </summary>
+        public decimal MinProductValue => P - P1;
+
+        /// <summary>
+        /// 最大产品扭转目标值
+        /// </summary>
+        public decimal MaxProductValue => P + P1;
+
+        /// <summary>
+        /// 是产品最小扭转目标值
+        /// </summary>
+        public bool IsAtMinTarget { get; private set; }
+
+        /// <summary>
+        /// 是产品最大扭转目标值
+        /// </summary>
+        public bool IsAtMaxTarget { get; private set; }
+
+        /// <summary>
+        /// 最小箱总目标值
+        /// </summary>
+        public decimal MinBoxTotal
+        {
+            get
+            {
+                if (X == P - P1 * ProductsPerBox)
+                    return X;
+                if (X == P + P1 * ProductsPerBox)
+                    return X - X1;
+                return X - X1;
+            }
+        }
+
+        /// <summary>
+        /// 最大箱总目标值
+        /// </summary>
+        public decimal MaxBoxTotal
+        {
+            get
+            {
+                if (X == P + P1 * ProductsPerBox)
+                    return X;
+                if (X == P - P1 * ProductsPerBox)
+                    return X + X1;
+                return X + X1;
+            }
+        }
+
+        /// <summary>
+        /// 最小层总目标值
+        /// </summary>
+        public decimal MinLayerTotal
+        {
+            get
+            {
+                if (C == P - P1 * PRODUCTS_PER_LAYER)
+                    return C;
+                if (C == P + P1 * PRODUCTS_PER_LAYER)
+                    return C - C1;
+                return C - C1;
+            }
+        }
+
+        /// <summary>
+        /// 最大层总目标值
+        /// </summary>
+        public decimal MaxLayerTotal
+        {
+            get
+            {
+                if (C == P + P1 * PRODUCTS_PER_LAYER)
+                    return C;
+                if (C == P - P1 * PRODUCTS_PER_LAYER)
+                    return C + C1;
+                return C + C1;
+            }
+        }
+
+        /// <summary>
+        /// 更新箱层目标值
+        /// </summary>
+        /// <param name="products"></param>
+        public void UpdateTargetValues(List<LayerPackingProduct> products)
+        {
+            if (products == null || !products.Any()) return;
+
+            decimal avgValue = products.Average(p => p.TorsChkValue);
+            decimal effectiveProductTarget; // 有效产品目标值  --计算箱层目标值时使用的产品目标值
+
+            if (avgValue <= MinProductValue)
+            {
+                effectiveProductTarget = MinProductValue;
+                IsAtMinTarget = true;
+                IsAtMaxTarget = false;
+            }
+            else if (avgValue >= MaxProductValue)
+            {
+                effectiveProductTarget = MaxProductValue;
+                IsAtMinTarget = false;
+                IsAtMaxTarget = true;
+            }
+            else
+            {
+                effectiveProductTarget = avgValue;
+                IsAtMinTarget = false;
+                IsAtMaxTarget = false;
+            }
+
+            _x = effectiveProductTarget * ProductsPerBox;
+            _c = effectiveProductTarget * PRODUCTS_PER_LAYER;
+            X1 = P1 * ProductsPerBox;
+            C1 = P1 * PRODUCTS_PER_LAYER;
+        }
+
+        /// <summary>
+        /// 获取焊点在装箱中的约束方案
+        /// </summary>
+        /// <returns></returns>
+        public SolderConstraintType GetSolderConstraintType()
+        {
+            if (SolderMaxCount <= 0) return SolderConstraintType.NoConstraint;
+            else if (PerSolderMaxCount <= 0) return SolderConstraintType.OnlyProductCount;
+            else return SolderConstraintType.BothConstraints;
+        }
+    }
+
+    /// <summary>
+    /// 焊点约束条件
+    /// </summary>
+    public enum SolderConstraintType
+    {
+        /// <summary>
+        /// 无约束条件
+        /// </summary>
+        NoConstraint,
+
+        /// <summary>
+        /// 仅产品数
+        /// </summary>
+        OnlyProductCount,
+
+        /// <summary>
+        /// 同时满足产品数和焊点数
+        /// </summary>
+        BothConstraints
+    }
+}

+ 137 - 0
wms.service/Extensions/LayerPacking/model/LayerPackingLayerInfo.cs

@@ -0,0 +1,137 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace wms.service.Extensions.LayerPacking.model
+{
+    /// <summary>
+    /// 层信息
+    /// </summary>
+    public class LayerPackingLayerInfo
+    {
+        /// <summary>
+        /// 层产品
+        /// </summary>
+        public List<LayerPackingProduct> Products { get; set; }
+
+        /// <summary>
+        /// 层总扭转值
+        /// </summary>
+        public decimal TotalTorsion => Products.Sum(p => p.TorsChkValue);
+
+        /// <summary>
+        /// 层总焊点盘数
+        /// </summary>
+        public int SolderProductCount => Products.Count(p => p.IsSolderProduct);
+
+        /// <summary>
+        /// 层总焊点数
+        /// </summary>
+        public decimal TotalSolderJoints => Products.Sum(p => p.SolderCount);
+
+        /// <summary>
+        /// 层最小目标值
+        /// </summary>
+        public decimal MinLayerTarget { get; set; }
+
+        /// <summary>
+        /// 层最大目标值
+        /// </summary>
+        public decimal MaxLayerTarget { get; set; }
+
+        /// <summary>
+        /// 层去掉最大最小值后的最大标准差
+        /// </summary>
+        private double LayerStandardDeviation = 0.5;
+
+        /// <summary>
+        /// 层目标值
+        /// </summary>
+        public decimal LayerTarget { get; set; }
+
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        public LayerPackingLayerInfo()
+        {
+            Products = new List<LayerPackingProduct>();
+        }
+
+        /// <summary>
+        /// 当前层是否有效
+        /// </summary>
+        /// <param name="constraints"></param>
+        /// <returns></returns>
+        public bool IsValid(LayerPackingConstraints constraints)
+        {
+            var total = TotalTorsion;
+            return total >= constraints.MinLayerTotal && total <= constraints.MaxLayerTotal && LayerStdValid();
+        }
+
+        /// <summary>
+        /// 获取当前层偏差
+        /// </summary>
+        /// <param name="target"></param>
+        /// <returns></returns>
+        public decimal GetDeviationFromTarget(decimal target)
+        {
+            return Math.Abs(TotalTorsion - target);
+        }
+
+        /// <summary>
+        /// 当前层中极值对是否有效
+        /// </summary>
+        /// <param name="highThreshold">大极值阈值</param>
+        /// <param name="lowThreshold">小极值阈值</param>
+        /// <returns></returns>
+        public bool HasValidExtremePair(decimal highThreshold, decimal lowThreshold)
+        {
+            var highCount = Products.Count(p => p.TorsChkValue >= highThreshold);
+            var lowCount = Products.Count(p => p.TorsChkValue <= lowThreshold);
+            return (highCount == 0 && lowCount == 0) || (highCount == 1 && lowCount == 1);
+        }
+
+        /// <summary>
+        /// 去除最大/最小值后10个产品的标准差≤0.5
+        /// </summary>
+        /// <returns></returns>
+        public bool LayerStdValid()
+        {
+            if (Products == null || Products.Count < 3) return true;
+
+            var ordered = Products.OrderBy(p => p.TorsChkValue).ToList();
+            var ten = ordered.Skip(1).Take(Products.Count - 2).ToList();
+            if (ten.Count > 10)
+            {
+                int skip = (ten.Count - 10) / 2;
+                ten = ten.Skip(skip).Take(10).ToList();
+            }
+            if (ten.Count < 1) return true;
+
+            double avg = (double)ten.Average(p => p.TorsChkValue);
+            double std = Math.Sqrt(ten.Sum(p => Math.Pow((double)p.TorsChkValue - avg, 2)) / ten.Count);
+            return std <= LayerStandardDeviation;
+        }
+
+        /// <summary>
+        /// 获取层标准差(去除最大最小后10个产品)
+        /// </summary>
+        public double GetStdAfterTrim()
+        {
+            if (Products == null || Products.Count < 3) return 0;
+
+            var ordered = Products.OrderBy(p => p.TorsChkValue).ToList();
+            var ten = ordered.Skip(1).Take(Products.Count - 2).ToList();
+            if (ten.Count > 10)
+            {
+                int skip = (ten.Count - 10) / 2;
+                ten = ten.Skip(skip).Take(10).ToList();
+            }
+            if (ten.Count < 1) return 0;
+
+            double avg = (double)ten.Average(p => p.TorsChkValue);
+            double std = Math.Sqrt(ten.Sum(p => Math.Pow((double)p.TorsChkValue - avg, 2)) / ten.Count);
+            return std;
+        }
+    }
+}

+ 202 - 0
wms.service/Extensions/LayerPacking/model/LayerPackingProduct.cs

@@ -0,0 +1,202 @@
+using System;
+using wms.dto;
+
+namespace wms.service.Extensions.LayerPacking.model
+{
+    /// <summary>
+    /// 层配产品信息
+    /// </summary>
+    public class LayerPackingProduct
+    {
+        /// <summary>
+        /// 物料编码
+        /// </summary>
+        public string MatCode { get; set; }
+
+        /// <summary>
+        /// 条码号
+        /// </summary>
+        public string InvBarCode { get; set; }
+
+        /// <summary>
+        /// 等级
+        /// </summary>
+        public string Grade { get; set; }
+
+        /// <summary>
+        /// 库存状态
+        /// </summary>
+        public string InvStateCode { get; set; }
+
+        /// <summary>
+        /// 生产时间
+        /// </summary>
+        public DateTime ProductTime { get; set; }
+
+        /// <summary>
+        /// 第一次入库时间
+        /// </summary>
+        public DateTime OneInTime { get; set; }
+
+        /// <summary>
+        /// 机台组
+        /// </summary>
+        public string WbGroupCode { get; set; }
+
+        /// <summary>
+        /// 是否扭转检测
+        /// </summary>
+        public bool IsTorsChk { get; set; }
+
+        /// <summary>
+        /// 扭转次数
+        /// </summary>
+        public int TorsChkQty { get; set; }
+
+        /// <summary>
+        /// 扭转值 
+        /// </summary>
+        private decimal? _torsChkValue;
+
+        /// <summary>
+        /// 扭转值
+        /// </summary>
+        public decimal TorsChkValue
+        {
+            get => Math.Round(_torsChkValue ?? 0, 3);
+            set => _torsChkValue = Math.Round(value, 3);
+        }
+
+        /// <summary>
+        /// 扭转时长
+        /// </summary>
+        public decimal HoldTime { get; set; }
+
+        /// <summary>
+        /// 生产机台号
+        /// </summary>
+        public string ProductMachCode { get; set; }
+
+        /// <summary>
+        /// 是否控制盘
+        /// </summary>
+        public bool IsControlpanel { get; set; }
+
+        /// <summary>
+        /// 工字轮类型
+        /// </summary>
+        public string HWTypeCode { get; set; }
+
+        /// <summary>
+        /// 焊点数量
+        /// </summary>
+        public decimal SolderCount { get; set; }
+
+        /// <summary>
+        /// 是否焊点盘
+        /// </summary>
+        public bool IsSolderProduct => SolderCount > 0;
+
+        /// <summary>
+        /// 返工标记
+        /// </summary>
+        public bool IsRework { get; set; }
+
+        /// <summary>
+        /// 是否黑盘
+        /// </summary>
+        public bool IsBlack { get; set; }
+
+        /// <summary>
+        /// 列
+        /// </summary>
+        public int Col { get; set; }
+
+        /// <summary>
+        /// 层
+        /// </summary>
+        public int Layer { get; set; }
+
+        /// <summary>
+        /// 货架
+        /// </summary>
+        public string Shelf { get; set; }
+
+        /// <summary>
+        /// 深度
+        /// </summary>
+        public int Depth { get; set; }
+
+        /// <summary>
+        /// 货位号
+        /// </summary>
+        public string Code { get; set; }
+
+        /// <summary>
+        /// 巷道
+        /// </summary>
+        public int Tunnel { get; set; }
+
+        /// <summary>
+        /// 堆垛机
+        /// </summary>
+        public string SCRel { get; set; }
+
+        /// <summary>
+        /// 楼层
+        /// </summary>
+        public int Floor { get; set; }
+
+        /// <summary>
+        /// 仓库编码
+        /// </summary>
+        public string WarehouseCode { get; set; }
+
+        /// <summary>
+        /// 容器编码
+        /// </summary>
+        public string ContGrpBarCode { get; set; }
+
+        /// <summary>
+        /// 容器ID
+        /// </summary>
+        public long? ContGrpId { get; set; }
+
+        /// <summary>
+        /// 货位ID
+        /// </summary>
+        public long Id { get; set; }
+
+        /// <summary>
+        /// 货位状态
+        /// </summary>
+        public LocationState StateNum { get; set; }
+
+        /// <summary>
+        /// SKU编码
+        /// </summary>
+        public string SkuCode { get; set; }
+
+        /// <summary>
+        /// 绕向
+        /// </summary>
+        public string Wind { get; set; }
+
+        /// <summary>
+        /// 单号
+        /// </summary>
+        public string InDocsNo { get; set; }
+
+        /// <summary>
+        /// 批次号
+        /// </summary>
+        public string BatchNo { get; set; }
+
+        /// <summary>
+        /// 是否使用,此值为前期测试时使用,用于模拟产品已被消耗
+        /// 目前线上不需使用,但是在整个装箱逻辑中去相关内容过于麻烦,所以先保留
+        /// </summary>
+        [Obsolete]
+        public bool IsUsed { get; set; }
+    }
+}

+ 49 - 0
wms.service/Extensions/LayerPacking/model/LayerPackingResult.cs

@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+
+namespace wms.service.Extensions.LayerPacking.model
+{
+    /// <summary>
+    /// 装箱结果
+    /// </summary>
+    public class LayerPackingResult
+    {
+        /// <summary>
+        /// 所有箱
+        /// </summary>
+        public List<LayerPackingBoxInfo> Boxes { get; set; }
+
+        /// <summary>
+        /// 使用的总产品数
+        /// </summary>
+        public int TotalProductsUsed { get; set; }
+
+        /// <summary>
+        /// 所有焊点盘总数
+        /// </summary>
+        public int TotalSolderProductsUsed { get; set; }
+
+        /// <summary>
+        /// 剩余产品数
+        /// </summary>
+        public int RemainingProducts { get; set; }
+
+        /// <summary>
+        /// 剩余焊点盘数
+        /// </summary>
+        public int RemainingSolderProducts { get; set; }
+
+        /// <summary>
+        /// 总处理时间
+        /// </summary>
+        public TimeSpan TotalProcessingTime { get; set; }
+
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        public LayerPackingResult()
+        {
+            Boxes = new List<LayerPackingBoxInfo>();
+        }
+    }
+}

+ 44 - 6
wms.service/Extensions/SxServiceExtension.cs

@@ -2,6 +2,7 @@
 using System;
 using System.Collections.Generic;
 using System.Data;
+using System.Globalization;
 using System.Linq;
 using wms.dto.response.sx;
 using wms.sqlsugar;
@@ -115,23 +116,23 @@ namespace wms.service.Extensions
         public static List<sxSysConfig> GetAvailablePalletizingStationsByConfiguration(this List<sxSysConfig> palletizingList, Repository<sxSysConfig> config, string code)
         {
             // 获取配置中的机器人列表
-            var configuredRobots = config.GetFirst(x => x.Code == code).SContent.Split("|");
+            var configuredRobots = config.GetFirst(x => x.Code == code).SContent.Split("|").Select(x => ToTitleCase(x));
 
             // 定义机器人及其对应的码垛位映射关系
             var robotToPalletizingMap = new Dictionary<string, List<string>>
             {
-               { "robot1", new List<string> { "8090", "8092" } },
-               { "robot2", new List<string> { "8096", "8098" } },
-               { "robot3", new List<string> { "8307" } }
+               { "Robot1", new List<string> { "8090", "8092" } },
+               { "Robot2", new List<string> { "8096", "8098" } },
+               { "Robot3", new List<string> { "8307" } }
             };
 
             // 获取所有符合条件的码垛位
             var availablePalletizingCodes = configuredRobots
                 .SelectMany(robot =>
                 {
-                    if (robotToPalletizingMap.TryGetValue(robot, out var palletizingList))
+                    if (robotToPalletizingMap.TryGetValue(robot, out var palletizing))
                     {
-                        return palletizingList;
+                        return palletizing;
                     }
                     return Enumerable.Empty<string>();
                 })
@@ -141,6 +142,43 @@ namespace wms.service.Extensions
             return palletizingList.Where(palletizing => availablePalletizingCodes.Contains(palletizing.Code)).ToList();
         }
 
+        /// <summary>
+        /// 根据配置信息获取可用机械臂
+        /// </summary>
+        /// <param name="palletizingList">现有码垛位</param>
+        /// <param name="config">配置表</param>
+        /// <param name="code">配置信息code</param>
+        /// <returns></returns>
+        public static List<string> GetAvailablePalletizingRobotsByConfiguration(this List<string> palletizingList, Repository<sxSysConfig> config, string code)
+        {
+            // 获取配置中的机器人列表
+            var configuredRobots = config.GetFirst(x => x.Code == code).SContent.Split("|").Select(x => ToTitleCase(x));
+            // 返回匹配的码垛位列表
+            return palletizingList.Select(x => ToTitleCase(x)).Where(x => configuredRobots.Contains(x)).ToList();
+        }
+
+        /// <summary>
+        /// 将字符串转换为首字母大写,其他全小写
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static string ToTitleCase(string str)
+        {
+            if (string.IsNullOrEmpty(str))
+            {
+                return str;
+            }
+
+            // 将字符串转换为全小写
+            string lowerStr = str.ToLower();
+
+            // 使用 TextInfo.ToTitleCase 转换为标题大小写
+            TextInfo textInfo = CultureInfo.CurrentCulture.TextInfo;
+            string titleCasedStr = textInfo.ToTitleCase(lowerStr);
+
+            return titleCasedStr;
+        }
+
         #endregion 获取码垛位
     }
 }

+ 91 - 1
wms.service/IService/ISXService.cs

@@ -21,11 +21,15 @@ namespace wms.service.IService
     public interface ISXService
     {
         public string GetSysConfigContentByCode(string code);
+
         public bool SyncMaterInfo(List<SxSyncMaterInfoRequestItem> ResData);
+
         public sxSysConfig GetSysConfigByCode(string code);
+
         public sxSysConfig GetSysConfigByExpression(Expression<Func<sxSysConfig, bool>> WhereExpression);
 
         public bool UpdateSysConfigModelColumns(UpdateModelColumns<sxSysConfig> updateModel);
+
         public SRes SpoolMaterialInfoTrans(SxSpoolMaterialInfoTransRequest reqDto);
 
         /// <summary>
@@ -41,81 +45,167 @@ namespace wms.service.IService
         /// <param name="reqDto"></param>
         /// <returns></returns>
         public SRes SynchronizeBoxStatus(SynchronizeBoxStatusRequest reqDto);
+
         public SRes<ApplyFloorResponse> ApplyFloor(ApplyFloorRequest reqDto);
+
         public SRes SKUInfoTrans(SKUInfoTransRequest reqDto);
+
         public SRes PackInfoTrans(PackInfoTrans reqDto);
+
         public SRes<string> GetTunnelPriorityList(GetSxTunnelPriorityListRequest reqEntity);
+
+        /// <summary>
+        /// 库存改判
+        /// </summary>
+        /// <param name="reqDto"></param>
+        /// <returns></returns>
         public SRes StockChange(SxStockChangeRequest reqDto);
+
+        /// <summary>
+        /// 预赋扭转值
+        /// </summary>
+        /// <param name="reqDto"></param>
+        /// <returns></returns>
+        public SRes PreSpecifiedTorsionValues(SxStockChangeRequest reqDto);
+
         SRes<List<ApplyStockInLocResponse>> ApplyStockInLoc(SxApplyStockInLocRequest reqEntity);
+
         SRes<int> CompleteTask(SxCompleteTaskRequest reqDto);
+
         public List<BillPushinfo> GetPushDatelistAuto(PushDate request);
+
         public bool UpdatePushModelColumns(UpdateModelColumns<BillPushinfo> updateModel);
+
         SRes InitWareCell(int row, int col, int deot, int rowcount, int tun);
+
         SRes<int> TorsChkComplete(TorsChkCompleteRequest request);
+
         sxSysJob GetJobEntityByid(string id);
+
         sxSysJob GetJobEntityByCode(string Code);
+
         List<sxSysJobApiRelation> GetJobapilist(long id);
+
         List<sxSysJob> GetJobList();
+
         SRes TorsChkStockOut(TorsChkStockOutRequest request);
+
         SRes CancelTaskVerify(CancelTaskRequest request);
+
         SRes CarryTaskInfo(CancelTaskRequest request);
+
         SRes CancelTask(CompleteTaskRequest request);
+
         SRes PalletizingLayerPackStockOut(PalletizingPackStockOutRequest request);
+
         SRes TorsChkValue(TorsChkValueRequest request);
+
         SRes Manaler(DetailCodeStrdtoRequest request);
+
         SRes<int> SrmCompleted(SxCompleteTaskRequest reqDto);
+
         public SRes CurtainProductionOrder(CurtainProductionOrderRequest reqDto);
+
         SRes<int> TorsChkFloor(TorsChkFloorRequest reqDto);
+
         SRes PalletizingPatchWheel(PalletizingPatchWheelRequest request);
+
         SRes PalletizingForceknot(PalletizingForceknotRequest request);
+
         SRes Palletizinginfobinde(PalletizinginfobindeRequest request);
+
         SRes<int> CompletePalletizingTask(SxPalletizingCompleteRequest reqDto);
+
         SRes<WcsMoveTaskResponse> WcsMoveTask(WcsMoveTaskRequest request);
+
         public SRes PalletizingSpcNotPackStockOut2(PalletizingPackStockOutRequest request);
+
         public SRes PalletizingSpcNotPackStockOut3(PalletizingPackStockOutRequest request);
+
         public SRes PalletizingSpcNotPackRulePre(PalletizingPackStockOutRequest request);
+
         SRes PalletizingSpcNotPackRulePre2(PalletizingPackStockOutRequest request);
 
         public SRes PalletizingSpcPackStockOut2(PalletizingPackStockOutRequest request);
+
         public SRes PalletizingSpcPackStockOut3(PalletizingPackStockOutRequest request);
+
         public SRes PalletizingSpcPackRulePre(PalletizingPackStockOutRequest request);
+
         SRes TorsChkStationbinde(TorsChkStationbindeRequest request);
+
         SRes UpdateBlackCount(UpdateBlackCountReq request);
+
         SRes TorsChkErrorinfo(TorsChkErrorinfoRequest request);
+
         TorsChkResponse GetTorsChkinfo(GetTorsChkinfoRequest request);
+
         SRes CellInfo(string code);
+
         SRes GetMesErrorInfo(SxErrorinfoRequest request);
+
         SRes PalletizingPackBStockOut(PalletizingPackStockOutRequest request);
+
         CurStockListResponse GetCurStockList(CurStockListRequest request);
+
         public SRes UpdateBoxNo(UpdateBoxNoRequest request);
+
         SRes SolderErrorStockOut(PalletizingPackStockOutRequest request);
+
         SRes ChongraoPackStockOut(PalletizingPackStockOutRequest request);
+
         SRes ForceCancelTask(ErrorTaskRequest request);
+
         SRes DeleteStockInfo(DetailCodeStrRequest request);
+
         SRes DeleteTorsBind(DetailCodeStrRequest request);
+
         SRes DeleteHwPre(DetailCodeStrRequest request);
+
         SRes UpdateStockState(DetailCodeStr1Requestdto request);
+
         SRes UpdateCellState(DetailCodeStr1Requestdto request);
+
         SRes DataSwapCell(DataSwapCellStrRequest request);
+
         SRes DataBasePatch(DataBasePatchRequest request);
+
         SRes DataBasePatch2(DataBasePatchRequest request);
+
         SRes DataBasePatch3(DataBasePatchRequest request);
+
         SRes UpdatePurchState(UpdatePurchStateRequestdto request);
+
         SRes DataMoveCell(DataSwapCellStrRequest request);
+
         SRes TaskRepeatSend(TaskRepeatRequest request);
+
         SRes UpdateTaskState(UpdateTaskStateRequest request);
+
         SRes SpecialStockOut(PalletizingPackStockOutRequest request);
+
         SRes ManualPalletizingStockOut(ManualPalletizingStockOutRequest request);
+
         SRes OnePackStock(PalletizingPackStockOutRequest request);
+
         public SRes<bool> IsExistTask(DetailCodeRequest request);
+
         public SRes<string> GetLocationInfo(DetailCodeRequest request);
+
         SRes CGrageStockOut(PalletizingPackStockOutRequest request);
+
         SRes CancelPreStockinfo(CancelPreStockinfoRequest request);
+
         SRes<GetTorsRangeRes> GetTorsRange(DetailCodeRequest request);
+
         public SRes<string> GetCGradeList();
+
         public SRes<string> GetBGradeTimeOutList();
+
         SRes ErrorDateclearJob();
+
         SRes FlowDataMoveJob(PalletizingPackStockOutRequest request);
+
         SRes RemoveTaskData(PalletizingPackStockOutRequest request);
     }
-}
+}

File diff suppressed because it is too large
+ 996 - 160
wms.service/Service/SXService.cs


+ 12 - 2
wms.sqlsugar/model/sx/BillPboxrule.cs

@@ -99,6 +99,18 @@ namespace wms.sqlsugar.model.sx
         [SugarColumn(ColumnDataType = "decimal", Length = 18, DecimalDigits = 2, IsNullable = false)]
         public decimal TorsionErrRange { get; set; }
 
+        /// <summary>
+        /// 目标扭转值
+        /// </summary>
+        [SugarColumn(ColumnDataType = "decimal", Length = 18, DecimalDigits = 2, IsNullable = true)]
+        public decimal TorsChkValue { get; set; }
+
+        /// <summary>
+        /// 目标扭转值偏差范围
+        /// </summary>
+        [SugarColumn(ColumnDataType = "decimal", Length = 18, DecimalDigits = 2, IsNullable = true)]
+        public decimal TorsChkValueRange { get; set; }
+
         /// <summary>
         /// 子托盘编码
         /// </summary>
@@ -189,6 +201,4 @@ namespace wms.sqlsugar.model.sx
         [SugarColumn(ColumnDataType = "decimal", Length = 18, IsNullable = true)]
         public decimal BaoCaiWeight { get; set; }
     }
-
-    
 }

+ 12 - 0
wms.sqlsugar/model/sx/BillPboxruleHty.cs

@@ -105,6 +105,18 @@ namespace wms.sqlsugar.model.sx
         [SugarColumn(ColumnDataType = "decimal", Length = 18, DecimalDigits = 2, IsNullable = false)]
         public decimal TorsionErrRange { get; set; }
 
+        /// <summary>
+        /// 目标扭转值
+        /// </summary>
+        [SugarColumn(ColumnDataType = "decimal", Length = 18, DecimalDigits = 2, IsNullable = true)]
+        public decimal TorsChkValue { get; set; }
+
+        /// <summary>
+        /// 目标扭转值偏差范围
+        /// </summary>
+        [SugarColumn(ColumnDataType = "decimal", Length = 18, DecimalDigits = 2, IsNullable = true)]
+        public decimal TorsChkValueRange { get; set; }
+
         /// <summary> 
         /// 子托盘编码
         /// </summary> 

Some files were not shown because too many files changed in this diff