Browse Source

新增装箱逻辑及相关模型类

在 `SxController.cs` 中添加 `PreLockInventoryForLayerPacking` 和 `ProcessLayerPackingOutboundAndCachePallets` 方法以处理码垛层配装箱的预锁库存和出库任务,并增加锁定对象以确保线程安全。

在 `CurtainProductionOrderRequest.cs` 中扩展请求数据结构,新增多个属性。

在 `LayerPackingService.cs` 中增加 `_currentResult` 属性以记录装箱结果,并添加失败原因记录逻辑。

重构 `LayerPackingResult.cs`,增加失败原因列表和处理逻辑。

在 `SxServiceHelp.cs` 和 `ISXService.cs` 中重构方法以提高可读性,并支持新的装箱逻辑。

在 `SXService.cs` 中集成MES系统以验证箱号,并重构相关方法。

在多个模型类中添加新属性以支持新的业务需求和装箱逻辑。

新增 `CompareProduct.cs`、`MesVerificationResult.cs`、`PackingFailureReason.cs` 等模型类以支持新的业务逻辑和数据结构。
林豪 左 3 months ago
parent
commit
9bd55dd150

+ 36 - 1
wms.api/Controllers/SxController.cs

@@ -59,7 +59,8 @@ namespace wms.api.Controllers
         private static object lockerSxMoveTask = new object();
         private static object lockerPalletizingSpcPackStockOut = new object();
         private static object lockerPalletizingSpcPackPreALL = new object();
-
+        private static object lockPreLockInventoryForLayerPacking = new object();
+        private static object lockProcessLayerPackingOutboundAndCachePallets = new object();
         private static object lockerPalletizingSpcNotPackStockOut = new object();
         private static object lockerTorsChkStationbinde = new object();
         private static object lockerTorsChkErrorinfo = new object();
@@ -374,6 +375,40 @@ namespace wms.api.Controllers
             }
         }
 
+        /// <summary>
+        /// 码垛层配装箱预锁库存程序 --在用
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [HttpPost]
+        public SRes PreLockInventoryForLayerPacking(PalletizingPackStockOutRequest request)
+        {
+            lock (lockPreLockInventoryForLayerPacking)
+            {
+                var temp2 = _sxService.PreLockInventoryForLayerPacking(request);
+                temp2.ResMsg = "层配预占库存执行结果:" + temp2.ResMsg; //+ ";控制箱预占库存执行结果:"+ temp1.ResMsg;
+                return temp2;
+            }
+        }
+
+        /// <summary>
+        /// 码垛层配装箱 --在用
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [HttpPost]
+        public SRes ProcessLayerPackingOutboundAndCachePallets(PalletizingPackStockOutRequest request)
+        {
+            lock (lockProcessLayerPackingOutboundAndCachePallets)
+            {
+                var timesign = IdFactory.NewId();
+                var temp2 = _sxService.ProcessLayerPackingOutboundAndCachePallets(request);
+
+                temp2.ResMsg = "层配装箱执行结果:" + temp2.ResMsg;
+                return temp2;
+            }
+        }
+
         ///// <summary>
         /////  码垛SPC非控制装箱计算预占箱号-去掉黑盘和批次限制
         ///// </summary>

+ 45 - 5
wms.dto/request/fj/CurtainProductionOrderRequest.cs

@@ -6,7 +6,6 @@ namespace wms.dto.request.fj
 {
     public class CurtainProductionOrderRequest : BaseRequest
     {
-
         /// <summary>
         /// 生产订单
         /// </summary>
@@ -74,8 +73,6 @@ namespace wms.dto.request.fj
 
         public List<ListPackagelnfoItem> ListPackagelnfo { get; set; }
 
-        
-
         /////<summary>
         /////芯股标记
         /////</summary>
@@ -90,6 +87,7 @@ namespace wms.dto.request.fj
         /// 扭转目标值
         /// </summary>
         public string TorsionTargetval { get; set; }
+
         /// <summary>
         /// 扭转目标值范围
         /// </summary>
@@ -99,6 +97,47 @@ namespace wms.dto.request.fj
         /// 每箱焊点总数
         /// </summary>
         public decimal HuSolderJointMax { get; set; }
+
+        /// <summary>
+        /// 绕向及颜色信息
+        /// </summary>
+        public List<Tqmtmbm3_Upload_Dto> WindingList { get; set; }
+    }
+
+    /// <summary>
+    /// 绕向及颜色信息
+    /// </summary>
+    public class Tqmtmbm3_Upload_Dto
+    {
+        /// <summary>
+        /// 标准号
+        /// </summary>
+        public string IDX_NO { get; set; }
+
+        /// <summary>
+        /// 标准名称
+        /// </summary>
+        public string IDX_NAME { get; set; }
+
+        /// <summary>
+        /// L/R类别
+        /// </summary>
+        public string LR_TYPE { get; set; }
+
+        /// <summary>
+        /// 工字轮型号
+        /// </summary>
+        public string SPOOL { get; set; }
+
+        /// <summary>
+        /// 工字轮主色 浅蓝/灰色/橙色
+        /// </summary>
+        public string SPOOL_MAJOR_COLOR { get; set; }
+
+        /// <summary>
+        /// 收线自动扭转检测(俯视)  顺时针/逆时针
+        /// </summary>
+        public string TAKEUP_SIDE { get; set; }
     }
 
     public class ListBoxInfoItem
@@ -132,11 +171,11 @@ namespace wms.dto.request.fj
         /// 是否允许焊点盘
         /// </summary>
         public string? IsSolder { get; set; }
+
         /// <summary>
         /// 绕向
         /// </summary>
         public string? Winding { get; set; }
-
     }
 
     public class ListPackagelnfoItem
@@ -145,9 +184,10 @@ namespace wms.dto.request.fj
         public string MaterialDesc { get; set; }
         public decimal Quantity { get; set; }
         public string PackagingCode { get; set; }
+
         /// <summary>
         /// 物料单重
         /// </summary>
         public decimal Weight { get; set; }
     }
-}
+}

+ 46 - 0
wms.service/Help/LayerPacking/CompareProduct.cs

@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using wms.dto.response.sx;
+using wms.service.Help.LayerPacking.model;
+
+namespace wms.service.Help.LayerPacking
+{
+    /// <summary>
+    /// 比较产品
+    /// </summary>
+    public class CompareProduct : IEqualityComparer<LayerPackingProduct>
+    {
+        /// <summary>
+        /// 比较产品
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        /// <returns></returns>
+        public bool Equals(LayerPackingProduct x, LayerPackingProduct y)
+        {
+            if (ReferenceEquals(x, y)) return true;
+            if (x == null || y == null) return false;
+
+            return string.Equals(x.ContGrpBarCode, y.ContGrpBarCode, StringComparison.Ordinal);
+        }
+
+        /// <summary>
+        /// 获取产品哈希码
+        /// </summary>
+        /// <param name="obj"></param>
+        /// <returns></returns>
+        public int GetHashCode(LayerPackingProduct obj)
+        {
+            if (obj?.ContGrpBarCode == null) return 0;
+
+            // 使用更好的哈希算法,减少碰撞
+            unchecked
+            {
+                int hash = 17;
+                hash = hash * 31 + obj.ContGrpBarCode.GetHashCode();
+                return hash;
+            }
+        }
+    }
+}

+ 256 - 30
wms.service/Help/LayerPacking/LayerPackingService.cs

@@ -66,6 +66,11 @@ namespace wms.service.Help.LayerPacking
         /// </summary>
         private const decimal MAX_EXTREME_PERCENTAGE = 0.25m;
 
+        /// <summary>
+        /// 当前装箱结果(用于记录失败原因)
+        /// </summary>
+        private LayerPackingResult _currentResult;
+
         /// <summary>
         /// 构造函数
         /// </summary>
@@ -288,6 +293,7 @@ namespace wms.service.Help.LayerPacking
         public LayerPackingResult PackBoxe()
         {
             var result = new LayerPackingResult();
+            _currentResult = result; // 设置当前结果引用
             var startTime = DateTime.Now;
             int boxNumber = 1;
 
@@ -298,6 +304,23 @@ namespace wms.service.Help.LayerPacking
             {
                 result.RemainingProducts = availableProducts.Count;
                 result.RemainingSolderProducts = availableProducts.Count(p => p.IsSolderProduct);
+
+                // 记录失败原因
+                result.AddFailureReason(new PackingFailureReason
+                {
+                    Type = PackingFailureType.InsufficientProducts,
+                    Message = $"产品数量不足,需要{PRODUCTS_PER_BOX}个,实际只有{availableProducts.Count}个",
+                    Details = $"其中焊点盘数量:{availableProducts.Count(p => p.IsSolderProduct)}个",
+                    RelatedData = new Dictionary<string, object>
+                    {
+                        { "RequiredProducts", PRODUCTS_PER_BOX },
+                        { "AvailableProducts", availableProducts.Count },
+                        { "AvailableSolderProducts", availableProducts.Count(p => p.IsSolderProduct) }
+                    }
+                });
+
+                result.TotalProcessingTime = DateTime.Now - startTime;
+                return result;
             }
 
             // 每次装箱前更新目标值(基于当前可用产品)
@@ -332,7 +355,6 @@ namespace wms.service.Help.LayerPacking
                 // 标记产品为已使用
                 MarkProductsAsUsed(box);
 
-                Console.WriteLine($"  ✓ 第 {boxNumber} 箱装配成功,扭转值总和: {box.TotalTorsion:F2},焊点盘数: {box.SolderProductCount},焊点总数: {box.TotalSolderJoints}");
                 boxNumber++;
 
                 // 更新极值阈值
@@ -343,9 +365,28 @@ namespace wms.service.Help.LayerPacking
             }
             else
             {
-                Console.WriteLine($"  ✗ 无法继续装箱,剩余产品不满足约束条件");
                 result.RemainingProducts = availableProducts.Count;
                 result.RemainingSolderProducts = availableProducts.Count(p => p.IsSolderProduct);
+
+                // 如果没有记录其他失败原因,添加通用失败原因
+                if (result.FailureReasons.Count == 0)
+                {
+                    result.AddFailureReason(new PackingFailureReason
+                    {
+                        Type = PackingFailureType.NoValidSolutionFound,
+                        Message = "无法找到满足所有约束条件的装箱方案",
+                        Details = $"尝试了多种策略但均未成功。产品池平均值:{currentPoolAverage:F3},目标值:{_constraints.X:F3}",
+                        RelatedData = new Dictionary<string, object>
+                        {
+                            { "AvailableProducts", availableProducts.Count },
+                            { "ProductPoolAverage", currentPoolAverage },
+                            { "BoxTarget", _constraints.X },
+                            { "LayerTarget", _constraints.C },
+                            { "ExtremeHighThreshold", _dynamicHighThreshold },
+                            { "ExtremeLowThreshold", _dynamicLowThreshold }
+                        }
+                    });
+                }
             }
 
             result.TotalProductsUsed = result.Boxes.Sum(b => b.GetTotalProductCount());
@@ -380,7 +421,12 @@ namespace wms.service.Help.LayerPacking
 
             // 获取当前可用产品
             var currentAvailableProducts = _productPool.Where(p => !p.IsUsed).ToList();
-            if (currentAvailableProducts.Count < PRODUCTS_PER_BOX) return null;
+            if (currentAvailableProducts.Count < PRODUCTS_PER_BOX)
+            {
+                _currentResult?.AddFailureReason(PackingFailureType.InsufficientProducts,
+                    $"产品数量不足:需要{PRODUCTS_PER_BOX}个,只有{currentAvailableProducts.Count}个");
+                return null;
+            }
 
             // 策略1:目标导向装箱策略 - 最高优先级
             var targetOrientedBox = TargetOrientedPacking();
@@ -460,6 +506,8 @@ namespace wms.service.Help.LayerPacking
                 catch (Exception ex)
                 {
                     // 记录错误但继续执行
+                    _currentResult?.AddFailureReason(PackingFailureType.Other,
+                        "随机装箱策略出现异常", ex.Message);
                 }
             });
 
@@ -467,11 +515,88 @@ namespace wms.service.Help.LayerPacking
             if (bestBox == null)
             {
                 bestBox = AggressiveSearch();
+                if (bestBox == null)
+                {
+                    // 记录最终失败原因
+                    RecordFinalFailureReasons(currentAvailableProducts);
+                }
             }
 
             return bestBox;
         }
 
