| | |
| | | var date = DateTime.Now.ToString("yyMMdd"); |
| | | return $"SO{date}{id.ToString().PadLeft(4, '0')}"; |
| | | } |
| | | internal static List<WMSTask> GetOperationListByState(string state) |
| | | { |
| | | var db = new SqlHelper<object>().GetInstance(); |
| | | return db.Queryable<WMSTask>().Where(a => a.S_B_STATE == state).ToList(); |
| | | } |
| | | internal static List<WMSTask> GetOperationListByState(int state) |
| | | { |
| | | var db = new SqlHelper<object>().GetInstance(); |
| | | return db.Queryable<WMSTask>().Where(a => a.N_B_STATE == state).ToList(); |
| | | } |
| | | internal static List<WMSTask> GetWaitingOperationList() |
| | | { |
| | | var db = new SqlHelper<object>().GetInstance(); |
| | | return db.Queryable<WMSTask>().Where(a => a.N_B_STATE == 0 || a.N_B_STATE == 3).ToList(); |
| | | } |
| | | internal static PutawayOrder GetPutawayOrder(string no) |
| | | { |
| | | var db = new SqlHelper<object>().GetInstance(); |
| | | return db.Queryable<PutawayOrder>().Where(a => a.S_NO == no).First(); |
| | | } |
| | | internal static bool CreatePutawayOrder(PutawayOrder model) |
| | | { |
| | | var db = new SqlHelper<object>().GetInstance(); |
| | | var result = db.Insertable<PutawayOrder>(model).ExecuteCommand() > 0; |
| | | db.Insertable<PutawayDetail>(model.Details).ExecuteCommand(); |
| | | return result; |
| | | } |
| | | |
| | | internal static PutawayDetail GetPutawayOrderDetail(string no, string item_code) |
| | | { |
| | | var db = new SqlHelper<object>().GetInstance(); |
| | | return db.Queryable<PutawayDetail>().Where(a => a.S_PUTAWAY_NO == no && a.S_ITEM_CODE == item_code).First(); |
| | | } |
| | | internal static PutawayDetail GetPutawayOrderDetail(string item_code) |
| | | { |
| | | var db = new SqlHelper<object>().GetInstance(); |
| | | return db.Queryable<PutawayDetail>().Where(a => a.S_ITEM_CODE == item_code && a.F_QTY - a.F_ACC_B_QTY > 0).OrderByDescending(a => a.T_CREATE).First(); |
| | | } |
| | | |
| | | internal static void UpdatePutawayOrderDetailQty(PutawayDetail model) |
| | | { |
| | | var db = new SqlHelper<object>().GetInstance(); |
| | | db.Updateable(model).UpdateColumns(it => new { it.F_ACC_B_QTY }).ExecuteCommand(); |
| | | } |
| | | |
| | | |
| | | internal static ShippingOrder GetShippingOrder(string no) |
| | | { |
| | | var db = new SqlHelper<object>().GetInstance(); |
| | | return db.Queryable<ShippingOrder>().Includes(a => a.Details).Where(a => a.S_NO == no).First(); |
| | | } |
| | | internal static bool CreateShippingOrder(ShippingOrder model) |
| | | { |
| | | var db = new SqlHelper<object>().GetInstance(); |
| | | var result = db.Insertable<ShippingOrder>(model).ExecuteCommand() > 0; |
| | | db.Insertable<ShippingDetail>(model.Details).ExecuteCommand(); |
| | | return result; |
| | | } |
| | | |
| | | internal static bool CreateSortingOrder(List<string> list) |
| | |
| | | throw new NotImplementedException(); |
| | | } |
| | | |
| | | internal static Location GetStart(WMSTask a) |
| | | { |
| | | throw new NotImplementedException(); |
| | | } |
| | | |
| | | internal static void UpdateTaskState(WMSTask task) |
| | | { |
| | | var db = new SqlHelper<object>().GetInstance(); |
| | |
| | | return db.Updateable<WMSTask>(a).UpdateColumns(it => new { it.S_END_LOC, it.T_MODIFY }).ExecuteCommand() > 0; |
| | | } |
| | | |
| | | internal static WMSTask GetWmsTask(string code) |
| | | { |
| | | var db = new SqlHelper<object>().GetInstance(); |
| | | return db.Queryable<WMSTask>().Where(a => a.S_CODE == code).First(); |
| | | } |
| | | |
| | | |
| | | |
| | | internal static void CreateSortingOrderDetail(string so_no) |
| | | { |
| | |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取开始配货的分拣单,一次性生成分拣明细,避免生成一半再生成,所以创建分拣明细的时候加上事务 |
| | | /// </summary> |
| | | /// <returns></returns> |
| | | internal static List<SortingOrder> GetWaitingSortingOrderList() |
| | | { |
| | | var db = new SqlHelper<object>().GetInstance(); |
| | | return db.Queryable<SortingOrder>().Includes(a => a.Composes).Where(a => a.N_B_STATE == 1 || a.N_B_STATE == 20).ToList(); |
| | | } |
| | | /// <summary> |
| | | /// 获取配货完成的分拣单,每个分拣单单独创建分拣作业 |
| | | /// </summary> |
| | |
| | | internal static LocCntrRel GetCntrLoc(string trayCode) |
| | | { |
| | | var db = new SqlHelper<object>().GetInstance(); |
| | | |
| | | |
| | | var locCntr = db.Queryable<LocCntrRel>().Where(a => a.S_CNTR_CODE.Trim() == trayCode).First(); |
| | | var locList = db.Queryable<Location>().Where(p => p.S_CODE == locCntr.S_LOC_CODE |
| | | && p.N_CURRENT_NUM == p.N_CAPACITY |
| | | var locList = db.Queryable<Location>().Where(p => p.S_CODE == locCntr.S_LOC_CODE |
| | | && p.N_CURRENT_NUM == p.N_CAPACITY |
| | | && p.S_LOCK_STATE.Trim() == "无" |
| | | && p.S_AREA_CODE == "JXHCQ").First(); |
| | | if (locList == null) |
| | |
| | | /// <param name="state">状态</param> |
| | | /// <param name="trayCode">托盘号</param> |
| | | /// <returns></returns> |
| | | internal static bool UpdateDistributionCntrState(int taskState,int state,string trayCode) |
| | | internal static bool UpdateDistributionCntrState(int taskState, int state, string trayCode) |
| | | { |
| | | var db = new SqlHelper<object>().GetInstance(); |
| | | var distributionCntr = db.Queryable<DistributionCntr>().Where(a => a.N_B_STATE == taskState && a.S_CNTR_CODE == trayCode ).First(); |
| | | var distributionCntr = db.Queryable<DistributionCntr>().Where(a => a.N_B_STATE == taskState && a.S_CNTR_CODE == trayCode).First(); |
| | | if (distributionCntr != null) |
| | | { |
| | | distributionCntr.N_B_STATE = state; |
| | |
| | | return res; |
| | | } |
| | | |
| | | internal static bool UpdateTask(WMSTask a, int state) |
| | | { |
| | | var db = new SqlHelper<object>().GetInstance(); |
| | | a.T_MODIFY = DateTime.Now; |
| | | a.N_B_STATE = state; |
| | | a.S_B_STATE = GetStateStr(state); |
| | | return db.Updateable<WMSTask>(a).UpdateColumns(it => new { it.N_B_STATE, it.S_B_STATE, it.S_CNTR_CODE, it.S_START_LOC, it.T_MODIFY }).ExecuteCommand() > 0; |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 杭氧立库出入库逻辑算法 |
| | | |
| | | |
| | | /// <summary> |
| | | /// 立库入库封装算法 |
| | | /// </summary> |
| | |
| | | { |
| | | var db = new SqlHelper<object>().GetInstance(); |
| | | var locations = db.Queryable<Location>().Where(a => a.S_AREA_CODE == inbound.areaCode).ToList(); |
| | | if(inbound.roadWay != 0) |
| | | if (inbound.roadWay != 0) |
| | | { |
| | | locations.RemoveAll(s => s.N_ROADWAY != inbound.roadWay); |
| | | } |
| | |
| | | return null; // 无可用位置 |
| | | } |
| | | private readonly ConcurrentDictionary<string, SemaphoreSlim> _locationLocks = new ConcurrentDictionary<string, SemaphoreSlim>(); |
| | | |
| | | |
| | | public async Task<LocationParams> StoreItemAsync() |
| | | { |
| | | LocationParams location = null; |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 立库出库封装算法 |
| | | /// 配盘出库封装算法 |
| | | /// </summary> |
| | | public class DoubleDeepOutboundScheduler |
| | | { |
| | |
| | | |
| | | // 生成出库任务队列(含移库任务) |
| | | /// <summary> |
| | | /// 立库WMS货位出库算法(运用于配盘单出库、空托出库) |
| | | /// 立库WMS货位出库算法(运用于配盘单出库) |
| | | /// </summary> |
| | | /// <param name="outbound">出库参数</param> |
| | | /// <returns></returns> |
| | |
| | | var wmsTask = WMSHelper.GetWmsTaskList("执行", outboundItem.trayCode); |
| | | if (wmsTask == null) |
| | | { |
| | | LogHelper.Info($"未查询到在执行中的作业:{outboundItem.trayCode}!", "杭氧"); |
| | | //LogHelper.Info($"未查询到在执行中的作业:{outboundItem.trayCode}!", "杭氧"); |
| | | outboundItem.opCode = ""; |
| | | } |
| | | |
| | |
| | | .Where(x => x.S_CODE == outerLoc.S_CODE) |
| | | .ToList() |
| | | .ForEach(x => x.N_LOCK_STATE = 3); |
| | | var trayCode = ContainerHelper.GetLocCntr(outerLoc.S_CODE); |
| | | tasks.Add(new WCSTask |
| | | { |
| | | S_CODE = GenerateTaskNo(), |
| | |
| | | N_SCHEDULE_TYPE = 1, |
| | | N_B_STATE = 0, |
| | | S_B_STATE = WCSTask.GetStateStr(0), |
| | | S_CNTR_CODE = outboundItem.trayCode, |
| | | S_CNTR_CODE = trayCode.S_CNTR_CODE, |
| | | N_START_LAYER = 1, |
| | | N_END_LAYER = 1, |
| | | N_CNTR_COUNT = 1 |
| | | }); |
| | | } |
| | | else continue; |
| | | |
| | | } |
| | | //else |
| | | //{ |
| | | // //货位为空直接生成出库任务 |
| | | // tasks.Add(CreateOutboundTask(targetLoc, outboundItem)); |
| | | //} |
| | | } |
| | | else |
| | | { |
| | |
| | | |
| | | private Location FindBestRelocationTarget(Location outerLoc) |
| | | { |
| | | return _allLocations |
| | | .Where(loc => |
| | | loc.N_ROADWAY == outerLoc.N_ROADWAY && |
| | | loc.N_LOCK_STATE == 0 && |
| | | loc.N_CURRENT_NUM == 0 && |
| | | loc.S_CODE != outerLoc.S_CODE) |
| | | .OrderBy(loc => loc.N_ROW == outerLoc.N_ROW ? 1 : 0) |
| | | .ThenBy(loc => loc.N_COL) |
| | | .ThenBy(loc => Math.Abs(loc.N_LAYER - outerLoc.N_LAYER)) |
| | | .FirstOrDefault(); |
| | | return _allLocations |
| | | .Where(loc => |
| | | loc.N_ROADWAY == outerLoc.N_ROADWAY && |
| | | loc.N_LOCK_STATE == 0 && |
| | | loc.N_CURRENT_NUM == 0 && |
| | | loc.S_CODE != outerLoc.S_CODE) |
| | | .OrderBy(loc => loc.N_ROW == outerLoc.N_ROW ? 1 : 0) |
| | | .ThenBy(loc => loc.N_COL) |
| | | .ThenBy(loc => Math.Abs(loc.N_LAYER - outerLoc.N_LAYER)) |
| | | .FirstOrDefault(); |
| | | } |
| | | |
| | | private WCSTask CreateOutboundTask(Location loc,Outbound outbound) => |
| | | private WCSTask CreateOutboundTask(Location loc, Outbound outbound) => |
| | | new WCSTask |
| | | { |
| | | S_CODE = GenerateTaskNo(), |
| | |
| | | S_END_LOC = outbound.endBit, |
| | | S_TYPE = outbound.taskType, |
| | | S_OP_CODE = outbound.opCode, |
| | | N_PRIORITY = 1, |
| | | N_PRIORITY = 0, |
| | | N_SCHEDULE_TYPE = 1, |
| | | N_B_STATE = 0, |
| | | S_B_STATE = WCSTask.GetStateStr(0), |
| | |
| | | |
| | | } |
| | | } |
| | | |
| | | public class EmptyPalletOutboundScheduler |
| | | { |
| | | private readonly List<Location> _allLocations; |
| | | |
| | | public EmptyPalletOutboundScheduler(string areaCode) |
| | | { |
| | | var db = new SqlHelper<object>().GetInstance(); |
| | | _allLocations = db.Queryable<Location>() |
| | | .Where(a => a.S_AREA_CODE == areaCode) |
| | | .ToList(); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 空托盘出库算法(自动寻找可用空托并生成任务) |
| | | /// </summary> |
| | | public List<WCSTask> GenerateEmptyPalletTasks(Outbound outbound) |
| | | { |
| | | var tasks = new List<WCSTask>(); |
| | | var foundPallets = 0; |
| | | |
| | | if (foundPallets < outbound.requiredCount) |
| | | { |
| | | var doubleDeepPallets = FindDoubleDeepEmptyPallets(); |
| | | foreach (var palletLoc in doubleDeepPallets) |
| | | { |
| | | if (IsDoubleDeepRow(palletLoc.N_ROW)) |
| | | { |
| | | var outerLoc = FindOuterLocation(palletLoc); |
| | | if (outerLoc != null) |
| | | { |
| | | if (outerLoc.N_CURRENT_NUM == outerLoc.N_CAPACITY) |
| | | { |
| | | // 优先移到深位,其次外侧 |
| | | var bestTarget = FindBestRelocationTarget(outerLoc); |
| | | if (bestTarget != null) |
| | | { |
| | | //计算到了外侧把外侧的货位锁定 |
| | | _allLocations |
| | | .Where(x => x.S_CODE == outerLoc.S_CODE) |
| | | .ToList() |
| | | .ForEach(x => x.N_LOCK_STATE = 3); |
| | | var trayCode = ContainerHelper.GetLocCntr(outerLoc.S_CODE); |
| | | |
| | | tasks.Add(new WCSTask |
| | | { |
| | | S_CODE = GenerateTaskNo(), |
| | | S_START_AREA = outerLoc.S_AREA_CODE, |
| | | S_END_AREA = bestTarget.S_AREA_CODE, |
| | | S_START_LOC = outerLoc.S_CODE, |
| | | S_END_LOC = bestTarget.S_CODE, |
| | | S_TYPE = "深位移库", |
| | | S_OP_CODE = outbound.opCode, |
| | | N_PRIORITY = 1, |
| | | N_SCHEDULE_TYPE = 1, |
| | | N_B_STATE = 0, |
| | | S_B_STATE = WCSTask.GetStateStr(0), |
| | | S_CNTR_CODE = trayCode.S_CNTR_CODE, |
| | | N_START_LAYER = 1, |
| | | N_END_LAYER = 1, |
| | | N_CNTR_COUNT = 1 |
| | | }); |
| | | } |
| | | else continue; |
| | | } |
| | | } |
| | | } |
| | | var tray = ContainerHelper.GetLocCntr(palletLoc.S_CODE); |
| | | outbound.trayCode = tray.S_CNTR_CODE; |
| | | tasks.Add(CreateEmptyPalletTask(palletLoc, outbound)); |
| | | foundPallets++; |
| | | |
| | | if (foundPallets >= outbound.requiredCount) break; |
| | | } |
| | | } |
| | | return tasks; |
| | | } |
| | | |
| | | // 关键辅助方法 |
| | | |
| | | private List<Location> FindDoubleDeepEmptyPallets() => |
| | | _allLocations.Where(loc => |
| | | loc.N_LOCK_STATE == 0 && |
| | | loc.N_CURRENT_NUM == loc.N_CAPACITY && |
| | | ContainerHelper.GetLocItemRel(loc.S_CODE) |
| | | ).OrderBy(loc => loc.N_ROW) // 按排排序 |
| | | .ThenBy(loc => loc.N_COL) |
| | | .ToList(); |
| | | |
| | | private WCSTask CreateEmptyPalletTask(Location loc, Outbound outbound) => |
| | | new WCSTask |
| | | { |
| | | S_CODE = GenerateTaskNo(), |
| | | S_START_AREA = loc.S_AREA_CODE, |
| | | S_END_AREA = outbound.endArea, |
| | | S_START_LOC = loc.S_CODE, |
| | | S_END_LOC = outbound.endBit, |
| | | S_TYPE = outbound.taskType, |
| | | S_OP_CODE = outbound.opCode, |
| | | N_PRIORITY = 0, |
| | | N_SCHEDULE_TYPE = 1, |
| | | N_B_STATE = 0, |
| | | S_B_STATE = WCSTask.GetStateStr(0), |
| | | S_CNTR_CODE = outbound.trayCode, |
| | | N_START_LAYER = 1, |
| | | N_END_LAYER = 1, |
| | | N_CNTR_COUNT = 1 |
| | | }; |
| | | |
| | | // 复用原有双深位方法 |
| | | private bool IsDoubleDeepRow(int row) => row == 1 || row == 4; |
| | | |
| | | private Location FindTargetLocation(string code) => |
| | | _allLocations.FirstOrDefault(loc => |
| | | loc.S_CODE == code && loc.N_LOCK_STATE == 0); |
| | | |
| | | //查询深位外侧的货位 |
| | | private Location FindOuterLocation(Location deepLoc) => |
| | | _allLocations.FirstOrDefault(loc => |
| | | loc.N_ROW != deepLoc.N_ROW && |
| | | loc.N_ROADWAY == deepLoc.N_ROADWAY && |
| | | loc.N_COL == deepLoc.N_COL && |
| | | loc.N_LAYER == deepLoc.N_LAYER && |
| | | loc.N_LOCK_STATE == 0 || loc.N_LOCK_STATE == 5 |
| | | ); |
| | | private Location FindBestRelocationTarget(Location outerLoc) |
| | | { |
| | | return _allLocations |
| | | .Where(loc => |
| | | loc.N_ROADWAY == outerLoc.N_ROADWAY && |
| | | loc.N_LOCK_STATE == 0 && |
| | | loc.N_CURRENT_NUM == 0 && |
| | | loc.S_CODE != outerLoc.S_CODE) |
| | | .OrderBy(loc => loc.N_ROW == outerLoc.N_ROW ? 1 : 0) |
| | | .ThenBy(loc => loc.N_COL) |
| | | .ThenBy(loc => Math.Abs(loc.N_LAYER - outerLoc.N_LAYER)) |
| | | .FirstOrDefault(); |
| | | } |
| | | private void MarkReservedLocations(List<Outbound> outbound) |
| | | { |
| | | //查询已经被锁住的货位 |
| | | var lockLoc = _allLocations |
| | | .Where(loc => loc.N_LOCK_STATE != 0) |
| | | .ToList(); |
| | | //把锁住的货位标记为占用 |
| | | foreach (var item in lockLoc) |
| | | { |
| | | if (IsDoubleDeepRow(item.N_ROW)) |
| | | { |
| | | _allLocations |
| | | .Where(x => x.S_CODE == item.S_CODE) |
| | | .ToList() |
| | | .ForEach(x => x.N_LOCK_STATE = 3); |
| | | } |
| | | } |
| | | foreach (var item in outbound) |
| | | { |
| | | var loc = FindTargetLocation(item.locCode); |
| | | if (loc != null && IsDoubleDeepRow(loc.N_ROW)) |
| | | { |
| | | // 标记该深位对应的外侧货位(如果存在) |
| | | var outerLoc = FindOuterLocation(loc); |
| | | if (outerLoc != null) |
| | | { |
| | | // 标记深位的外侧货位(避免被移库占用) |
| | | _allLocations |
| | | .Where(x => x.S_CODE == outerLoc.S_CODE) |
| | | .ToList() |
| | | .ForEach(x => x.N_LOCK_STATE = 5); |
| | | //标记深位的外侧货位为5 |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
| | | } |
| | | #endregion |
| | | |
| | | #region 杭氧升降量表帮助方法 |