杨前锦
2025-06-13 b7308bba3d7ffad271ce7fc7a93c8c45d76be87d
HH.WCS.Mobox3/HH.WCS.Mobox3.YNJT_PT/wms/WMSHelper.cs
@@ -384,121 +384,309 @@
            return greenTireInformation;
        }
        /// <summary>
        /*/// <summary>
        /// 查询入库终点货位
        /// 入库策略:
        /// 巷道内同规格数量 小于 10(暂定),按物料均衡计算,如果所有的巷道内同规格数量都大于10,则按照巷道均衡计算
        /// </summary>
        /// <param name="locNum">1.单货位 2.双货位</param>
        /// <returns></returns>
        public static EndLocGroup getInStockEndLoc(int locNum)
        public static EndLocGroup getInStockEndLoc(int locNum , string itemCode)
        {
            var db = new SqlHelper<object>().GetInstance();
            EndLocGroup endLocGroup = new EndLocGroup();
            List<Location> locations = new List<Location>();
            // 1.按容积率从大到小,对巷道进行排序
            var roadwayOrderList = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y")
                .GroupBy(a => a.N_ROADWAY)
                .Select(a => new { roadway = a.N_ROADWAY ,  num = SqlFunc.AggregateCount(a.S_CODE)})
                .OrderByDescending(a => a.num)
            List<int> roadwayList = new List<int>();
            // 1.按巷道进行分组,查询每个巷道内同规格物料的数量,并从小到大排序
            var roadwayItemNumOrderGroup = db.Queryable<Location>()
                .LeftJoin<LocCntrRel>((a,b) => a.S_CODE == b.S_LOC_CODE)
                .LeftJoin<CntrItemRel>((a,b,c) => b.S_CNTR_CODE == c.S_CNTR_CODE)
                .Where((a, b, c) => a.S_AREA_CODE == Settings.stockArea && a.N_CURRENT_NUM == 1 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y" && c.S_ITEM_CODE == itemCode)
                .GroupBy((a, b, c) => a.N_ROADWAY)
                .Select((a, b, c) => new { roadway = a.N_ROADWAY, num = SqlFunc.AggregateCount(a.S_CODE) })
                .OrderBy(a => a.num)
                .ToList();
            // 查询单货位
            foreach (var order in roadwayOrderList)
            roadwayList = roadwayItemNumOrderGroup.Where(a => a.num < 10).OrderBy(a => a.num).Select(a => a.roadway).ToList();
            // 2.按巷道进行分组,查询每个巷道空货位数量,并从大到小排序
            if (roadwayList.Count == 0)
            {
                if (locNum == 1)
                var roadwayEmptyNumOrderGroup = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y")
                    .GroupBy(a => a.N_ROADWAY)
                    .Select(a => new { roadway = a.N_ROADWAY, num = SqlFunc.AggregateCount(a.S_CODE) })
                    .OrderByDescending(a => a.num)
                    .ToList();
                roadwayList = roadwayEmptyNumOrderGroup.Select(a => a.roadway).ToList();
            }
            if (roadwayList.Count > 0)
            {
                // 查询单货位
                foreach (var roadway in roadwayList)
                {
                    /*// 查询所有相同物料货位
                    var sameItemLocList = db.Queryable<Location>()
                       .LeftJoin<LocCntrRel>((a, b) => a.S_CODE == b.S_LOC_CODE)
                       .LeftJoin<CntrItemRel>((a, b, c) => b.S_CNTR_CODE == c.S_CNTR_CODE)
                       .Where((a, b, c) => a.S_AREA_CODE == Settings.stockArea
                                       && a.N_ROADWAY == order.roadway
                                       && a.N_CURRENT_NUM == 1
                                       && a.N_LOCK_STATE == 0
                                       && a.C_ENABLE == "Y"
                                       && b.S_CNTR_CODE != null
                                       && c.S_ITEM_CODE == itemCode
                                       )
                       .OrderBy((a, b, c) => new { a.N_LAYER, a.N_COL })
                       .ToList();
                    // 查询相同物料的左右是否有空货位
                    foreach (var loc in sameItemLocList)
                    if (locNum == 1)
                    {
                        var leftLoc = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_ROADWAY == order.roadway && a.N_ROW == loc.N_ROW && a.N_COL == loc.N_COL - 1 && a.N_LAYER == loc.N_LAYER && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y").First();
                        if (leftLoc != null)
                        // 查询空货位
                        var emptyLoc = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_ROADWAY == roadway && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y").OrderBy(a => new { a.N_LAYER, a.N_COL }).First();
                        if (emptyLoc != null)
                        {
                            locations.Add(leftLoc);
                            break;
                            locations.Add(emptyLoc);
                            endLocGroup.endLocList = locations;
                            return endLocGroup;
                        }
                    }
                        var rightLoc = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_ROADWAY == order.roadway && a.N_ROW == loc.N_ROW && a.N_COL == loc.N_COL + 1 && a.N_LAYER == loc.N_LAYER && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y").First();
                        if (rightLoc != null)
                        {
                            locations.Add(leftLoc);
                            break;
                        }
                    }*/
                    // 查询空货位
                    var emptyLoc = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_ROADWAY == order.roadway && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y").OrderBy(a => new { a.N_LAYER, a.N_COL }).First();
                    if (emptyLoc != null)
                    if (locNum == 2)
                    {
                        locations.Add(emptyLoc);
                        endLocGroup.endLocList = locations;
                        return endLocGroup;
                        var emptyLocList = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_ROADWAY == roadway && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y").OrderBy(a => new { a.N_LAYER, a.N_COL }).ToList();
                        if (emptyLocList.Count > 0)
                        {
                            // 优先查询相邻双拖空货位
                            if (locations.Count == 0)
                            {
                                foreach (var loc in emptyLocList)
                                {
                                    // 查询相邻左侧是否有空货位
                                    var leftLoc = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_ROADWAY == roadway && a.N_ROW == loc.N_ROW && a.N_COL == loc.N_COL - 1 && a.N_LAYER == loc.N_LAYER && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y").First();
                                    if (leftLoc != null)
                                    {
                                        locations.Add(loc);
                                        locations.Add(leftLoc);
                                        break;
                                    }
                                    // 查询相邻右侧是否有空货位
                                    var rightLoc = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_ROADWAY == roadway && a.N_ROW == loc.N_ROW && a.N_COL == loc.N_COL + 1 && a.N_LAYER == loc.N_LAYER && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y").First();
                                    if (rightLoc != null)
                                    {
                                        locations.Add(loc);
                                        locations.Add(rightLoc);
                                        break;
                                    }
                                }
                            }
                            if (locations.Count < locNum)
                            {
                                // 查询不相邻的单拖货位
                                foreach (var loc in emptyLocList)
                                {
                                    locations.Add(loc);
                                    if (locations.Count == locNum)
                                    {
                                        break;
                                    }
                                }
                            }
                            if (locations.Count == locNum)
                            {
                                endLocGroup.endLocList = locations;
                                // 校验货位是否是同巷道,是则生成任务组号
                                var groupNum = locations.GroupBy(a => a.N_ROADWAY).Count();
                                if (groupNum == 1)
                                {
                                    endLocGroup.groupNo = WMSHelper.GenerateTaskGroupNo();
                                }
                                return endLocGroup;
                            }
                        }
                    }
                }
                if (locNum == 2)
                {
                    var emptyLocList = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_ROADWAY == order.roadway && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y").OrderBy(a => new { a.N_LAYER, a.N_COL }).ToList();
                    if (emptyLocList.Count > 0)
                    {
                        // 查询双拖空货位
                        if (locations.Count == 0)
                        {
                            foreach (var loc in emptyLocList)
                            {
                                var leftLoc = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_ROADWAY == order.roadway && a.N_ROW == loc.N_ROW && a.N_COL == loc.N_COL - 1 && a.N_LAYER == loc.N_LAYER && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y").First();
                                if (leftLoc != null)
                                {
                                    locations.Add(loc);
                                    locations.Add(leftLoc);
                                    break;
                                }
                                var rightLoc = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_ROADWAY == order.roadway && a.N_ROW == loc.N_ROW && a.N_COL == loc.N_COL + 1 && a.N_LAYER == loc.N_LAYER && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y").First();
                                if (rightLoc != null)
                                {
                                    locations.Add(loc);
                                    locations.Add(leftLoc);
                                    break;
                                }
                            }
                            if (locations.Count == locNum)
                            {
                                endLocGroup.groupNo = WMSHelper.GenerateTaskGroupNo();
                                endLocGroup.endLocList = locations;
                                return endLocGroup;
                            }
                        }
                        // 查询单拖货位
                        foreach (var loc in emptyLocList)
                        {
                            locations.Add(loc);
                            if (locations.Count == locNum)
                            {
                                endLocGroup.endLocList = locations;
                                return endLocGroup;
                            }
                        }
                    }
                }
            }
            return endLocGroup;
        }*/
        /// <summary>
        /// 查询入库终点货位
        /// 入库策略:
        /// 巷道内同规格数量 小于 10(暂定),按物料均衡计算,如果所有的巷道内同规格数量都大于10,则按照巷道均衡计算
        /// </summary>
        /// <param name="locNum"></param>
        /// <param name="itemCode"></param>
        /// <returns></returns>
        public static EndLocGroup getInStockEndLoc(int locNum, string itemCode)
        {
            var db = new SqlHelper<object>().GetInstance();
            EndLocGroup endLocGroup = new EndLocGroup { endLocList = new List<Location>() };
            // 1. 获取候选巷道列表(物料均衡或巷道均衡)
            var candidateRoadways = GetCandidateRoadways(db, itemCode);
            if (candidateRoadways.Count == 0) return endLocGroup;
            // 2. 处理单货位入库
            if (locNum == 1)
            {
                return FindSingleLocation(db, candidateRoadways);
            }
            // 3. 处理双货位入库
            return FindDoubleLocations(db, candidateRoadways);
        }
        /// <summary>
        /// 获取候选巷道列表(按策略排序)
        /// </summary>
        private static List<int> GetCandidateRoadways(SqlSugarClient db, string itemCode)
        {
            // 策略1:巷道内同规格物料 < 10 的巷道(按数量升序)
            var materialRoadways = db.Queryable<Location>()
                .LeftJoin<LocCntrRel>((a, b) => a.S_CODE == b.S_LOC_CODE)
                .LeftJoin<CntrItemRel>((a, b, c) => b.S_CNTR_CODE == c.S_CNTR_CODE)
                .Where((a, b, c) =>
                    a.S_AREA_CODE == Settings.stockArea &&
                    a.N_CURRENT_NUM == 1 &&
                    a.N_LOCK_STATE == 0 &&
                    a.C_ENABLE == "Y" &&
                    b.S_CNTR_CODE != null &&
                    c.S_ITEM_CODE == itemCode)
                .GroupBy(a => a.N_ROADWAY)
                .Having(a => SqlFunc.AggregateCount(a.S_CODE) < 10)
                .Select(a => new { Roadway = a.N_ROADWAY, Count = SqlFunc.AggregateCount(a.S_CODE) })
                .OrderBy(a => a.Count)
                .Select(a => a.Roadway)
                .ToList();
            if (materialRoadways.Count > 0) return materialRoadways;
            // 策略2:所有巷道按空货位数降序排序
            return db.Queryable<Location>()
                .Where(a =>
                    a.S_AREA_CODE == Settings.stockArea &&
                    a.N_CURRENT_NUM == 0 &&
                    a.N_LOCK_STATE == 0 &&
                    a.C_ENABLE == "Y")
                .GroupBy(a => a.N_ROADWAY)
                .Select(a => new { Roadway = a.N_ROADWAY, Count = SqlFunc.AggregateCount(a.S_CODE) })
                .OrderByDescending(a => a.Count)
                .Select(a => a.Roadway)
                .ToList();
        }
        /// <summary>
        /// 查找单货位
        /// </summary>
        private static EndLocGroup FindSingleLocation(SqlSugarClient db, List<int> candidateRoadways)
        {
            foreach (var roadway in candidateRoadways)
            {
                var location = db.Queryable<Location>()
                    .Where(a =>
                        a.S_AREA_CODE == Settings.stockArea &&
                        a.N_ROADWAY == roadway &&
                        a.N_CURRENT_NUM == 0 &&
                        a.N_LOCK_STATE == 0 &&
                        a.C_ENABLE == "Y")
                    .OrderBy(a => a.N_LAYER)
                    .OrderBy(a => a.N_COL)
                    .First();
                if (location != null)
                {
                    return new EndLocGroup
                    {
                        endLocList = new List<Location> { location }
                    };
                }
            }
            return new EndLocGroup();
        }
        /// <summary>
        /// 查找双货位(优化相邻货位查找)
        /// </summary>
        private static EndLocGroup FindDoubleLocations(SqlSugarClient db, List<int> candidateRoadways)
        {
            // 先尝试找相邻货位
            foreach (var roadway in candidateRoadways)
            {
                // 一次性获取巷道所有空货位(减少DB查询)
                var emptyLocs = db.Queryable<Location>()
                    .Where(a =>
                        a.S_AREA_CODE == Settings.stockArea &&
                        a.N_ROADWAY == roadway &&
                        a.N_CURRENT_NUM == 0 &&
                        a.N_LOCK_STATE == 0 &&
                        a.C_ENABLE == "Y")
                    .OrderBy(a => a.N_LAYER)
                    .OrderBy(a => a.N_COL)
                    .ToList();
                if (emptyLocs.Count < 2) continue;
                // 在内存中查找相邻货位(高性能)
                var adjacentPair = FindAdjacentLocations(emptyLocs);
                if (adjacentPair != null)
                {
                    return CreateDoubleLocGroup(adjacentPair);
                }
            }
            // 没有相邻货位时,取任意两个货位
            foreach (var roadway in candidateRoadways)
            {
                var emptyLocs = db.Queryable<Location>()
                    .Where(a =>
                        a.S_AREA_CODE == Settings.stockArea &&
                        a.N_ROADWAY == roadway &&
                        a.N_CURRENT_NUM == 0 &&
                        a.N_LOCK_STATE == 0 &&
                        a.C_ENABLE == "Y")
                    .OrderBy(a => a.N_LAYER)
                    .OrderBy(a => a.N_COL)
                    .Take(2)
                    .ToList();
                if (emptyLocs.Count == 2)
                {
                    return CreateDoubleLocGroup(emptyLocs);
                }
            }
            return new EndLocGroup();
        }
        /// <summary>
        /// 在内存中查找相邻货位(高效算法)
        /// </summary>
        private static List<Location> FindAdjacentLocations(List<Location> locations)
        {
            // 按层->列排序,便于查找相邻
            var sorted = locations
                .OrderBy(l => l.N_LAYER)
                .ThenBy(l => l.N_COL)
                .ToList();
            for (int i = 0; i < sorted.Count - 1; i++)
            {
                var current = sorted[i];
                var next = sorted[i + 1];
                // 判断是否同一层且相邻列
                if (current.N_LAYER == next.N_LAYER &&
                    current.N_COL + 1 == next.N_COL)
                {
                    return new List<Location> { current, next };
                }
            }
            return null;
        }
        /// <summary>
        /// 创建双货位返回结果
        /// </summary>
        private static EndLocGroup CreateDoubleLocGroup(List<Location> locations)
        {
            return new EndLocGroup
            {
                endLocList = locations,
                groupNo = locations.GroupBy(a => a.N_ROADWAY).Count() == 1
                    ? WMSHelper.GenerateTaskGroupNo()
                    : null
            };
        }
        public class EndLocGroup