+        /// <summary>
+        /// 记录最终失败原因
+        /// </summary>
+        /// <param name="availableProducts">可用产品列表</param>
+        private void RecordFinalFailureReasons(List<LayerPackingProduct> availableProducts)
+        {
+            // 分析产品分布
+            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();
+
+            // 极值产品不成对
+            if (extremeHigh.Count != extremeLow.Count || (extremeHigh.Count > 0 && extremeLow.Count == 0) || (extremeHigh.Count == 0 && extremeLow.Count > 0))
+            {
+                _currentResult?.AddFailureReason(new PackingFailureReason
+                {
+                    Type = PackingFailureType.ExtremePairConstraintViolation,
+                    Message = "极值产品无法成对匹配",
+                    Details = $"极高值产品:{extremeHigh.Count}个,极低值产品:{extremeLow.Count}个",
+                    RelatedData = new Dictionary<string, object>
+                    {
+                        { "ExtremeHighCount", extremeHigh.Count },
+                        { "ExtremeLowCount", extremeLow.Count },
+                        { "NormalCount", normal.Count },
+                        { "HighThreshold", _dynamicHighThreshold },
+                        { "LowThreshold", _dynamicLowThreshold }
+                    }
+                });
+            }
+
+            // 产品值分布问题
+            decimal avgValue = availableProducts.Average(p => p.TorsChkValue);
+            decimal variance = (decimal)availableProducts.Sum(p => Math.Pow((double)(p.TorsChkValue - avgValue), 2)) / availableProducts.Count;
+            decimal stdDev = (decimal)Math.Sqrt((double)variance);
+
+            if (Math.Abs(avgValue - _constraints.P) > _constraints.P1 * 2)
+            {
+                _currentResult?.AddFailureReason(new PackingFailureReason
+                {
+                    Type = PackingFailureType.TorsionConstraintViolation,
+                    Message = "产品池平均值严重偏离目标值",
+                    Details = $"产品池平均值:{avgValue:F3},目标值:{_constraints.P:F3},偏差:{Math.Abs(avgValue - _constraints.P):F3}",
+                    RelatedData = new Dictionary<string, object>
+                    {
+                        { "ProductPoolAverage", avgValue },
+                        { "ProductTarget", _constraints.P },
+                        { "StandardDeviation", stdDev },
+                        { "Deviation", Math.Abs(avgValue - _constraints.P) }
+                    }
+                });
+            }
+
+            // 焊点产品过多
+            int solderCount = availableProducts.Count(p => p.IsSolderProduct);
+            if (_constraints.SolderMaxCount > 0 && solderCount > _constraints.SolderMaxCount)
+            {
+                _currentResult?.AddFailureReason(new PackingFailureReason
+                {
+                    Type = PackingFailureType.SolderConstraintViolation,
+                    Message = "焊点盘数量超过约束限制",
+                    Details = $"焊点盘数量:{solderCount}个,限制:{_constraints.SolderMaxCount}个",
+                    RelatedData = new Dictionary<string, object>
+                    {
+                        { "SolderProductCount", solderCount },
+                        { "SolderMaxCount", _constraints.SolderMaxCount },
+                        { "TotalSolderJoints", availableProducts.Sum(p => p.SolderCount) },
+                        { "PerSolderMaxCount", _constraints.PerSolderMaxCount }
+                    }
+                });
+            }
+        }
+
         /// <summary>
         /// 目标导向装箱策略 - 优先考虑接近目标值
         /// </summary>
@@ -514,14 +639,26 @@ namespace wms.service.Help.LayerPacking
                     usedProducts
                 );
 
-                if (layer == null || layer.Products.Count != PRODUCTS_PER_LAYER) return null;
+                if (layer == null || layer.Products.Count != PRODUCTS_PER_LAYER)
+                {
+                    _currentResult?.AddFailureReason(PackingFailureType.Other,
+                        $"目标导向策略在第{layerIndex + 1}层构建失败",
+                        $"无法找到满足层目标值{idealLayerSum:F3}的产品组合");
+                    return null;
+                }
 
                 box.Layers.Add(layer);
                 currentBoxSum += layer.TotalTorsion;
             }
 
             // 检查并修复焊点约束
-            if (!CheckAndFixSolderConstraints(box, availableProducts)) return null;
+            if (!CheckAndFixSolderConstraints(box, availableProducts))
+            {
+                _currentResult?.AddFailureReason(PackingFailureType.SolderConstraintViolation,
+                    "无法满足焊点约束要求",
+                    $"焊点盘数:{box.SolderProductCount},焊点总数:{box.TotalSolderJoints}");
+                return null;
+            }
 
             return box;
         }
@@ -752,7 +889,12 @@ namespace wms.service.Help.LayerPacking
                             .ToList();
                     }
 
-                    if (!candidates.Any()) return null;
+                    if (!candidates.Any())
+                    {
+                        _currentResult?.AddFailureReason(PackingFailureType.InsufficientProducts,
+                            $"精确贪心策略在第{layerIndex + 1}层第{i + 1}个产品位置无法找到合适产品");
+                        return null;
+                    }
 
                     // 选择最优的产品(考虑焊点约束)
                     var selected = candidates.First();
@@ -774,7 +916,13 @@ namespace wms.service.Help.LayerPacking
                 if (!layer.IsValid(_constraints) || !layer.HasValidExtremePair(_dynamicHighThreshold, _dynamicLowThreshold))
                 {
                     // 尝试微调
-                    if (!OptimizeLayerForTarget(layer, normalAvailable, usedProducts, layerTargetSum)) return null;
+                    if (!OptimizeLayerForTarget(layer, normalAvailable, usedProducts, layerTargetSum))
+                    {
+                        _currentResult?.AddFailureReason(PackingFailureType.TorsionConstraintViolation,
+                            $"精确贪心策略第{layerIndex + 1}层无法满足约束",
+                            $"层总值:{layer.TotalTorsion:F3},目标:{layerTargetSum:F3},偏差:{layer.GetDeviationFromTarget(layerTargetSum):F3}");
+                        return null;
+                    }
                 }
 
                 box.Layers.Add(layer);
@@ -896,12 +1044,24 @@ namespace wms.service.Help.LayerPacking
                     usedProducts
                 );
 
-                if (layer == null || layer.Products.Count != PRODUCTS_PER_LAYER) return null;
+                if (layer == null || layer.Products.Count != PRODUCTS_PER_LAYER)
+                {
+                    _currentResult?.AddFailureReason(PackingFailureType.Other,
+                        $"极值优先策略在第{layerIndex + 1}层构建失败",
+                        $"剩余极高值:{extremeHigh.Where(p => !usedProducts.Contains(p.Id)).Count()}," +
+                        $"剩余极低值:{extremeLow.Where(p => !usedProducts.Contains(p.Id)).Count()}");
+                    return null;
+                }
                 box.Layers.Add(layer);
             }
 
             // 检查并修复焊点约束
-            if (!CheckAndFixSolderConstraints(box, availableProducts)) return null;
+            if (!CheckAndFixSolderConstraints(box, availableProducts))
+            {
+                _currentResult?.AddFailureReason(PackingFailureType.SolderConstraintViolation,
+                    "极值优先策略无法满足焊点约束");
+                return null;
+            }
 
             return box;
         }
@@ -995,7 +1155,14 @@ namespace wms.service.Help.LayerPacking
             }
 
             // 最终验证:确保层满足极值成对约束
-            if (!layer.HasValidExtremePair(_dynamicHighThreshold, _dynamicLowThreshold)) return null;
+            if (!layer.HasValidExtremePair(_dynamicHighThreshold, _dynamicLowThreshold))
+            {
+                _currentResult?.AddFailureReason(PackingFailureType.ExtremePairConstraintViolation,
+                    "构建的层不满足极值成对约束",
+                    $"层中极高值数:{layer.Products.Count(p => p.TorsChkValue >= _dynamicHighThreshold)}," +
+                    $"极低值数:{layer.Products.Count(p => p.TorsChkValue <= _dynamicLowThreshold)}");
+                return null;
+            }
 
             return layer;
         }
@@ -1039,10 +1206,20 @@ namespace wms.service.Help.LayerPacking
                     layer = BuildMixedExtremeLayer(extremeHigh, extremeLow, normalHigh, normalLow, usedProducts);
                 }
 
-                if (layer == null || layer.Products.Count != PRODUCTS_PER_LAYER) return null;
+                if (layer == null || layer.Products.Count != PRODUCTS_PER_LAYER)
+                {
+                    _currentResult?.AddFailureReason(PackingFailureType.Other,
+                        $"智能极值策略在第{layerIndex + 1}层构建失败");
+                    return null;
+                }
 
                 // 验证层的极值成对约束
-                if (!layer.HasValidExtremePair(_dynamicHighThreshold, _dynamicLowThreshold)) return null;
+                if (!layer.HasValidExtremePair(_dynamicHighThreshold, _dynamicLowThreshold))
+                {
+                    _currentResult?.AddFailureReason(PackingFailureType.ExtremePairConstraintViolation,
+                        $"智能极值策略第{layerIndex + 1}层不满足极值成对约束");
+                    return null;
+                }
 
                 box.Layers.Add(layer);
             }
@@ -1627,6 +1804,13 @@ namespace wms.service.Help.LayerPacking
                 }
             }
 
+            if (excess > 0)
+            {
+                _currentResult?.AddFailureReason(PackingFailureType.SolderConstraintViolation,
+                    $"无法将焊点盘数量降低到约束要求,超出{excess}个",
+                    $"当前焊点盘数:{box.SolderProductCount},限制:{_constraints.SolderMaxCount}");
+            }
+
             return excess <= 0;
         }
 
@@ -1673,6 +1857,13 @@ namespace wms.service.Help.LayerPacking
                 }
             }
 
+            if (excessJoints > 0)
+            {
+                _currentResult?.AddFailureReason(PackingFailureType.SolderConstraintViolation,
+                    $"无法将焊点总数降低到约束要求,超出{excessJoints}个",
+                    $"当前焊点总数:{box.TotalSolderJoints},限制:{_constraints.PerSolderMaxCount}");
+            }
+
             return excessJoints <= 0;
         }
 
@@ -1690,9 +1881,9 @@ namespace wms.service.Help.LayerPacking
                 P = _constraints.P,
                 P1 = _constraints.P1,
                 X = _constraints.X,
-                X1 = _constraints.X1 * 1.2m, // 放宽20%
+                X1 = _constraints.X1 * _constraints.BR, // 放宽
                 C = _constraints.C,
-                C1 = _constraints.C1 * 1.2m, // 放宽20%
+                C1 = _constraints.C1 * _constraints.LR, // 放宽
                 SolderMaxCount = _constraints.SolderMaxCount,
                 PerSolderMaxCount = _constraints.PerSolderMaxCount,
                 ProductsPerBox = _constraints.ProductsPerBox
@@ -1783,7 +1974,12 @@ namespace wms.service.Help.LayerPacking
 
             // 生成初始解
             var currentBox = GenerateInitialSolution(candidates);
-            if (currentBox == null) return null;
+            if (currentBox == null)
+            {
+                _currentResult?.AddFailureReason(PackingFailureType.NoValidSolutionFound,
+                    "模拟退火算法无法生成初始解");
+                return null;
+            }
 
             var bestBox = currentBox;
             var bestScore = CalculateBoxScore(currentBox);
@@ -1816,7 +2012,15 @@ namespace wms.service.Help.LayerPacking
                 temperature *= coolingRate;
             }
 
-            return bestBox.IsValid(_constraints) && bestBox.HasValidExtremePairs(_dynamicHighThreshold, _dynamicLowThreshold) ? bestBox : null;
+            if (!bestBox.IsValid(_constraints) || !bestBox.HasValidExtremePairs(_dynamicHighThreshold, _dynamicLowThreshold))
+            {
+                _currentResult?.AddFailureReason(PackingFailureType.NoValidSolutionFound,
+                    "模拟退火算法未能找到满足所有约束的解",
+                    $"迭代次数:{iterations},最佳分数:{bestScore:F3}");
+                return null;
+            }
+
+            return bestBox;
         }
 
         /// <summary>
@@ -2101,37 +2305,59 @@ namespace wms.service.Help.LayerPacking
             // 基础分:箱子是否满足约束
             if (!box.IsValid(_constraints))
             {
-                score += 10000; // 不满足约束的严重惩罚
+                score += 10000;
             }
 
             // 极值成对约束检查
             if (!box.HasValidExtremePairs(_dynamicHighThreshold, _dynamicLowThreshold))
             {
-                score += 5000; // 不满足极值成对约束的惩罚
+                score += 5000;
             }
 
-            // 整箱偏差(提高权重,强调接近目标值
+            // 整箱偏差(处理目标值为0的情况
             var boxDiff = box.GetDeviationFromTarget(_constraints.X);
-            score += boxDiff * 5; // 提高权重
+            score += boxDiff * 5;
 
-            // 整箱偏差百分比惩罚
-            decimal boxDeviationPercent = boxDiff / _constraints.X * 100;
-            if (boxDeviationPercent > 5) // 偏差超过5%
+            // 整箱偏差百分比惩罚(避免除零)
+            if (_constraints.X != 0)
             {
-                score += boxDeviationPercent * 10;
+                decimal boxDeviationPercent = boxDiff / Math.Abs(_constraints.X) * 100;
+                if (boxDeviationPercent > 5)
+                {
+                    score += boxDeviationPercent * 10;
+                }
+            }
+            else
+            {
+                // 目标值为0时,使用绝对偏差
+                if (boxDiff > _constraints.X1 * 0.5m)
+                {
+                    score += boxDiff * 20;
+                }
             }
 
-            // 每层偏差(提高权重)
+            // 每层偏差
             foreach (var layer in box.Layers)
             {
                 var layerDiff = layer.GetDeviationFromTarget(_constraints.C);
-                score += layerDiff * 2; // 提高权重
+                score += layerDiff * 2;
 
-                // 层偏差百分比惩罚
-                decimal layerDeviationPercent = layerDiff / _constraints.C * 100;
-                if (layerDeviationPercent > 5) // 偏差超过5%
+                // 层偏差百分比惩罚(避免除零)
+                if (_constraints.C != 0)
                 {
-                    score += layerDeviationPercent * 5;
+                    decimal layerDeviationPercent = layerDiff / Math.Abs(_constraints.C) * 100;
+                    if (layerDeviationPercent > 5)
+                    {
+                        score += layerDeviationPercent * 5;
+                    }
+                }
+                else
+                {
+                    // 目标值为0时,使用绝对偏差
+                    if (layerDiff > _constraints.C1 * 0.5m)
+                    {
+                        score += layerDiff * 10;
+                    }
                 }
 
                 // 层约束违反惩罚

+ 1239 - 0
wms.service/Help/LayerPacking/SxLayerPackingHelp.cs

@@ -0,0 +1,1239 @@
+using AutoMapper;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using WCS.Entity.sx;
+using wms.dto;
+using wms.dto.request;
+using wms.dto.request.share;
+using wms.dto.request.sx;
+using wms.dto.response;
+using wms.dto.response.sx;
+using wms.service.Help.LayerPacking;
+using wms.service.Help.LayerPacking.model;
+using wms.service.Service;
+using wms.sqlsugar;
+using wms.sqlsugar.model.sx;
+using wms.util.Ext;
+using wms.util.Http;
+using TaskStatus = wms.dto.TaskStatus;
+
+namespace wms.service.Help.Packing
+{
+    /// <summary>
+    /// 层配装箱帮助类
+    /// </summary>
+    public class SxLayerPackingHelp
+    {
+        private readonly Repository<BaseWarecell> _wareCell;
+        private readonly Repository<BillInvnow> _invNow;
+        private readonly Repository<BillPboxrule> _boxRule;
+        private readonly Repository<sxSysConfig> _sysconfing;
+        private readonly RepositoryTask<BillTimeOutRecord> _timeoutrecord;
+        private readonly ILogger<SXService> _logger;
+        private readonly Repository<PalletLayerMath> _palletLayerMath;
+        private readonly Repository<BillInvflow> _invFlow;
+        private readonly RepositoryTask<WCS_TaskOld> _wcsTaskOld;
+        private readonly Repository<BaseWarehouse> _wareHouse;
+        private readonly RepositoryTask<WCS_TaskDtl> _taskDetail;
+        private readonly RepositoryTask<WCS_TaskInfo> _task;
+        private readonly Repository<Palletizing> _palletiz;
+        private readonly ITenant _db;
+        private readonly IMapper _mapper;
+        private readonly object _lockerPalletizingPackTask;
+        private readonly object _lockerApplyLoc;
+
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        public SxLayerPackingHelp(
+            Repository<BaseWarecell> wareCell,
+            Repository<BillInvnow> invNow,
+            Repository<BillPboxrule> boxRule,
+            Repository<sxSysConfig> sysconfing,
+            RepositoryTask<BillTimeOutRecord> timeoutrecord,
+            ILogger<SXService> logger,
+            Repository<PalletLayerMath> palletLayerMath,
+            Repository<BillInvflow> invFlow,
+            RepositoryTask<WCS_TaskOld> wcsTaskOld,
+            Repository<BaseWarehouse> wareHouse,
+            RepositoryTask<WCS_TaskDtl> taskDetail,
+            RepositoryTask<WCS_TaskInfo> task,
+            Repository<Palletizing> palletiz,
+            ITenant db,
+            IMapper mapper,
+            object lockerPalletizingPackTask,
+            object lockerApplyLoc)
+        {
+            _wareCell = wareCell;
+            _invNow = invNow;
+            _boxRule = boxRule;
+            _sysconfing = sysconfing;
+            _timeoutrecord = timeoutrecord;
+            _logger = logger;
+            _palletLayerMath = palletLayerMath;
+            _invFlow = invFlow;
+            _wcsTaskOld = wcsTaskOld;
+            _wareHouse = wareHouse;
+            _taskDetail = taskDetail;
+            _task = task;
+            _palletiz = palletiz;
+            _db = db;
+            _mapper = mapper;
+            _lockerPalletizingPackTask = lockerPalletizingPackTask;
+            _lockerApplyLoc = lockerApplyLoc;
+        }
+
+        #region 获取可用库存
+
+        /// <summary>
+        ///  获取可用库存
+        /// </summary>
+        /// <returns></returns>
+        public IEnumerable<LayerPackingProduct> GetAvailableInventory()
+        {
+            int timeOutHoldHours = 72;
+            var timeOut = _sysconfing.GetFirst(p => p.Code == "CP_TimeOutHoldHours");
+            if (timeOut != null) timeOutHoldHours = int.Parse(timeOut.SContent);
+
+            var normalInventory = GetNormalInventory(timeOutHoldHours);
+            var reworkInventory = GetReworkInventory(timeOutHoldHours);
+            var torsInventory = GetTorsInventory(timeOutHoldHours);
+            var torsReworkInventory = GetTorsReworkInventory(timeOutHoldHours);
+
+            var allInventory = normalInventory
+                .Union(reworkInventory)
+                .Union(torsInventory)
+                .Union(torsReworkInventory)
+                .Distinct(new CompareProduct());
+
+            return FilterByTunnelRestrictions(allInventory);
+        }
+
+        /// <summary>
+        /// 获取非检测非重绕盘
+        /// </summary>
+        /// <param name="timeOutHoldHours">超时小时</param>
+        /// <returns></returns>
+        private IEnumerable<LayerPackingProduct> GetNormalInventory(int timeOutHoldHours)
+        {
+            return from loc in _wareCell.GetList(p =>
+                    p.IsStop == LocationStop.LocationInvoke.GetHashCode() &&
+                    p.StateNum == LocationState.LocationState_Full)
+                   join stock in _invNow.GetList(p =>
+                       p.InvStateCode == InvState.InvEcecState_In.ToString() &&
+                       p.ExecStateCode == InvLockState.InvState_Normal.ToString() &&
+                       p.Grade == "A" &&
+                       !p.IsControlpanel &&
+                       !p.IsTorsChk &&
+                       string.IsNullOrEmpty(p.PreStock) &&
+                       p.ContUsageQty <= 0 &&
+                       !p.ProductMachCode.Contains("R") &&
+                       (DateTime.Now - p.ProductTime).TotalHours < timeOutHoldHours)
+                   on loc.ContGrpBarCode equals stock.ContGrpBarCode
+                   join rule in _boxRule.AsQueryable().Where(p => p.PackRule == "CP").Select(p => new { p.DocsNo, p.SkuCode }).Distinct().ToList()
+                   on stock.InDocsNo equals rule.DocsNo
+                   select CreateStockTemp(loc, stock, rule.SkuCode);
+        }
+
+        /// <summary>
+        /// 获取非检测重绕盘
+        /// </summary>
+        /// <param name="timeOutHoldHours">超时小时</param>
+        /// <returns></returns>
+        private IEnumerable<LayerPackingProduct> GetReworkInventory(int timeOutHoldHours)
+        {
+            return from loc in _wareCell.GetList(p =>
+                    p.IsStop == LocationStop.LocationInvoke.GetHashCode() &&
+                    p.StateNum == LocationState.LocationState_Full)
+                   join stock in _invNow.GetList(p =>
+                       p.InvStateCode == InvState.InvEcecState_In.ToString() &&
+                       p.ExecStateCode == InvLockState.InvState_Normal.ToString() &&
+                       p.Grade == "A" &&
+                       !p.IsControlpanel &&
+                       !p.IsTorsChk &&
+                       string.IsNullOrEmpty(p.PreStock) &&
+                       p.ContUsageQty <= 0 &&
+                       p.ProductMachCode.Contains("R") &&
+                       (DateTime.Now - p.OneInTime).TotalHours < timeOutHoldHours)
+                   on loc.ContGrpBarCode equals stock.ContGrpBarCode
+                   join rule in _boxRule.AsQueryable().Where(p => p.PackRule == "CP").Select(p => new { p.DocsNo, p.SkuCode }).Distinct().ToList()
+                   on stock.InDocsNo equals rule.DocsNo
+                   select CreateStockTemp(loc, stock, rule.SkuCode);
+        }
+
+        /// <summary>
+        /// 获取已检测非重绕盘
+        /// </summary>
+        /// <param name="timeOutHoldHours">超时小时</param>
+        /// <returns></returns>
+        private IEnumerable<LayerPackingProduct> GetTorsInventory(int timeOutHoldHours)
+        {
+            return from loc in _wareCell.GetList(p =>
+                    p.IsStop == LocationStop.LocationInvoke.GetHashCode() &&
+                    p.StateNum == LocationState.LocationState_Full)
+                   join stock in _invNow.GetList(p =>
+                       p.InvStateCode == InvState.InvEcecState_In.ToString() &&
+                       p.ExecStateCode == InvLockState.InvState_Normal.ToString() &&
+                       p.Grade == "A" &&
+                       p.IsControlpanel &&
+                       p.IsTorsChk &&
+                       p.TorsChkQty > 0 &&
+                       string.IsNullOrEmpty(p.PreStock) &&
+                       !p.ProductMachCode.Contains("R") &&
+                       p.ContUsageQty <= 0 &&
+                       (DateTime.Now - p.ProductTime).TotalHours < timeOutHoldHours)
+                   on loc.ContGrpBarCode equals stock.ContGrpBarCode
+                   join rule in _boxRule.AsQueryable().Where(p => p.PackRule == "CP").Select(p => new { p.DocsNo, p.SkuCode }).Distinct().ToList()
+                   on stock.InDocsNo equals rule.DocsNo
+                   select CreateStockTemp(loc, stock, rule.SkuCode);
+        }
+
+        /// <summary>
+        /// 获取已检测重绕盘
+        /// </summary>
+        /// <param name="timeOutHoldHours">超时小时</param>
+        /// <returns></returns>
+        private IEnumerable<LayerPackingProduct> GetTorsReworkInventory(int timeOutHoldHours)
+        {
+            return from loc in _wareCell.GetList(p =>
+                    p.IsStop == LocationStop.LocationInvoke.GetHashCode() &&
+                    p.StateNum == LocationState.LocationState_Full)
+                   join stock in _invNow.GetList(p =>
+                       p.InvStateCode == InvState.InvEcecState_In.ToString() &&
+                       p.ExecStateCode == InvLockState.InvState_Normal.ToString() &&
+                       p.Grade == "A" &&
+                       p.IsControlpanel &&
+                       p.IsTorsChk &&
+                       p.TorsChkQty > 0 &&
+                       string.IsNullOrEmpty(p.PreStock) &&
+                       p.ProductMachCode.Contains("R") &&
+                       p.ContUsageQty <= 0 &&
+                       (DateTime.Now - p.OneInTime).TotalHours < timeOutHoldHours)
+                   on loc.ContGrpBarCode equals stock.ContGrpBarCode
+                   join rule in _boxRule.AsQueryable().Where(p => p.PackRule == "CP").Select(p => new { p.DocsNo, p.SkuCode }).Distinct().ToList()
+                   on stock.InDocsNo equals rule.DocsNo
+                   select CreateStockTemp(loc, stock, rule.SkuCode);
+        }
+
+        /// <summary>
+        /// 创建库存临时对象
+        /// </summary>
+        /// <param name="loc">货位信息</param>
+        /// <param name="stock">库存信息</param>
+        /// <param name="skuCode">sku</param>
+        /// <returns></returns>
+        private LayerPackingProduct CreateStockTemp(BaseWarecell loc, BillInvnow stock, string skuCode)
+        {
+            return new LayerPackingProduct
+            {
+                MatCode = stock.MatCode,
+                InvBarCode = stock.InvBarCode,
+                Grade = stock.Grade,
+                InvStateCode = stock.InvStateCode,
+                ProductTime = stock.ProductTime,
+                WbGroupCode = stock.WbGroupCode,
+                IsTorsChk = stock.IsTorsChk,
+                TorsChkQty = stock.TorsChkQty,
+                TorsChkValue = stock.TorsChkValue == null ? 0 : (decimal)stock.TorsChkValue,
+                HoldTime = stock.HoldTime,
+                ProductMachCode = stock.ProductMachCode,
+                IsControlpanel = stock.IsControlpanel,
+                HWTypeCode = stock.HWTypeCode,
+                SolderCount = stock.SolderCount,
+                IsRework = stock.IsRework,
+                IsBlack = stock.IsBlack,
+                Col = loc.Col,
+                Layer = loc.Layer,
+                Shelf = loc.Shelf,
+                Depth = loc.Depth,
+                Code = loc.Code,
+                Tunnel = loc.Tunnel,
+                SCRel = loc.SCRel,
+                Floor = loc.Floor,
+                WarehouseCode = loc.WarehouseCode,
+                ContGrpBarCode = loc.ContGrpBarCode,
+                ContGrpId = loc.ContGrpId,
+                Id = loc.Id,
+                StateNum = loc.StateNum,
+                SkuCode = skuCode,
+                Wind = stock.Wind,
+                InDocsNo = stock.InDocsNo,
+                BatchNo = stock.BatchNo
+            };
+        }
+
+        /// <summary>
+        /// 过滤禁用巷道
+        /// </summary>
+        /// <param name="inventory">可用产品</param>
+        /// <returns></returns>
+        public IEnumerable<LayerPackingProduct> FilterByTunnelRestrictions(IEnumerable<LayerPackingProduct> inventory)
+        {
+            var tunnelRestrictions = _sysconfing.GetList(p =>
+                p.SType == "OutStop" && !string.IsNullOrEmpty(p.SContent));
+
+            if (!tunnelRestrictions.Any())
+            {
+                return inventory;
+            }
+
+            var filteredInventory = inventory.ToList();
+
+            // 按楼层过滤禁用巷道
+            for (int floor = 1; floor <= 3; floor++)
+            {
+                var floorRestrictions = tunnelRestrictions
+                    .Where(p => p.Default2 == floor.ToString())
+                    .Select(x => x.Default1)
+                    .ToList();
+
+                if (floorRestrictions.Any())
+                {
+                    var restrictedContainers = filteredInventory
+                        .Where(p => p.Floor == floor &&
+                               floorRestrictions.Contains(p.Tunnel.ToString()))
+                        .Select(p => p.ContGrpBarCode)
+                        .ToList();
+
+                    filteredInventory = filteredInventory
+                        .Where(p => !restrictedContainers.Contains(p.ContGrpBarCode))
+                        .ToList();
+                }
+            }
+
+            return filteredInventory;
+        }
+
+        #endregion 获取可用库存
+
+        #region 装箱逻辑
+
+        /// <summary>
+        /// 按SKU执行装箱
+        /// </summary>
+        /// <param name="skuGroup"></param>
+        /// <param name="boxRules"></param>
+        /// <param name="inventory"></param>
+        /// <returns></returns>
+        public SRes ProcessSkuGroup(
+            IGrouping<string, LayerPackingProduct> skuGroup,
+            List<BillPboxrule> boxRules,
+            IEnumerable<LayerPackingProduct> inventory)
+        {
+            var res = new SRes { ResCode = ResponseStatusCodeEnum.Fail.GetHashCode() };
+            var skuCode = skuGroup.Key;
+            var skuBoxRules = boxRules.Where(p => p.SkuCode == skuCode).OrderBy(p => p.BatchNo).ThenBy(p => p.PBoxCode);
+
+            if (!skuBoxRules.Any())
+            {
+                return HandleNoBoxRuleForSku(skuCode, inventory);
+            }
+
+            var ruleGroups = skuBoxRules.GroupBy(p => new { p.Wind, p.SpoolType, p.FullCountQty });
+
+            foreach (var ruleGroup in ruleGroups)
+            {
+                var result = ProcessRuleGroup(ruleGroup, inventory, skuCode);
+                if (result.ResCode == ResponseStatusCodeEnum.Sucess.GetHashCode())
+                {
+                    return result;
+                }
+                else
+                {
+                    res.ResMsg += $"[{ruleGroup.Key}:{result.ResMsg}]";
+                }
+            }
+
+            return res;
+        }
+
+        /// <summary>
+        /// 处理装箱规则组
+        /// </summary>
+        /// <param name="ruleGroup">箱号</param>
+        /// <param name="inventory">产品</param>
+        /// <param name="skuCode">sku</param>
+        /// <returns></returns>
+        public SRes ProcessRuleGroup(
+            IGrouping<dynamic, BillPboxrule> ruleGroup,
+            IEnumerable<LayerPackingProduct> inventory,
+            string skuCode)
+        {
+            var rule = ruleGroup.OrderBy(p => p.BatchNo).ThenBy(p => p.PBoxCode).FirstOrDefault();
+            if (rule == null) return new SRes { ResCode = ResponseStatusCodeEnum.Fail.GetHashCode() };
+
+            return ProcessSpecialSku(rule, inventory);
+        }
+
+        /// <summary>
+        /// 处理特殊SKU --指不管控机器组
+        /// </summary>
+        /// <param name="skuCode">sku</param>
+        /// <returns></returns>
+        private bool IsSpecialSku(string skuCode)
+        {
+            return skuCode == "6210010401" || skuCode == "6210120101";
+        }
+
+        /// <summary>
+        /// 处理不需要管控机器组的SKU
+        /// </summary>
+        /// <param name="rule"></param>
+        /// <param name="inventory"></param>
+        /// <returns></returns>
+        public SRes ProcessSpecialSku(
+            BillPboxrule rule,
+            IEnumerable<LayerPackingProduct> inventory)
+        {
+            var res = new SRes();
+
+            // 1. 分配库存
+            var allocationResult = AllocateStock(rule, inventory, isSpecialSku: true);
+
+            if (!allocationResult.IsSuccess)
+            {
+                _logger.LogError($"特殊SKU库存分配失败: {allocationResult.ErrorMessage}");
+                res.ResCode = ResponseStatusCodeEnum.Fail.GetHashCode();
+                res.ResMsg = allocationResult.ErrorMessage;
+                return res;
+            }
+
+            // 2. MES验证
+            var mesResult = VerifyBoxWithMes(rule.PBoxCode, rule.BatchNo);
+
+            if (!mesResult.IsSuccess)
+            {
+                UpdateBoxStatus(rule, 2, mesResult.Message);
+                res.ResCode = ResponseStatusCodeEnum.ErrParam.GetHashCode();
+                res.ResMsg = $"MES验证失败: {mesResult.Message}";
+                return res;
+            }
+
+            // 3. 预占库存
+            PreOccupyInventory(rule, allocationResult.BoxInfo);
+
+            res.ResCode = ResponseStatusCodeEnum.Sucess.GetHashCode();
+            return res;
+        }
+
+        /// <summary>
+        /// 装箱
+        /// </summary>
+        /// <param name="rule">箱号</param>
+        /// <param name="availableStock">有效库存</param>
+        /// <param name="isSpecialSku">是否控制机器组</param>
+        /// <param name="skuGroup">对应的分组sku</param>
+        /// <returns></returns>
+        public StockAllocationResult AllocateStock(
+            BillPboxrule rule,
+            IEnumerable<LayerPackingProduct> availableStock,
+            bool isSpecialSku = false,
+            IGrouping<string, LayerPackingProduct>? skuGroup = null)
+        {
+            var result = new StockAllocationResult();
+
+            // 过滤符合条件的库存
+            var filteredStock = FilterStock(availableStock, rule, isSpecialSku, skuGroup).ToList();
+
+            if (!filteredStock.Any())
+            {
+                result.IsSuccess = false;
+                result.ErrorMessage = "没有符合条件的库存";
+                return result;
+            }
+
+            //构建初始装箱参数
+            var constraints = new LayerPackingConstraints
+            {
+                P = rule.TorsChkValue,
+                P1 = rule.TorsChkValueRange,
+                SolderMaxCount = rule.SolderMaxCount,
+                PerSolderMaxCount = rule.PerSolderMaxCount,
+                ProductsPerBox = rule.FullCountQty
+            };
+            var config = _sysconfing.GetFirst(x => x.Code == "CP_RelaxationRatio");
+            if (config != null)
+            {
+                constraints.BR = decimal.Parse(config.SContent);
+                constraints.LR = decimal.Parse(config.Default1);
+            }
+
+            constraints.UpdateTargetValues(filteredStock);
+            //开始装箱
+            var layerPacking = new LayerPackingService(filteredStock, constraints);
+            var layerPackingResult = layerPacking.PackBoxe();
+
+            //装箱结果为空
+            if (layerPackingResult.Boxes.Count < 1)
+            {
+                result.IsSuccess = false;
+                result.ErrorMessage = JsonConvert.SerializeObject(layerPackingResult.FailureReasons);
+                return result;
+            }
+
+            if (!layerPackingResult.IsSuccess)
+            {
+                result.ErrorMessage = JsonConvert.SerializeObject(layerPackingResult.FailureReasons);
+            }
+
+            result.IsSuccess = true;
+            result.BoxInfo = layerPackingResult.Boxes.First();
+            return result;
+        }
+
+        /// <summary>
+        /// 过滤库存
+        /// </summary>
+        /// <param name="stock">产品</param>
+        /// <param name="rule">装箱规则</param>
+        /// <param name="isSpecialSku">是否管控机器组</param>
+        /// <param name="skuGroup">管控机器组时用于过滤的机器组信息</param>
+        /// <returns></returns>
+        private IEnumerable<LayerPackingProduct> FilterStock(
+            IEnumerable<LayerPackingProduct> stock,
+            BillPboxrule rule,
+            bool isSpecialSku,
+            IGrouping<string, LayerPackingProduct>? skuGroup = null)
+        {
+            var filtered = stock.Where(p => p.SkuCode == rule.SkuCode);
+
+            if (!isSpecialSku && skuGroup != null)
+            {
+                filtered = filtered.Where(p => p.WbGroupCode == skuGroup.Key);
+            }
+
+            if (!string.IsNullOrEmpty(rule.SpoolType))
+            {
+                filtered = filtered.Where(p => p.HWTypeCode == rule.SpoolType);
+            }
+
+            if (!string.IsNullOrEmpty(rule.Wind))
+            {
+                filtered = filtered.Where(p => p.Wind == rule.Wind);
+            }
+
+            return filtered;
+        }
+
+        #region 箱号验证
+
+        /// <summary>
+        /// 箱号验证
+        /// </summary>
+        /// <param name="boxCode">待验证的箱号</param>
+        /// <param name="batchNo">批号</param>
+        /// <returns></returns>
+        public MesVerificationResult VerifyBoxWithMes(string boxCode, string batchNo)
+        {
+            var result = new MesVerificationResult();
+
+            try
+            {
+                var boxVerifyRequest = new MesBoxVerify
+                {
+                    HuNr = boxCode,
+                    Batch = batchNo
+                };
+
+                var requestId = Guid.NewGuid().ToString();
+                var esbRequest = CreateEsbRequest(requestId);
+                var url = wms.util.AppSettings.GetConfig("EsbMesPushUrl");
+                var response = SendMesRequest(boxVerifyRequest, requestId, esbRequest, url);
+                var mesResponse = JsonConvert.DeserializeObject<TorschMesResponse>(response);
+
+                _logger.LogInformation($"MES箱号验证返回: {response}");
+
+                result.IsSuccess = mesResponse.success;
+                result.Message = mesResponse.message;
+                result.RawResponse = response;
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError($"MES验证异常: {ex}");
+                result.IsSuccess = false;
+                result.Message = "MES验证异常: " + ex.Message;
+            }
+
+            return result;
+        }
+
+        /// <summary>
+        /// 创建ESB请求
+        /// </summary>
+        /// <param name="requestId">请求ID</param>
+        /// <returns></returns>
+        private EsbReq CreateEsbRequest(string requestId)
+        {
+            return new EsbReq
+            {
+                headers = new HeadersReq
+                {
+                    serviceCode = "163K162003",
+                    requestId = requestId,
+                    TrackId = requestId,
+                    sourceCode = "163K"
+                }
+            };
+        }
+
+        /// <summary>
+        /// 发送MES请求
+        /// </summary>
+        /// <param name="request">请求参数</param>
+        /// <param name="requestId">请求ID</param>
+        /// <param name="esbRequest">esb请求信息</param>
+        /// <param name="_mesUrl">esb地址</param>
+        /// <returns></returns>
+        private string SendMesRequest(MesBoxVerify request, string requestId, EsbReq esbRequest, string _mesUrl)
+        {
+            return HttpUtil.PostRequest(
+                    _mesUrl,
+                    JsonConvert.SerializeObject(request),
+                    30000,
+                    "UTF-8",
+                    "application/json",
+                    requestId,
+                    esbRequest.headers.sourceCode,
+                    esbRequest.headers.serviceCode);
+        }
+
+        #endregion 箱号验证
+
+        #region 更新装箱/库存信息
+
+        /// <summary>
+        /// 预占用库存
+        /// </summary>
+        /// <param name="rule">箱号</param>
+        /// <param name="boxInfo">产品信息</param>
+        public SRes PreOccupyInventory(
+            BillPboxrule rule,
+            LayerPackingBoxInfo boxInfo)
+        {
+            var res = new SRes() { ResCode = ResponseStatusCodeEnum.Sucess.GetHashCode(), ResMsg = ResponseStatusCodeEnum.Sucess.GetDescription() };
+            if (boxInfo.Layers.SelectMany(x=>x.Products).Count() != rule.FullCountQty)
+            {
+                res.ResCode = ResponseStatusCodeEnum.Fail.GetHashCode();
+                res.ResMsg = "成箱产品数与满箱产品数不符";
+                return res;
+            }
+            try
+            {
+                _db.BeginTran();
+
+                // 更新箱号状态为预占用
+                UpdateBoxStatus(rule, 3, "箱号预占用");
+
+                // 更新库存预占用标记
+                var presign = rule.Id.ToString();
+                var layerNumber = 0;
+                foreach (var layer in boxInfo.Layers.OrderByDescending(x => x.TotalTorsion))
+                {
+                    var layerProductsIds = layer.Products.Select(x => x.ContGrpId).ToList();
+                    _invNow.UpdateSetColumnsTrue(p => new BillInvnow
+                    {
+                        PreStock = presign,
+                        EditTime = DateTime.Now,
+                        PackingLayer = layerNumber,
+                        Memo = $"箱号预占用:{rule.PBoxCode}"
+                    }, p => layerProductsIds.Contains(p.ContGrpId));
+                    layerNumber++;
+                }
+
+                var containerIds = boxInfo.TotalProduct.Select(x => x.ContGrpId).ToList();
+                _db.CommitTran();
+                _logger.LogInformation($"{rule.PBoxCode}预占:{JsonConvert.SerializeObject(containerIds)}");
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError($"箱号预占用异常: {ex}");
+                _db.RollbackTran();
+                res.ResCode = ResponseStatusCodeEnum.Fail.GetHashCode();
+                res.ResMsg = ex.Message;
+            }
+            return res;
+        }
+
+        /// <summary>
+        /// 更新箱号状态
+        /// </summary>
+        /// <param name="rule">箱号</param>
+        /// <param name="status">状态</param>
+        /// <param name="memo">备注</param>
+        public void UpdateBoxStatus(BillPboxrule rule, int status, string memo)
+        {
+            _boxRule.UpdateSetColumnsTrue(p => new BillPboxrule
+            {
+                ZXStateCode = status,
+                EditTime = DateTime.Now,
+                Memo = memo
+            }, p => p.Id == rule.Id);
+        }
+
+        #endregion 更新装箱/库存信息
+
+        #endregion 装箱逻辑
+
+        #region 处理无箱号可用的情况
+
+        /// <summary>
+        ///  处理没有箱号的情况
+        /// </summary>
+        /// <param name="inventory">产品信息</param>
+        /// <returns></returns>
+        public SRes HandleNoBoxRuleError(IEnumerable<LayerPackingProduct> inventory)
+        {
+            string msg = "SPC" + ResponseStatusCodeEnum.NotBoxRule.GetDescription();
+            RedisHelper.Set("Sx:WMSErrorInfo:NoControlOut8",
+                new RedisErrorInfo { Equip = "NoControlOut8", Con = msg, Time = DateTime.Now });
+            _logger.LogInformation("非控制不满足装箱:" + msg);
+
+            var docNos = inventory.GroupBy(v => v.InDocsNo).Select(p => p.Key).Distinct().ToList();
+            SxPackingHelp.WreTimeOutRecord(inventory.Select(p => p.ContGrpBarCode).ToList(), $"没有箱号可以使用,单号:{JsonConvert.SerializeObject(docNos)}", _timeoutrecord, _logger);
+
+            return new SRes
+            {
+                ResCode = ResponseStatusCodeEnum.NotBoxRule.GetHashCode(),
+                ResMsg = msg
+            };
+        }
+
+        /// <summary>
+        /// 处理没有箱号可用的情况 --SKU
+        /// </summary>
+        /// <param name="skuCode">SKU</param>
+        /// <param name="inventory">产品信息</param>
+        /// <returns></returns>
+        public SRes HandleNoBoxRuleForSku(string skuCode, IEnumerable<LayerPackingProduct> inventory)
+        {
+            var skuInventory = inventory.Where(p => p.SkuCode == skuCode);
+            var docNos = inventory.GroupBy(v => v.InDocsNo).Select(p => p.Key).Distinct().ToList();
+            string msg = $"没有箱号可以使用,SKU:{skuCode},单号:{JsonConvert.SerializeObject(docNos)}";
+            RedisHelper.Set("Sx:WMSErrorInfo:NoControlOut9", new RedisErrorInfo { Equip = "NoControlOut9", Con = msg, Time = DateTime.Now });
+            _logger.LogInformation($"非控制不满足装箱:{msg}");
+            SxPackingHelp.WreTimeOutRecord(skuInventory.Select(p => p.ContGrpBarCode).ToList(), $"没有箱号可以使用,SKU:{skuCode},单号:{JsonConvert.SerializeObject(docNos)}", _timeoutrecord, _logger);
+
+            return new SRes { ResCode = ResponseStatusCodeEnum.Fail.GetHashCode(), ResMsg = msg };
+        }
+
+        #endregion 处理无箱号可用的情况
+
+        #region 下发装箱任务及缓存托盘
+
+        #region 处理正在码垛的任务
+
+        /// <summary>
+        /// 处理正在码垛的任务
+        /// </summary>
+        public SRes ProcessActivePalletizingTasks()
+        {
+            var res = new SRes();
+
+            var activePalletList = _palletiz.GetList(p =>
+                p.PalletizState == 0 &&
+                p.BoxRule == "CP" &&
+                p.IsControlpanel == false);
+
+            if (!activePalletList.Any())
+            {
+                res.ResCode = ResponseStatusCodeEnum.Sucess.GetHashCode();
+                res.ResMsg = "没有正在码垛的任务";
+                return res;
+            }
+
+            // 按任务数量排序
+            activePalletList = SortPalletListByTaskCount(activePalletList);
+
+            var taskConfig = GetTaskConfiguration();
+
+            foreach (var pallet in activePalletList)
+            {
+                // 检查机械手任务数量限制
+                if (!CheckRobotTaskLimit(pallet.Robot, taskConfig.MaxTaskCount))
+                {
+                    _logger.LogInformation($"{pallet.Robot}当前机械手任务数量已经超最大值{taskConfig.MaxTaskCount}");
+                    continue;
+                }
+
+                // 检查任务下发顺序
+                if (!CheckTaskSequence(pallet, taskConfig.TaskStatus))
+                {
+                    continue;
+                }
+
+                // 下发码垛任务
+                var taskResult = CreatePalletizingTasks(pallet);
+                if (taskResult.ResCode != ResponseStatusCodeEnum.Sucess.GetHashCode())
+                {
+                    continue;
+                }
+
+                // 更新任务标识
+                UpdateTaskFlag(pallet.Robot, pallet.Equip);
+            }
+
+            res.ResCode = ResponseStatusCodeEnum.Sucess.GetHashCode();
+            res.ResMsg = "正在码垛任务处理完成";
+            return res;
+        }
+
+        /// <summary>
+        /// 按任务数量排序码垛列表
+        /// </summary>
+        /// <param name="palletList">码垛列表</param>
+        /// <returns>排序后的码垛列表</returns>
+        private List<Palletizing> SortPalletListByTaskCount(List<Palletizing> palletList)
+        {
+            if (!palletList.Any())
+            {
+                return palletList;
+            }
+
+            // 获取每个码垛工位的未完成任务数量
+            var palletIds = palletList.Select(p => p.Id).ToList();
+
+            var taskCountQuery = _palletLayerMath
+                .GetList(p => p.Istask == 0 && palletIds.Contains(p.PalletizingId))
+                .GroupBy(p => p.Palletequip);
+
+            if (!taskCountQuery.Any())
+            {
+                return palletList;
+            }
+
+            // 统计每个工位的任务数量
+            var taskCountList = (from taskGroup in taskCountQuery
+                                 select new TunnelCountTemp
+                                 {
+                                     Tunnel = taskGroup.Key,
+                                     Count = taskGroup.Count()
+                                 }).ToList();
+
+            // 按任务数量降序排序码垛列表
+            var sortedList = palletList.OrderByDescending(p =>
+            {
+                var matchedCount = taskCountList.FirstOrDefault(q => q.Tunnel == p.Equip);
+                return matchedCount?.Count ?? 0;
+            }).ToList();
+
+            return sortedList;
+        }
+
+        /// <summary>
+        /// 创建码垛任务
+        /// </summary>
+        private SRes CreatePalletizingTasks(Palletizing pallet)
+        {
+            var res = new SRes();
+
+            // 获取码垛工位配置
+            var equipConfig = GetPalletizingEquipConfig(pallet.Equip);
+            if (equipConfig == null)
+            {
+                SetRedisError("NoControlOut2", $"不存在该{pallet.Equip}的码垛工位");
+                res.ResCode = ResponseStatusCodeEnum.Fail.GetHashCode();
+                res.ResMsg = $"不存在该{pallet.Equip}的码垛工位";
+                return res;
+            }
+
+            // 获取待下发的码垛任务
+            var palletMathList = _palletLayerMath
+                .GetList(p => p.PalletizingId == pallet.Id && p.Istask == 0)
+                .OrderBy(p => p.Depth)
+                .Take(72);
+
+            if (!palletMathList.Any())
+            {
+                SetRedisError("NoControlOut3", $"{pallet.Equip}没有新的码垛任务需要下发");
+                res.ResCode = ResponseStatusCodeEnum.Sucess.GetHashCode();
+                res.ResMsg = "没有新的码垛任务需要下发";
+                return res;
+            }
+
+            // 获取DocID
+            var docId = GenerateDocId(pallet.Id);
+
+            // 批量创建任务
+            var taskResults = new List<SRes>();
+            foreach (var palletMath in palletMathList)
+            {
+                var taskRes = CreateSinglePalletizingTask(palletMath, pallet, equipConfig, docId);
+                if (taskRes.ResCode == ResponseStatusCodeEnum.Sucess.GetHashCode())
+                {
+                    taskResults.Add(taskRes);
+                }
+            }
+
+            res.ResCode = ResponseStatusCodeEnum.Sucess.GetHashCode();
+            res.ResMsg = $"成功创建{taskResults.Count}个码垛任务";
+            return res;
+        }
+
+        /// <summary>
+        /// 创建单个码垛任务
+        /// </summary>
+        private SRes CreateSinglePalletizingTask(
+            PalletLayerMath palletMath,
+            Palletizing pallet,
+            sxSysConfig equipConfig,
+            int docId)
+        {
+            var res = new SRes();
+
+            // 获取库位信息
+            var cell = _wareCell.GetSingle(p =>
+                p.ContGrpBarCode == palletMath.ContBarCode &&
+                p.StateNum == LocationState.LocationState_Full &&
+                p.ContGrpId == palletMath.ContGrpId);
+
+            if (cell == null)
+            {
+                res.ResCode = ResponseStatusCodeEnum.Fail.GetHashCode();
+                res.ResMsg = "库位信息不存在";
+                return res;
+            }
+
+            // 获取库存信息
+            var inventory = _invNow.GetSingle(p =>
+                p.ContGrpBarCode == palletMath.ContBarCode &&
+                p.InvStateCode == InvState.InvEcecState_In.ToString());
+
+            if (inventory == null)
+            {
+                res.ResCode = ResponseStatusCodeEnum.Fail.GetHashCode();
+                res.ResMsg = "库存信息不存在";
+                return res;
+            }
+
+            // 处理二深位移库
+            if (cell.Depth == 2)
+            {
+                var moveRes = SxServiceHelp.MoveTask(cell.Code, _wcsTaskOld, _wareHouse, _invNow, _wareCell, _task, _taskDetail, _db, _logger, _mapper, _lockerPalletizingPackTask, _lockerApplyLoc);
+                if (!string.IsNullOrEmpty(moveRes.Memo1) && moveRes.Memo1 == "1")
+                {
+                    SetRedisError("NoControlOut23", $"{cell.Code}库位下发移库任务失败,请检查一深货位状态");
+                    res.ResCode = ResponseStatusCodeEnum.Fail.GetHashCode();
+                    res.ResMsg = "移库任务失败";
+                    return res;
+                }
+            }
+
+            // 创建码垛任务请求
+            var taskRequest = BuildPalletizingTaskRequest(palletMath, cell, inventory, pallet, docId);
+
+            // 调用任务创建接口
+            var taskResponse = SxServiceHelp.PalletizingPackTask(taskRequest, _wareCell, _invNow, _invFlow, _task, _wcsTaskOld, _taskDetail, _db, _mapper, _lockerPalletizingPackTask);
+
+            if (taskResponse.ResCode == ResponseStatusCodeEnum.Sucess.GetHashCode())
+            {
+                // 更新码垛层任务状态
+                _palletLayerMath.UpdateSetColumnsTrue(
+                    p => new PalletLayerMath() { Istask = 1, Layer = inventory.PackingLayer },
+                    p => p.ContBarCode == inventory.ContGrpBarCode);
+            }
+
+            return taskResponse;
+        }
+
+        /// <summary>
+        /// 获取任务配置
+        /// </summary>
+        private TaskConfiguration GetTaskConfiguration()
+        {
+            return new TaskConfiguration
+            {
+                TaskStatus = (TaskStatus)int.Parse(_sysconfing.GetSingle(p => p.Code == "PreNextTaskStatus").SContent),
+                MaxTaskCount = int.Parse(_sysconfing.GetSingle(p => p.Code == "PreNextTaskCount").SContent)
+            };
+        }
+
+        /// <summary>
+        /// 获取码垛工位配置
+        /// </summary>
+        /// <param name="equipCode">工位编码</param>
+        /// <returns>工位配置信息</returns>
+        private sxSysConfig GetPalletizingEquipConfig(string equipCode)
+        {
+            try
+            {
+                // 从视图中查询码垛工位配置
+                var config = _sysconfing.Context.Ado
+                    .SqlQuery<sxSysConfig>($"select * from WMS_ZT_01SX.dbo.V_PalletizingPack where code = '{equipCode}'")
+                    .FirstOrDefault();
+
+                if (config == null)
+                {
+                    _logger.LogWarning($"未找到工位 {equipCode} 的配置信息");
+                }
+
+                return config;
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, $"获取工位 {equipCode} 配置信息时发生异常");
+                return null;
+            }
+        }
+
+        /// <summary>
+        /// 检查任务下发顺序
+        /// </summary>
+        private bool CheckTaskSequence(
+            Palletizing currentPallet,
+            TaskStatus taskStatus)
+        {
+            // 找到当前机械手最早的码垛信息
+            var earliestPallet = _palletiz
+                .GetList(p => p.PalletizState != 1 && p.Robot == currentPallet.Robot)
+                .OrderBy(p => p.Id)
+                .FirstOrDefault();
+
+            if (earliestPallet == null || currentPallet.Id == earliestPallet.Id)
+            {
+                return true;
+            }
+
+            // 检查是否是第3箱
+            var result = CheckThirdBoxRestriction(currentPallet, earliestPallet, taskStatus);
+
+            return result;
+        }
+
+        /// <summary>
+        /// 检查第3箱限制
+        /// </summary>
+        private bool CheckThirdBoxRestriction(
+            Palletizing currentPallet,
+            Palletizing earliestPallet,
+            TaskStatus taskStatus)
+        {
+            var robotPallets = _palletiz.GetList(p =>
+                p.PalletizState != 1 &&
+                p.Robot == currentPallet.Robot);
+
+            if (robotPallets.Count < 3)
+            {
+                return true;
+            }
+
+            var thirdBoxId = robotPallets.OrderBy(p => p.Id).Take(3).Last().Id;
+
+            // 如果是第3箱
+            if (currentPallet.Id == thirdBoxId)
+            {
+                // 检查最早的箱子任务是否过了卡控点
+                if (HasUnfinishedTasks(earliestPallet.Id, taskStatus))
+                {
+                    _logger.LogInformation($"第3箱卡控同一机械手最早的箱子的码垛任务没有全部过卡控点{taskStatus.GetDescription()}");
+                    return false;
+                }
+
+                // 检查前两箱是否都是同一目标地址
+                var firstTwoBoxes = robotPallets.Where(p => p.Id < thirdBoxId).ToList();
+                if (firstTwoBoxes.Count == 2 && firstTwoBoxes.Select(p => p.Equip).Distinct().Count() == 1)
+                {
+                    _logger.LogInformation("第3箱卡控同一机械手前面两箱都是同一目标地址,则不下第3箱任务");
+                    return false;
+                }
+            }
+            else if (currentPallet.Id > thirdBoxId)
+            {
+                _logger.LogInformation($"同一机械手第3箱之后的任务不允许下发,码垛主表id:{currentPallet.Id}");
+                return false;
+            }
+
+            return true;
+        }
+
+        /// <summary>
+        /// 构建码垛任务请求
+        /// </summary>
+        /// <param name="palletMath">码垛层数学信息</param>
+        /// <param name="cell">库位信息</param>
+        /// <param name="inventory">库存信息</param>
+        /// <param name="pallet">码垛主信息</param>
+        /// <param name="docId">文档ID</param>
+        /// <returns>码垛任务请求对象</returns>
+        private PalletizingPackTaskRequest BuildPalletizingTaskRequest(
+            PalletLayerMath palletMath,
+            BaseWarecell cell,
+            BillInvnow inventory,
+            Palletizing pallet,
+            int docId)
+        {
+            // 确定机械手
+            var robot = SxServiceHelp.DetermineRoot(palletMath.Palletequip);
+
+            // 构建任务请求
+            var request = new PalletizingPackTaskRequest
+            {
+                // 容器条码
+                Code = palletMath.ContBarCode,
+
+                // 库位信息
+                CellCode = cell.Code,
+                Srm = cell.SCRel,
+                Tunnel = cell.Tunnel.ToString(),
+                Floor = cell.Floor,
+
+                // 产品信息
+                Grade = inventory.Grade,
+                Mater = inventory.MatCode,
+                SkuCode = palletMath.SkuCode,
+                ProductMachCode = inventory.ProductMachCode,
+
+                // 码垛信息
+                PalletLayer = inventory.PackingLayer,
+                Equip = pallet.Equip,
+                PalletizingId = pallet.Id,
+                Robot = robot,
+
+                // 货物类型:1-BS80/33, 2-其他
+                GoodsType = inventory.HWTypeCode == "BS80/33" ? 1 : 2,
+
+                // 文档ID
+                DocId = docId
+            };
+
+            return request;
+        }
+
+        #endregion 处理正在码垛的任务
+
+        /// <summary>
+        /// 更新码垛状态为正在码垛
+        /// </summary>
+        private void UpdatePalletStateToActive(long palletId)
+        {
+            try
+            {
+                _palletiz.AsUpdateable()
+                    .SetColumns(p => new Palletizing() { PalletizState = 0 })
+                    .Where(p => p.Id == palletId)
+                    .ExecuteCommand();
+
+                _logger.LogInformation($"码垛任务 {palletId} 状态已更新为正在码垛");
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, $"更新码垛任务 {palletId} 状态时发生异常");
+            }
+        }
+
+        #region 辅助方法
+
+        /// <summary>
+        /// 设置Redis错误信息
+        /// </summary>
+        private void SetRedisError(string equipCode, string message)
+        {
+            RedisHelper.Set($"Sx:WMSErrorInfo:{equipCode}", new RedisErrorInfo
+            {
+                Equip = equipCode,
+                Con = message,
+                Time = DateTime.Now
+            });
+        }
+
+        /// <summary>
+        /// 检查机械手任务数量限制
+        /// </summary>
+        private bool CheckRobotTaskLimit(string robot, int maxCount)
+        {
+            var currentCount = _task.AsQueryable()
+                .With(SqlWith.NoLock)
+                .Where(p => p.Status < TaskStatus.码垛抓取完成 &&
+                           p.Robot == robot &&
+                           p.BusType == "装箱码垛")
+                .Count();
+
+            return currentCount < maxCount;
+        }
+
+        /// <summary>
+        /// 判断是否有未完成的任务
+        /// </summary>
+        private bool HasUnfinishedTasks(long palletizingId, TaskStatus taskStatus)
+        {
+            var hasUnfinishedMath = _palletLayerMath
+                .GetList(p => p.PalletizingId == palletizingId && p.Istask == 0)
+                .Any();
+
+            var hasUnfinishedTask = _task.AsQueryable()
+                .With(SqlWith.NoLock)
+                .Where(p => p.PalletizingId == palletizingId && p.Status < taskStatus)
+                .Any();
+
+            return hasUnfinishedMath || hasUnfinishedTask;
+        }
+
+        /// <summary>
+        /// 更新任务标识
+        /// </summary>
+        private void UpdateTaskFlag(string robot, string equipCode)
+        {
+            var currentFlag = _sysconfing.Context.Ado
+                .SqlQuery<sxSysConfig>("select * from WMS_ZT_01SX.dbo.V_PalletizingPack")
+                .Where(p => p.Robot == robot)
+                .Max(p => p.TaskFlag);
+
+            var newFlag = currentFlag + 1;
+
+            _sysconfing.Context.Ado.ExecuteCommand($"update WMS_ZT_01CP.dbo.sys_config set taskflag={newFlag} where code='{equipCode}'");
+        }
+
+        /// <summary>
+        /// 记录超时记录
+        /// </summary>
+        private void WreTimeOutRecord(List<string> containerBarcodes, string reason)
+        {
+            // 实现超时记录逻辑
+            _logger.LogWarning($"超时记录 - 容器条码: {string.Join(",", containerBarcodes)}, 原因: {reason}");
+        }
+
+        /// <summary>
+        /// 生成文档ID
+        /// </summary>
+        /// <param name="palletizingId">码垛ID</param>
+        /// <returns>文档ID</returns>
+        private int GenerateDocId(long palletizingId)
+        {
+            try
+            {
+                // 获取装箱码垛类型任务的最大DocID
+                var maxDocId = _task.AsQueryable()
+                    .Where(p => p.BusType == "装箱码垛")
+                    .Max(p => (int?)p.DocID) ?? 0;
+
+                // 默认使用最大值+1
+                var currentDocId = maxDocId + 1;
+
+                // 检查当前码垛是否已有任务,如果有则使用相同的DocID
+                var existingDocId = _task.AsQueryable()
+                    .With(SqlWith.NoLock)
+                    .Where(p => p.PalletizingId == palletizingId && p.DocID > 0)
+                    .Max(p => (int?)p.DocID);
+
+                if (existingDocId.HasValue)
+                {
+                    currentDocId = existingDocId.Value;
+                }
+
+                return currentDocId;
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, $"生成文档ID时发生异常,码垛ID:{palletizingId}");
+                throw;
+            }
+        }
+
+        #endregion 辅助方法
+
+        #endregion 下发装箱任务及缓存托盘
+    }
+}