@@ -509,14 +697,15 @@
        /// <summary>
        /// 查询出库开始货位
        /// 计算(1.巷道不报警、2.物料状态OK、3.小于失效时间 大于等于生效时间 4.加急料先出、5.先入先出(生产时间))出库物料
        /// 1.计算(1.巷道不报警、2.物料状态OK、3.小于失效时间 大于等于生效时间 4.加急料先出、5.先入先出(生产时间))出库物料
        /// 2.同等条件下,优先取前一托货的相邻货位(暂时不做考虑,需确定先入先出(生产时间)是按天算,还是精确到时分秒)
        /// </summary>
        /// <param name="itemCode"></param>
        /// <param name="locNum"></param>
        public static StartLocGroup getOutStockStartLoc(string itemCode, int locNum )
        /// <param name="prevLoc"></param>
        public static Location getOutStockStartLoc(string itemCode, Location prevLoc = null)
        {
            var db = new SqlHelper<object>().GetInstance();
            StartLocGroup startLocGroup = new StartLocGroup();
            Location startLoc = null;
            // 1.查询(物料状态OK ,且小于失效时间,大于等于生效时间)出库物料,并按加急料先出,先入先出(生产时间)的原则进行排序
            var query = db.Queryable<Location>()
@@ -557,48 +746,10 @@
                        continue;
                    }
                    var cntrItemRel = db.Queryable<CntrItemRel>().LeftJoin<LocCntrRel>((a, b) => a.S_CNTR_CODE == b.S_CNTR_CODE).Where((a, b) => b.S_LOC_CODE == loc.S_CODE).First();
                    if (cntrItemRel != null)
                    {
                        startLocGroup.startLocList.Add(loc);
                        if (locNum == startLocGroup.startLocList.Count)
                        {
                            break;
                        }
                        var leftLoc = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_ROADWAY == loc.N_ROADWAY && a.N_ROW == loc.N_ROW && a.N_COL == loc.N_COL - 1 && a.N_LAYER == loc.N_LAYER && a.N_CURRENT_NUM == 1 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y").First();
                        if (leftLoc != null)
                        {
                            var locCntrRel = db.Queryable<LocCntrRel>()
                               .LeftJoin<CntrItemRel>((a, b) => a.S_CNTR_CODE == b.S_CNTR_CODE)
                               .Where((a, b) => a.S_LOC_CODE == leftLoc.S_CODE && b.S_ITEM_CODE == itemCode)
                               .First();
                            if (locCntrRel != null)
                            {
                                startLocGroup.groupNo = WMSHelper.GenerateTaskGroupNo();
                                startLocGroup.startLocList.Add(leftLoc);
                                break;
                            }
                        }
                        var rightLoc = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_ROADWAY == loc.N_ROADWAY && a.N_ROW == loc.N_ROW && a.N_COL == loc.N_COL + 1 && a.N_LAYER == loc.N_LAYER && a.N_CURRENT_NUM == 1 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y").First();
                        if (rightLoc != null && startLocGroup.startLocList.Count == 0)
                        {
                            var locCntrRel = db.Queryable<LocCntrRel>()
                                .LeftJoin<CntrItemRel>((a, b) => a.S_CNTR_CODE == b.S_CNTR_CODE)
                                .Where((a, b) => a.S_LOC_CODE == rightLoc.S_CODE && b.S_ITEM_CODE == itemCode)
                                .First();
                            if (locCntrRel != null)
                            {
                                startLocGroup.groupNo = WMSHelper.GenerateTaskGroupNo();
                                startLocGroup.startLocList.Add(rightLoc);
                                break;
                            }
                        }
                    }
                    startLoc = loc;
                }
            }
            return startLocGroup;
            return startLoc;
        }