+ 5 - 0
wms.service/Help/LayerPacking/model/LayerPackingBoxInfo.cs

@@ -20,6 +20,11 @@ namespace wms.service.Help.LayerPacking.model
         /// </summary>
         public List<LayerPackingLayerInfo> Layers { get; set; }
 
+        /// <summary>
+        /// 所有产品
+        /// </summary>
+        public List<LayerPackingProduct> TotalProduct => Layers.SelectMany(l => l.Products).ToList();
+
         /// <summary>
         /// 箱总扭转值
         /// </summary>

+ 10 - 0
wms.service/Help/LayerPacking/model/LayerPackingConstraints.cs

@@ -18,6 +18,16 @@ namespace wms.service.Help.LayerPacking.model
         /// </summary>
         public decimal P1 { get; set; }
 
+        /// <summary>
+        /// 激进搜索策略中箱扭转目标值偏差范围放宽比例
+        /// </summary>
+        public decimal BR { get; set; } = 1m;
+
+        /// <summary>
+        /// 激进搜索策略中层扭转目标值偏差范围放宽比例
+        /// </summary>
+        public decimal LR { get; set; } = 1m;
+
         /// <summary>
         /// 箱总扭转目标值
         /// </summary>

+ 80 - 41
wms.service/Help/LayerPacking/model/LayerPackingResult.cs

@@ -1,49 +1,88 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
+using System;
 
-namespace wms.service.Help.LayerPacking.model
+using wms.service.Help.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 List<PackingFailureReason> FailureReasons { get; set; }
+
+    /// <summary>
+    /// 是否装箱成功
+    /// </summary>
+    public bool IsSuccess => FailureReasons == null || FailureReasons.Count == 0;
+
+    /// <summary>
+    /// 构造函数
+    /// </summary>
+    public LayerPackingResult()
+    {
+        Boxes = new List<LayerPackingBoxInfo>();
+        FailureReasons = new List<PackingFailureReason>();
+    }
+
+    /// <summary>
+    /// 添加失败原因
     /// </summary>
-    public class LayerPackingResult
+    /// <param name="reason">失败原因</param>
+    public void AddFailureReason(PackingFailureReason reason)
     {
-        /// <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()
+        if (reason != null)
         {
-            Boxes = new List<LayerPackingBoxInfo>();
+            FailureReasons.Add(reason);
         }
     }
-}
+
+    /// <summary>
+    /// 添加失败原因
+    /// </summary>
+    /// <param name="type">失败类型</param>
+    /// <param name="message">失败消息</param>
+    /// <param name="details">详细信息</param>
+    public void AddFailureReason(PackingFailureType type, string message, string details = null)
+    {
+        FailureReasons.Add(new PackingFailureReason
+        {
+            Type = type,
+            Message = message,
+            Details = details,
+            Timestamp = DateTime.Now
+        });
+    }
+}

+ 27 - 0
wms.service/Help/LayerPacking/model/MesVerificationResult.cs

@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace wms.service.Help.LayerPacking.model
+{
+    /// <summary>
+    /// MES验证结果
+    /// </summary>
+    public class MesVerificationResult
+    {
+        /// <summary>
+        /// 是否成功
+        /// </summary>
+        public bool IsSuccess { get; set; }
+
+        /// <summary>
+        /// 错误信息
+        /// </summary>
+        public string Message { get; set; }
+
+        /// <summary>
+        /// 原始响应
+        /// </summary>
+        public string RawResponse { get; set; }
+    }
+}

+ 87 - 0
wms.service/Help/LayerPacking/model/PackingFailureReason.cs

@@ -0,0 +1,87 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace wms.service.Help.LayerPacking.model
+{
+    /// <summary>
+    /// 装箱失败原因
+    /// </summary>
+    public class PackingFailureReason
+    {
+        /// <summary>
+        /// 失败类型
+        /// </summary>
+        public PackingFailureType Type { get; set; }
+
+        /// <summary>
+        /// 失败消息
+        /// </summary>
+        public string Message { get; set; }
+
+        /// <summary>
+        /// 详细信息
+        /// </summary>
+        public string Details { get; set; }
+
+        /// <summary>
+        /// 发生时间
+        /// </summary>
+        public DateTime Timestamp { get; set; }
+
+        /// <summary>
+        /// 相关数据(如产品数量、约束值等)
+        /// </summary>
+        public Dictionary<string, object> RelatedData { get; set; }
+
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        public PackingFailureReason()
+        {
+            RelatedData = new Dictionary<string, object>();
+            Timestamp = DateTime.Now;
+        }
+    }
+
+    /// <summary>
+    /// 装箱失败类型
+    /// </summary>
+    public enum PackingFailureType
+    {
+        /// <summary>
+        /// 产品数量不足
+        /// </summary>
+        InsufficientProducts,
+
+        /// <summary>
+        /// 扭转值约束违反
+        /// </summary>
+        TorsionConstraintViolation,
+
+        /// <summary>
+        /// 焊点约束违反
+        /// </summary>
+        SolderConstraintViolation,
+
+        /// <summary>
+        /// 极值成对约束违反
+        /// </summary>
+        ExtremePairConstraintViolation,
+
+        /// <summary>
+        /// 层标准差约束违反
+        /// </summary>
+        LayerStandardDeviationViolation,
+
+        /// <summary>
+        /// 无法找到有效解
+        /// </summary>
+        NoValidSolutionFound,
+
+        /// <summary>
+        /// 其他错误
+        /// </summary>
+        Other
+    }
+}

+ 23 - 0
wms.service/Help/LayerPacking/model/ReworkProcessResult.cs

@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using wms.dto.response.sx;
+
+namespace wms.service.Help.LayerPacking.model
+{
+    /// <summary>
+    /// 重绕处理结果
+    /// </summary>
+    public class ReworkProcessResult
+    {
+        /// <summary>
+        /// 重绕库存
+        /// </summary>
+        public List<LayerPackingProduct> ReworkStock { get; set; }
+
+        /// <summary>
+        /// 正常库存
+        /// </summary>
+        public IEnumerable<LayerPackingProduct> NormalStock { get; set; }
+    }
+}

+ 28 - 0
wms.service/Help/LayerPacking/model/StockAllocationResult.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using wms.dto.response.sx;
+
+namespace wms.service.Help.LayerPacking.model
+{
+    /// <summary>
+    /// 仓库库存分配结果
+    /// </summary>
+    public class StockAllocationResult
+    {
+        /// <summary>
+        /// 是否成功
+        /// </summary>
+        public bool IsSuccess { get; set; }
+
+        /// <summary>
+        /// 错误信息
+        /// </summary>
+        public string ErrorMessage { get; set; }
+
+        /// <summary>
+        /// 箱
+        /// </summary>
+        public LayerPackingBoxInfo BoxInfo { get; set; }
+    }
+}

+ 23 - 0
wms.service/Help/LayerPacking/model/TaskConfiguration.cs

@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using wms.dto;
+
+namespace wms.service.Help.LayerPacking.model
+{
+    /// <summary>
+    /// 任务配置类
+    /// </summary>
+    public class TaskConfiguration
+    {
+        /// <summary>
+        /// 任务状态
+        /// </summary>
+        public TaskStatus TaskStatus { get; set; }
+
+        /// <summary>
+        /// 最大任务数
+        /// </summary>
+        public int MaxTaskCount { get; set; }
+    }
+}

+ 16 - 0
wms.service/Help/LayerPacking/model/WeldingProcessResult.cs

@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using wms.dto.response.sx;
+
+namespace wms.service.Help.LayerPacking.model
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    public class WeldingProcessResult
+    {
+        public List<LayerPackingProduct> WeldingStock { get; set; }
+        public IEnumerable<LayerPackingProduct> NormalStock { get; set; }
+    }
+}

+ 21 - 0
wms.service/Help/Model/ValidationResult.cs

@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace wms.service.Help.Model
+{
+    /// <summary>
+    /// 验证结果
+    /// </summary>
+    public class ValidationResult
+    {
+        /// <summary>
+        /// 是否成功
+        /// </summary>
+        public bool IsSuccess { get; set; }
+        /// <summary>
+        /// 信息
+        /// </summary>
+        public string Message { get; set; }
+    }
+}

+ 1 - 3
wms.service/Help/Packing/SxSpcPackingHelp.cs

@@ -5,11 +5,9 @@ using System.Text;
 namespace wms.service.Help.Packing
 {
     /// <summary>
-    /// SPC装箱逻辑
+    /// SPC装箱帮助类
     /// </summary>
     public static class SxSpcPackingHelp
     {
-        #region 验证预占箱数
-        #endregion
     }
 }

+ 84 - 5
wms.service/Help/SxPackingHelp.cs

@@ -1,14 +1,93 @@
-using System;
+using Microsoft.Extensions.Logging;
+using System;
 using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.Text;
+using System.Linq;
+using wms.service.Help.Model;
+using wms.service.Service;
+using wms.sqlsugar;
+using wms.sqlsugar.model.sx;
 
 namespace wms.service.Help
 {
     /// <summary>
-    /// 封装箱扩展
+    ///  装箱帮助类
     /// </summary>
-    public static class SxSpcPackingExtension
+    public static class SxPackingHelp
     {
+        /// <summary>
+        /// 验证预占箱数
+        /// </summary>
+        /// <param name="_sysconfing">配置表</param>
+        /// <param name="_boxrule">箱号表</param>
+        /// <returns></returns>
+        public static ValidationResult ValidatePreBoxCount(Repository<sxSysConfig> _sysconfing, Repository<BillPboxrule> _boxrule)
+        {
+            var result = new ValidationResult() { IsSuccess = true };
+
+            var preBoxConfig = _sysconfing.GetFirst(p => p.Code == "CP_PreBoxNum");
+            if (preBoxConfig == null)
+            {
+                result.IsSuccess = false;
+                result.Message = "预占箱数配置不存在";
+                return result;
+            }
+
+            int maxPreBoxCount = int.Parse(preBoxConfig.SContent);
+            int currentPreBoxCount = _boxrule.Count(p => p.ZXStateCode > 2 && p.PackRule == "CP");
+
+            if (currentPreBoxCount > maxPreBoxCount)
+            {
+                result.IsSuccess = false;
+                result.Message = $"预占箱数已经大于设置值 (当前:{currentPreBoxCount}, 最大:{maxPreBoxCount})";
+            }
+
+            return result;
+        }
+
+        /// <summary>
+        /// 根据装箱规则获取箱号
+        /// </summary>
+        /// <param name="packRule">装箱规则</param>
+        /// <param name="_boxRule">箱号表</param>
+        /// <returns></returns>
+        public static List<BillPboxrule> GetAvailableBoxRules(string packRule, Repository<BillPboxrule> _boxRule)
+        {
+            return _boxRule.GetList(p =>
+                p.ZXStateCode == 0 &&
+                p.PackRule == packRule &&
+                !string.IsNullOrEmpty(p.Wind) &&
+                (p.FullCountQty == 72 || p.FullCountQty == 36));
+        }
+
+        /// <summary>
+        /// 记录装箱超时信息
+        /// </summary>
+        /// <param name="contbars">条码</param>
+        /// <param name="msg">信息</param>
+        /// <param name="_timeoutrecord">超时记录表</param>
+        /// <param name="_logger">日志</param>
+        public static void WreTimeOutRecord(List<string> contbars, string msg, RepositoryTask<BillTimeOutRecord> _timeoutrecord, ILogger<SXService> _logger)
+        {
+            try
+            {
+                if (contbars.Any())
+                {
+                    //存在的数据更新,不存在的新增
+                    if (msg.Length > 4900)
+                    {
+                        msg = msg.Substring(0, 4900);
+                    }
+                    _timeoutrecord.AsUpdateable().SetColumns(p => new BillTimeOutRecord() { Reason = msg, EditTime = DateTime.Now }).Where(p => contbars.Contains(p.ContGrpBarCode)).SplitTable(p => p.Take(2)).ExecuteCommand();
+                    var existlist = _timeoutrecord.AsQueryable().SplitTable(p => p.Take(2)).Select(p => p.ContGrpBarCode).Distinct().ToList();
+                    var noexistlist = contbars.Where(p => !existlist.Contains(p)).Distinct().ToList();
+                    List<BillTimeOutRecord> listrec = noexistlist.Select(p => new BillTimeOutRecord() { ContGrpBarCode = p, Reason = msg, AddTime = DateTime.Now, EditTime = DateTime.Now }).ToList();
+                    if (listrec.Any()) _timeoutrecord.Context.Insertable(listrec).SplitTable().ExecuteCommand();
+                }
+            }
+            catch (Exception ex)
+            {
+                _logger.LogInformation("记录超时信息异常" + ex.ToString());
+            }
+        }
     }
 }

+ 135 - 0
wms.service/Help/SxServiceHelp.cs

@@ -15,6 +15,7 @@ using wms.dto.response.sx;
 using wms.service.Service;
 using wms.sqlsugar;
 using wms.sqlsugar.model.sx;
+using wms.util.Check;
 using wms.util.Ext;
 
 namespace wms.service.Help
@@ -1010,5 +1011,139 @@ namespace wms.service.Help
         }
 
         #endregion 分配货位
+
+        /// <summary>
+        ///  码垛工位与对应的机器人的关系
+        /// </summary>
+        /// <param name="palletequip"></param>
+        /// <returns></returns>
+        public static string DetermineRoot(string palletequip)
+        {
+            return palletequip switch
+            {
+                "8090" => "Robot1",
+                "8092" => "Robot1",
+                "8096" => "Robot2",
+                "8098" => "Robot2",
+                "8307" => "Robot3",
+                "5434" => "Robot10",
+                _ => string.Empty  // 默认值或其他处理
+            };
+        }
+
+        /// <summary>
+        /// 创建码垛任务
+        /// </summary>
+        /// <param name="request"></param>
+        /// <param name="_wareCell"></param>
+        /// <param name="_invNow"></param>
+        /// <param name="_invFlow"></param>
+        /// <param name="_task"></param>
+        /// <param name="_wcsTaskOld"></param>
+        /// <param name="_taskDetail"></param>
+        /// <param name="_db"></param>
+        /// <param name="_mapper"></param>
+        /// <param name="lockerPalletizingPackTask"></param>
+        /// <returns></returns>
+        public static SRes PalletizingPackTask(
+            PalletizingPackTaskRequest request,
+            Repository<BaseWarecell> _wareCell,
+            Repository<BillInvnow> _invNow,
+            Repository<BillInvflow> _invFlow,
+            RepositoryTask<WCS_TaskInfo> _task,
+            RepositoryTask<WCS_TaskOld> _wcsTaskOld,
+            RepositoryTask<WCS_TaskDtl> _taskDetail,
+            ITenant _db,
+            IMapper _mapper,
+            object lockerPalletizingPackTask)
+        {
+            lock (lockerPalletizingPackTask)
+            {
+                var res = new SRes();
+                try
+                {
+                    var wcs = new WCS_TaskInfo()
+                    {
+                        Status = TaskStatus.NewBuild,
+                        Type = TaskType.OutDepot,
+                        Priority = 0,
+                        Device = request.Srm,
+                        SrmStation = request.Srm,
+                        AddrFrom = request.CellCode,
+                        BarCode = request.Code,
+                        AddWho = "wms",
+                        EditWho = "wms",
+                        WarehouseCode = "sxhouse",
+                        AddrTo = request.Equip,
+                        FullQty = 1,
+                        AgvTaskID = request.PalletLayer,  //层配层数
+                        Tunnel = request.Tunnel,
+                        MaterialCode = request.Mater,
+                        MatCode = request.Mater,
+                        OutType = OutTypeEnum.自动出库任务,
+                        BusType = SxTaskBusType.TaskBusType_SX_StackStockOut.GetDescription(),
+                        Floor = request.Floor,
+                        Grade = request.Grade,
+                        WorkBench = request.ProductMachCode,
+                        PalletizingId = request.PalletizingId,
+                        Robot = request.Robot,
+                        GoodsType = request.GoodsType,
+                        ManualRemarks = request.Memo,
+                        DocID = request.DocId
+                    };
+                    _db.BeginTran();
+                    _invNow.UpdateSetColumnsTrue(p => new BillInvnow()
+                    {
+                        InvStateCode = InvState.InvEcecState_OutGoing.ToString(),
+                        EditTime = DateTime.Now
+                    }, p => p.ContGrpBarCode == request.Code);
+                    _wareCell.UpdateSetColumnsTrue(p => new BaseWarecell()
+                    {
+                        StateNum = LocationState.LocationState_StockOut,
+                        EditTime = DateTime.Now
+                    }, p => p.Code == request.CellCode);
+                    var stock = _invNow.GetSingle(p => p.ContGrpBarCode == request.Code);
+
+                    var flow = _mapper.Map<BillInvflow>(stock);
+                    flow.Id = GetId();
+                    flow.AddTime = DateTime.Now;
+                    flow.EditTime = DateTime.Now;
+                    flow.InvStateCode = InvState.InvEcecState_OutGoing.ToString();
+                    flow.Memo = "装箱码垛出库";
+                    _invFlow.Insert(flow);
+                    var wcstask = _task.InsertReturnEntity(wcs);
+                    _wcsTaskOld.InsertableSplitTable(_mapper.Map<WCS_TaskOld>(wcstask));
+                    var wcsdtl = new WCS_TaskDtl()
+                    {
+                        ID = Guid.NewGuid(),
+                        CurPoint = request.Code,
+                        AddTime = DateTime.Now,
+                        AddWho = "wms",
+                        Enabled = true,
+                        ParentTaskCode = wcstask.ID,
+                        Desc = wcstask.BusType
+                    };
+                    _taskDetail.InsertableSplitTable(wcsdtl);
+                    _db.CommitTran();
+                }
+                catch (Exception ex)
+                {
+                    _db.RollbackTran();
+                    res.ResCode = ResponseStatusCodeEnum.Fail.GetHashCode();
+                    res.ResMsg = ResponseStatusCodeEnum.DataSaveErr.GetDescription();
+                    return res;
+                }
+                return res;
+            }
+        }
+
+        /// <summary>
+        /// 获取ID
+        /// </summary>
+        /// <returns></returns>
+        public static long GetId()
+        {
+            return IdFactory.NewId();
+        }
     }
 }

+ 17 - 5
wms.service/IService/ISXService.cs

@@ -1,10 +1,8 @@
-using System;
-using System.Collections.Generic;
-using System.Linq.Expressions;
-using System.Text;
+using System.Collections.Generic;
+using System.Threading.Tasks;
 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;
@@ -94,6 +92,20 @@ namespace wms.service.IService
 
         SRes PalletizingLayerPackStockOut(PalletizingPackStockOutRequest request);
 
+        /// <summary>
+        /// 预锁库存
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        SRes PreLockInventoryForLayerPacking(PalletizingPackStockOutRequest request);
+
+        /// <summary>
+        /// 处理层包装出库并缓存托盘
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        SRes ProcessLayerPackingOutboundAndCachePallets(PalletizingPackStockOutRequest request);
+
         SRes TorsChkValue(TorsChkValueRequest request);
 
         SRes Manaler(DetailCodeStrdtoRequest request);

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


+ 25 - 8
wms.sqlsugar/model/sx/BillInvnow.cs

@@ -467,59 +467,76 @@ namespace wms.sqlsugar.model.sx
         /// </summary>
         [SugarColumn(ColumnDataType = "nvarchar", Length = 100, IsNullable = true)]
         public string SkuCode { get; set; }
+
         /// <summary>
         ///  扭转检测弓高
         /// </summary>
         [SugarColumn(ColumnDataType = "decimal", Length = 18, DecimalDigits = 6, IsNullable = true)]
         public decimal? TorsChkChord { get; set; }
+
         /// <summary>
         ///  扭转检测平直度
         /// </summary>
         [SugarColumn(ColumnDataType = "decimal", Length = 18, DecimalDigits = 6, IsNullable = true)]
         public decimal? TorsChkFlatness { get; set; }
+
         /// <summary>
         ///  扭转时长
         /// </summary>
         [SugarColumn(ColumnDataType = "decimal", Length = 18, DecimalDigits = 2, IsNullable = true)]
         public decimal HoldTime { get; set; }
-        /// <summary> 
+
+        /// <summary>
         /// 预锁库存
-        /// </summary> 
+        /// </summary>
         [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = true)]
         public string PreStock { get; set; }
-        /// <summary> 
+
+        /// <summary>
+        /// 装箱层数
+        /// </summary>
+        [SugarColumn(ColumnDataType = "int", IsNullable = true)]
+        public int PackingLayer { get; set; }
+
+        /// <summary>
         /// 扭转机台工位
-        /// </summary> 
+        /// </summary>
         [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = true)]
         public string TorsChkStation { get; set; }
+
         /// <summary>
         ///  是否控制盘
         /// </summary>
         [SugarColumn(ColumnDataType = "bit", IsNullable = false)]
         public bool IsControlpanel { get; set; }
+
         /// <summary>
         ///  弓高是否合格
         /// </summary>
         [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = true)]
         public string TorsChkChordIsGood { get; set; }
+
         /// <summary>
         ///  平直度是否合格
         /// </summary>
         [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = true)]
         public string TorsChkFlatnessIsGood { get; set; }
+
         /// <summary>
         ///  扭转值是否合格
         /// </summary>
         [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = true)]
         public string TorsChkValueIsGood { get; set; }
+
         /// <summary>
         /// 焊点数量
         /// </summary>
         [SugarColumn(ColumnDataType = "decimal", Length = 18, DecimalDigits = 2, IsNullable = true)]
         public decimal SolderCount { get; set; }
-        /// <summary> 
+
+        /// <summary>
         /// 绕向
-        /// </summary> 
+        /// </summary>
         [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = false)]
         public string Wind { get; set; }
 
@@ -528,11 +545,11 @@ namespace wms.sqlsugar.model.sx
         /// </summary>
         [SugarColumn(ColumnDataType = "int", IsNullable = true)]
         public int Ovced { get; set; }
+
         /// <summary>
         ///  是否重投盘
         /// </summary>
         [SugarColumn(ColumnDataType = "bit", IsNullable = false)]
         public bool IsMulti { get; set; }
-
     }
-}
+}

+ 36 - 0
wms.sqlsugar/model/sx/BillPboxrule.cs

@@ -200,5 +200,41 @@ namespace wms.sqlsugar.model.sx
         /// </summary>
         [SugarColumn(ColumnDataType = "decimal", Length = 18, IsNullable = true)]
         public decimal BaoCaiWeight { get; set; }
+
+        /// <summary>
+        /// L/R类别
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = true)]
+        public string LR_TYPE_L { get; set; }
+
+        /// <summary>
+        /// 工字轮主色 浅蓝/灰色/橙色
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = true)]
+        public string SPOOL_MAJOR_COLOR_L { get; set; }
+
+        /// <summary>
+        /// 收线自动扭转检测(俯视)  顺时针/逆时针
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = true)]
+        public string TAKEUP_SIDE_L { get; set; }
+
+        /// <summary>
+        /// L/R类别
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = true)]
+        public string LR_TYPE_R { get; set; }
+
+        /// <summary>
+        /// 工字轮主色 浅蓝/灰色/橙色
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = true)]
+        public string SPOOL_MAJOR_COLOR_R { get; set; }
+
+        /// <summary>
+        /// 收线自动扭转检测(俯视)  顺时针/逆时针
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = true)]
+        public string TAKEUP_SIDE_R { get; set; }
     }
 }

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

@@ -192,5 +192,41 @@ namespace wms.sqlsugar.model.sx
         /// </summary>
         [SugarColumn(ColumnDataType = "decimal", Length = 18, IsNullable = false)]
         public decimal PackWeight { get; set; }
+
+        /// <summary>
+        /// L/R类别
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = true)]
+        public string LR_TYPE_L { get; set; }
+
+        /// <summary>
+        /// 工字轮主色 浅蓝/灰色/橙色
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = true)]
+        public string SPOOL_MAJOR_COLOR_L { get; set; }
+
+        /// <summary>
+        /// 收线自动扭转检测(俯视)  顺时针/逆时针
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = true)]
+        public string TAKEUP_SIDE_L { get; set; }
+
+        /// <summary>
+        /// L/R类别
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = true)]
+        public string LR_TYPE_R { get; set; }
+
+        /// <summary>
+        /// 工字轮主色 浅蓝/灰色/橙色
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = true)]
+        public string SPOOL_MAJOR_COLOR_R { get; set; }
+
+        /// <summary>
+        /// 收线自动扭转检测(俯视)  顺时针/逆时针
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = true)]
+        public string TAKEUP_SIDE_R { get; set; }
     }
 }

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