using HH.WCS.HangYang.api;
using HH.WCS.HangYang.LISTA.models;
using HH.WCS.HangYang.LISTA.models.wms;
using HH.WCS.HangYang.models.other;
using HH.WCS.HangYang.util;
using Newtonsoft.Json;
using NLog;
using NLog.Fluent;
using Opc.Ua;
using SqlSugar;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IdentityModel.Protocols.WSTrust;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static HH.WCS.HangYang.api.ApiModel;
namespace HH.WCS.HangYang.wms
{
///
/// wms管到作业
///
internal class WMSHelper
{
internal static string GenerateTaskNo()
{
var id = SYSHelper.GetSerialNumber("作业号", "OP");
var date = DateTime.Now.ToString("yyMMdd");
return $"OP{date}{id.ToString().PadLeft(4, '0')}";
}
internal static string GenerateSortingNo()
{
var id = SYSHelper.GetSerialNumber("分拣单", "SO");
var date = DateTime.Now.ToString("yyMMdd");
return $"SO{date}{id.ToString().PadLeft(4, '0')}";
}
internal static List GetWaitingOperationList()
{
var db = new SqlHelper().GetInstance();
return db.Queryable().Where(a => a.N_B_STATE == 0 || a.N_B_STATE == 3).ToList();
}
internal static bool CreateSortingOrder(List list)
{
var res = false;
//遍历获取发货单,然后判断库存,如果全都没库存,则不生成分拣单,如果有生成分拣单
//更新波次单,即使只有一个发货单也更新波次单
var db = new SqlHelper().GetInstance();
var sortingOrderNo = "";
SortingOrder sortingOrder = null;
try
{
db.BeginTran();
list.ForEach(a =>
{
var so = db.Queryable().Includes(s => s.Details).Where(s => s.S_NO == a).First();
//判断库存,只有已经入库的才可以计算,码盘的不算,入库后出库途中的也要计算,码盘后默认叫待入库,入库后叫正常
//生成出库任务,只有托盘位置在立库才需要创建出库任务(先写死立库)
//只有全部分拣完成才允许回库(一个托盘可能对应多个分拣明细)
//查找仓库量表和库区量表,出库单需要定位仓库和库区了,出库定位物理库区就行,入库可能物料库区还要区分逻辑库区
//暂时只查仓库量表(量表如何重置,查找所有查找C_ENABLE=N的,判断如果在仓库中,改成Y,然后统计)
//生成分拣单时候增加仓库量表分配量,生成分拣单明细时候,增加库区量表分配量
if (so != null && so.Details.Count > 0)
{
var fail = true;
for (int i = 0; i < so.Details.Count; i++)
{
var whi = db.Queryable().Where(w => w.S_ITEM_CODE == so.Details[i].S_ITEM_CODE).First();
if (whi != null && whi.F_QTY - whi.F_ALLOC_QTY > 0)
{
//有货就出
float qty = whi.F_QTY - whi.F_ALLOC_QTY > so.Details[i].F_QTY ? so.Details[i].F_QTY : whi.F_QTY - whi.F_ALLOC_QTY;
fail = false;
//有可用库存,生成分拣单
if (sortingOrderNo == "")
{
sortingOrderNo = GenerateSortingNo();
sortingOrder = new SortingOrder
{
S_NO = sortingOrderNo,
S_SHIPPING_NO = so.S_NO,
Composes = new List()
};
//创建分拣单
db.Insertable(sortingOrder).ExecuteCommand();
}
else
{
//获取最新分拣单
sortingOrder = db.Queryable().Includes(s => s.Composes).Where(s => s.S_NO == sortingOrderNo).First();
//更新分拣单中的发货单单号
sortingOrder.S_SHIPPING_NO = sortingOrder.S_SHIPPING_NO + ";" + so.S_NO;
sortingOrder.T_MODIFY = DateTime.Now;
db.Updateable(sortingOrder).UpdateColumns(it => new { it.S_SHIPPING_NO, it.T_MODIFY }).ExecuteCommand();
}
//查询分拣单子表(正常情况还需要增加批次判断)
var soc = db.Queryable().Where(s => s.S_ITEM_CODE == so.Details[i].S_ITEM_CODE && s.S_SORTING_NO == sortingOrder.S_NO).First();
if (soc == null)
{
soc = new SortingCompose
{
S_ITEM_CODE = so.Details[i].S_ITEM_CODE,
S_SORTING_NO = sortingOrder.S_NO,
F_QTY = qty,
N_ROW_NO = sortingOrder.Composes.Count() + 1,
S_BATCH_NO = so.Details[i].S_BATCH_NO
};
//创建分拣单子表
db.Insertable(soc).ExecuteCommand();
}
else
{
soc.F_QTY += qty;
soc.T_MODIFY = DateTime.Now;
//更新分拣单子表
db.Updateable(soc).UpdateColumns(it => new { it.F_QTY, it.T_MODIFY }).ExecuteCommand();
}
//更新仓库量表分配量
whi.F_ALLOC_QTY += qty;
whi.T_MODIFY = DateTime.Now;
db.Updateable(whi).UpdateColumns(it => new { it.F_ALLOC_QTY, it.T_MODIFY }).ExecuteCommand();
//跟新发货单子表配货数量
so.Details[i].F_ACC_D_QTY += qty;
so.Details[i].T_MODIFY = DateTime.Now;
db.Updateable(so.Details[i]).UpdateColumns(it => new { it.F_ACC_D_QTY, it.T_MODIFY }).ExecuteCommand();
}
}
//更新发货单状态
so.N_B_STATE = fail ? 5 : 1;
if (fail)
{
so.S_NOTE = "没有库存";
}
so.S_WAVE_CODE = sortingOrderNo;
so.T_MODIFY = DateTime.Now;
db.Updateable(so).UpdateColumns(it => new { it.N_B_STATE, it.T_MODIFY, it.S_WAVE_CODE, it.S_NOTE }).ExecuteCommand();
}
//查找库区内托盘生成托盘明细(包括分拣中)怎么判断库内?查找货位绑定?还是托盘上加标识比较好,如果是整托出的,分拣确认后去掉分拣标识。
//1、只有码盘入库后的才需要加标识,分拣回的不用,分拣回的标识还在,没有变。写死码盘入的完成后加标识,整托分拣的去掉标识,防止后面重新码盘脏数据
//2、或者关联查询库内的,只给分拣出的加标识,每次分拣回再把标识清除了(如果不清除带标识的会很多,还不如全部加标识),整托的清除。
//综合选择还是方案1,这样创建分拣明细,只需要托盘物料两表查询就能定位托盘(如果信息不准,可以重置),方案2需要货位、托盘、物料三表联查
//暂时不计库区,标识用容器表 C_ENABLE 来判断,表示能不能出库
//执行分拣创建任务,遍历分拣明细中的托盘,如果在仓库就出库,如果不在就不用出库
});
//全部分拣单生成之后将分拣单状态设置为开始配货,波次号为发货单
sortingOrder = db.Queryable().Where(s => s.S_NO == sortingOrderNo).First();
sortingOrder.N_B_STATE = 1;
sortingOrder.T_MODIFY = DateTime.Now;
db.Updateable(sortingOrder).UpdateColumns(it => new { it.N_B_STATE, it.T_MODIFY }).ExecuteCommand();
db.CommitTran();
res = true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
db.RollbackTran();
}
return res;
}
internal static List GetSortingDetailByCntr(string cntr)
{
var db = new SqlHelper().GetInstance();
var result = db.Queryable().Where(a => a.S_CNTR_CODE == cntr && a.N_B_STATE != 2).ToList();
return result;
}
internal static bool CreateWmsTask(WMSTask wmsTask)
{
try
{
var db = new SqlHelper().GetInstance();
return db.Insertable(wmsTask).ExecuteCommand() > 0;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw;
}
}
internal static WMSTask GetWmsTaskByCntr(string cntr, bool active = true)
{
WMSTask result = null;
var db = new SqlHelper().GetInstance();
if (active)
{
result = db.Queryable().Where(a => a.S_CNTR_CODE.Contains(cntr) && a.N_B_STATE < 2).First();
}
else
{
result = db.Queryable().Where(a => a.S_CNTR_CODE.Contains(cntr)).First();
}
return result;
}
internal static Location GetEnd(WMSTask a)
{
throw new NotImplementedException();
}
internal static void UpdateTaskState(WMSTask task)
{
var db = new SqlHelper().GetInstance();
task.T_MODIFY = DateTime.Now;
task.S_B_STATE = WMSTask.GetStateStr(task.N_B_STATE);
db.Updateable(task).UpdateColumns(a => new { a.N_B_STATE, a.S_B_STATE, a.T_MODIFY }).ExecuteCommand();
}
internal static bool UpdateTaskEnd(WMSTask a)
{
var db = new SqlHelper().GetInstance();
a.T_MODIFY = DateTime.Now;
return db.Updateable(a).UpdateColumns(it => new { it.S_END_LOC, it.T_MODIFY }).ExecuteCommand() > 0;
}
internal static void CreateSortingOrderDetail(string so_no)
{
//分拣单配货
var db = new SqlHelper().GetInstance();
var so = db.Queryable().Includes(a => a.Composes).Where(a => a.S_NO == so_no && (a.N_B_STATE == 1 || a.N_B_STATE == 20)).First();//
if (so != null && so.Composes.Count > 0)
{
db.BeginTran();
try
{
int rowNo = 1;
so.Composes.ForEach(a =>
{
//按分拣单子表去配货,查找可用托盘(先查所有符合的,后面再优化)
int param1 = 10;
int param2 = 20;
Expression> exp = x => x > param1;
var cirList = db.Queryable().Includes(c => c.Cntr).Where(c => c.Cntr.C_ENABLE == "Y" && c.S_ITEM_CODE == a.S_ITEM_CODE && (c.F_QTY - c.F_ALLOC_QTY) > 0).OrderBy(c => c.T_CREATE).ToList();
for (int i = 0; i < cirList.Count; i++)
{
var cir = cirList[i];
var sd = new SortingDetail
{
N_ROW_NO = rowNo,
S_BATCH_NO = a.S_BATCH_NO,
S_ITEM_CODE = a.S_ITEM_CODE,
S_CNTR_CODE = cir.S_CNTR_CODE,
S_SORTING_NO = a.S_SORTING_NO
};
bool needBreak = false;
if (cir.F_QTY - cir.F_ALLOC_QTY >= (a.F_QTY - a.F_ACC_S_QTY))
{
//容器可用数量大于分拣单子表要求的数量,生成分拣明细,然后跳出循环
sd.F_QTY = a.F_QTY - a.F_ACC_S_QTY;
needBreak = true;
}
else
{
//生成分拣明细,继续创建
sd.F_QTY = cir.F_QTY - cir.F_ALLOC_QTY;
}
db.Insertable(sd).ExecuteCommand();
rowNo++;
//更新容器货品表分配量
cir.F_ALLOC_QTY += sd.F_QTY;
cir.T_MODIFY = DateTime.Now;
db.Updateable(cir).UpdateColumns(it => new { it.F_ALLOC_QTY, it.T_MODIFY }).ExecuteCommand();
//更新分拣单子表的量
a.F_ACC_S_QTY += sd.F_QTY;
a.T_MODIFY = DateTime.Now;
db.Updateable(cir).UpdateColumns(it => new { it.F_ALLOC_QTY, it.T_MODIFY }).ExecuteCommand();
if (needBreak)
{
break;
}
}
});
//全部分拣单子表生成分拣明细,修改分拣单状态为配货完成(反正仓库的货能配的都配完了,除非数据有异常)
so.N_B_STATE = 2;
so.T_MODIFY = DateTime.Now;
db.Updateable(so).UpdateColumns(it => new { it.N_B_STATE, it.T_MODIFY }).ExecuteCommand();
db.CommitTran();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
db.RollbackTran();
}
}
}
///
/// 获取配货完成的分拣单,每个分拣单单独创建分拣作业
///
///
internal static List GetWaitingSortingOperationList()
{
var db = new SqlHelper().GetInstance();
return db.Queryable().Includes(a => a.Details).Where(a => a.N_B_STATE == 2 || a.N_B_STATE == 3).ToList();
}
///
///单个分拣单的分拣明细创建作业
///
internal static void CreateSortingOperation(SortingOrder so)
{
var list = so.Details.Where(a => a.N_B_STATE == 0).ToList();
var db = new SqlHelper().GetInstance();
if (list.Count > 0)
{
try
{
db.BeginTran();
list.GroupBy(g => g.S_CNTR_CODE).ToList().ForEach(g =>
{
var cntr = g.Key;
var sdList = g.ToList();
//查询托盘货位,查到了创建任务,查不到说明在别的分拣作业中,不创建任务,需要回库后重新创建
var lcr = db.Queryable().Where(c => c.S_CNTR_CODE == cntr).First();
if (lcr != null)
{
//判断托盘是否已经创建任务了,可能多个分拣明细是同一个托盘,如果创建任务了,其它分拣的要稍后,只出一次人工会搞不清楚是哪个分拣单的
var wmsTask = db.Queryable().Where(op => op.S_CNTR_CODE.Contains(cntr) && op.N_B_STATE < 2).First();
if (wmsTask == null)
{
wmsTask = new WMSTask
{
S_CNTR_CODE = cntr,
S_CODE = WMSHelper.GenerateTaskNo(),
S_START_LOC = lcr.S_LOC_CODE,
S_END_LOC = "",
N_TYPE = 2,
S_TYPE = WMSTask.GetTypeStr(2),
S_OP_DEF_CODE = "",
S_OP_DEF_NAME = "分拣出立库"
};
if (db.Insertable(wmsTask).ExecuteCommand() > 0)
{
LocationHelper.LockLoc(lcr.S_LOC_CODE, 2);
}
sdList.ForEach(a =>
{
a.N_B_STATE = 1;
a.T_MODIFY = DateTime.Now;
db.Updateable(a).UpdateColumns(it => new { it.N_B_STATE, it.T_MODIFY }).ExecuteCommand();
});
}
}
//修改分拣单状态为开始作业(不代表作业全部创建了,因为有的托盘可能已经被占用了)
so.N_B_STATE = 3;
//如果所有的作业都已经创建了,就设置为作业已经创建
if (so.Details.Count(s => s.N_B_STATE == 0) == 0)
{
so.N_B_STATE = 4;
}
so.T_MODIFY = DateTime.Now;
db.Updateable(so).UpdateColumns(it => new { it.N_B_STATE, it.T_MODIFY }).ExecuteCommand();
});
db.CommitTran();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
db.RollbackTran();
}
}
}
internal static void SortingConfrim(List models)
{
if (models.Count > 0)
{
var db = new SqlHelper().GetInstance();
try
{
db.BeginTran();
//先查寻该分拣单所有执行中的分拣明细
var sdList = db.Queryable().Where(a => a.S_SORTING_NO == models[0].sortNo).ToList();
var soList = db.Queryable().Includes(a => a.Details).Where(a => a.S_WAVE_CODE == models[0].sortNo).ToList();
//查出所有关联的发货单
models.ForEach(a =>
{
//根据条目修改累计分拣数量,添加分拣结果表,同时批分到发货单中
var sd = sdList.Where(b => b.S_ITEM_CODE == a.itemCode).First();
//简单一点,要求人工必须一次性分拣完,分拣数量和分拣明细中一致才创建分拣结果,不支持一条分拣明细多次分拣,多条分拣结果
//(sd.F_QTY - sd.F_ACC_SR_QTY) == a.qty
if (sd != null && sd.F_QTY == a.qty)
{
//查询分拣结果是否已经存在,存在就累加
//分拣单结果
var sdr = new SortingResult
{
S_SORTING_NO = models[0].sortNo,
F_QTY = a.qty,
N_ROW_NO = sd.N_ROW_NO,
S_ITEM_CODE = sd.S_ITEM_CODE,
S_ITEM_NAME = "",
S_BATCH_NO = sd.S_BATCH_NO,
S_CNTR_CODE = a.cntrCode
};
db.Insertable(sdr).ExecuteCommand();
//sd.F_ACC_SR_QTY += a.qty;
sd.N_B_STATE = 2;
sd.T_MODIFY = DateTime.Now;
db.Updateable(sd).UpdateColumns(it => new { it.N_B_STATE, it.T_MODIFY }).ExecuteCommand();
//批分到发货单中
var dList = soList.SelectMany(s => s.Details).Where(d => d.S_ITEM_CODE == a.itemCode && (d.F_QTY - d.F_ACC_S_QTY) > 0).ToList();
var qty = a.qty;
for (int i = 0; i < dList.Count; i++)
{
var d = dList[i];
//发货信息中间表
if (d.F_QTY - d.F_ACC_S_QTY >= a.qty)
{
//发货单数量超过托盘分拣数量,批分qty
d.F_ACC_S_QTY += a.qty;
qty = 0;
}
else
{
// 发货单数量小于托盘分拣数量
d.F_ACC_S_QTY = d.F_QTY;
qty -= (d.F_QTY - d.F_ACC_S_QTY);
}
d.T_MODIFY = DateTime.Now;
db.Updateable(d).UpdateColumns(it => new { it.F_ACC_S_QTY, it.T_MODIFY }).ExecuteCommand();
//插入到大元出库中间表
if (qty == 0)
{
break;
}
}
//更新托盘明细表
var cir = db.Queryable().Where(c => c.S_CNTR_CODE == a.cntrCode && c.S_ITEM_CODE == a.itemCode).First();
cir.F_QTY -= a.qty;
cir.F_ALLOC_QTY -= a.qty;
cir.T_MODIFY = DateTime.Now;
db.Updateable(cir).UpdateColumns(it => new { it.F_QTY, it.F_ALLOC_QTY, it.T_MODIFY }).ExecuteCommand();
//更新仓库量表和库区量表
//查询仓库量表
var wi = db.Queryable().Where(i => i.S_ITEM_CODE == a.itemCode).First();
if (wi != null)
{
wi.F_QTY -= a.qty;
wi.F_ALLOC_QTY -= a.qty;
wi.T_MODIFY = DateTime.Now;
db.Updateable(wi).UpdateColumns(it => new { it.F_QTY, it.F_ALLOC_QTY, it.T_MODIFY }).ExecuteCommand();
}
//麻烦了,分拣明细只记托盘,不记住起点,如果要统计库区量表,必须对托盘加标记,记录托盘的来源库区,托盘清空后再清除标记
//逻辑库区更不需要量表了,因为从一个逻辑库区出来,可能回其它逻辑库区,那就应该直接删除逻辑库区量表,后面再加到别的逻辑库区中
//但是会带来新的问题,就是分拣回的托盘不属于任何逻辑库区的量,只有回库了才能计算到
//var aziList = db.Queryable().Where(i => i.S_ITEM_CODE == a.itemCode).ToList();
}
//
});
//判断分拣单是否全部完成
if (sdList.Count(sd => sd.N_B_STATE != 2) == 0)
{
var spo = db.Queryable().Where(s => s.S_NO == models[0].sortNo).First();
if (spo != null)
{
spo.N_B_STATE = 10;
spo.T_MODIFY = DateTime.Now;
db.Updateable(spo).UpdateColumns(it => new { it.N_B_STATE, it.T_MODIFY }).ExecuteCommand();
}
}
//判断发货单配货是否分拣完成
soList.ForEach(s =>
{
if (s.Details.Count > 0 && s.Details.Count(sd => sd.F_ACC_D_QTY != sd.F_ACC_S_QTY) == 0)
{
//配货单的数量等于分拣的数量,分拣完成
s.N_B_STATE = 3;
s.T_MODIFY = DateTime.Now;
db.Updateable(s).UpdateColumns(it => new { it.N_B_STATE, it.T_MODIFY }).ExecuteCommand();
}
});
db.CommitTran();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
db.RollbackTran();
}
}
}
internal static bool CheckSortingWholeCntr(string cntr, bool autoSort)
{
var result = false;
//判断分拣明细是不是只有一个托盘,如果是,判断容器货品明细是不是和分拣明细完全一样
var db = new SqlHelper().GetInstance();
var sdList = db.Queryable().Where(a => a.S_CNTR_CODE == cntr).ToList();
if (sdList.Count > 0)
{
var groups = sdList.GroupBy(a => a.S_SORTING_NO).ToList();
if (groups.Count == 1)
{
var list = groups.ToList();
//只有一个分拣单的情况下,分配量和托盘量都相同,表示是整托
var cirList = db.Queryable().Where(a => a.S_CNTR_CODE == cntr).ToList();
if (list.Count == cirList.Count && cirList.Count(c => c.F_QTY != c.F_ALLOC_QTY) == 0)
{
result = true;
if (autoSort)
{
var data = sdList.Select(s => new SortingResultCheck { cntrCode = s.S_CNTR_CODE, itemCode = s.S_ITEM_CODE, qty = s.F_QTY, sortNo = s.S_SORTING_NO }).ToList();
SortingConfrim(data);
}
}
}
}
return result;
}
#region 杭氧WMS帮助方法
///
/// 新增入库单
///
///
///
internal static bool CreateOrderIn(List order)
{
try
{
bool res = false;
var db = new SqlHelper().GetInstance();
db.BeginTran();
foreach (var orderItem in order)
{
db.Insertable(orderItem).ExecuteCommand();
db.Insertable(orderItem.InOrderDetail).ExecuteCommand();
}
db.CommitTran();
res = true;
return res;
}
catch (Exception ex)
{
var sugarEx = ex as SqlSugar.SqlSugarException;
if (sugarEx != null)
{
LogHelper.Error($"创建入库单SQL错误: {sugarEx.Sql}", sugarEx, "杭氧");
}
else
{
LogHelper.Error($"创建入库单失败:{ex.Message}", ex, "杭氧");
}
return false;
}
}
///
/// 新增出库单
///
///
///
internal static bool CreateOrderOut(List order)
{
try
{
bool res = false;
var db = new SqlHelper().GetInstance();
db.BeginTran();
foreach (var orderItem in order)
{
db.Insertable(orderItem).ExecuteCommand();
db.Insertable(orderItem.OutOrderDetail).ExecuteCommand();
}
db.CommitTran();
res = true;
return res;
}
catch (Exception ex)
{
LogHelper.Error($"创建出库单失败:{ex.Message}", ex, "杭氧");
return false;
}
}
#endregion
#region 杭氧特殊帮助方法
///
/// 查询物料的属性
///
///
///
internal static TN_Material GetItemType(string itemCode)
{
var db = new SqlHelper().GetInstance();
var lcrItem = db.Queryable().Where(a => a.S_ITEM_CODE.Trim() == itemCode).First();
if (lcrItem == null)
{
return null;
}
return lcrItem;
}
///
/// 获取需要合托的托盘
///
/// 托盘号
///
internal static LocCntrRel GetLocMerge(string trayCode)
{
var db = new SqlHelper().GetInstance();
var reservoirs = Settings.ReservoirAreas.Where(s => s.areaName == "托盘立库区").FirstOrDefault();
if (reservoirs == null)
{
LogHelper.Info("Settings出现错误未查询到杭氧的托盘立库区!", "杭氧");
return null;
}
var lcrItem = db.Queryable().Where(a => a.S_CNTR_CODE.Trim() == trayCode).First();
// 计算时间范围:前一天和今天的 00:00:00 到 23:59:59
DateTime yesterdayStart = DateTime.Today.AddDays(-1);
DateTime yesterdayEnd = DateTime.Today.AddDays(-1).AddDays(1).AddSeconds(-1);
DateTime NowdayStart = DateTime.Now.AddDays(-1);
DateTime NowdayEnd = DateTime.Now.AddDays(-1).AddDays(1).AddSeconds(-1);
var locCntrs = db.Queryable().LeftJoin((p, m) => p.S_CNTR_CODE == m.S_CNTR_CODE)
.LeftJoin((p, m, s) => m.S_ITEM_CODE == s.S_ITEM_CODE)
.Where((p, m, s) => p.T_CREATE >= yesterdayStart && p.T_CREATE < yesterdayEnd || p.T_CREATE >= NowdayStart && p.T_CREATE < NowdayEnd) // 前一天或者今天的数据
.Where((p, m, s) => m.S_ITEM_CODE == lcrItem.S_ITEM_CODE)
.Where((p, m, s) => m.F_QTY < s.F_MAX_QTY)
.OrderBy((p, m, s) => p.T_CREATE)
.Select((p, m, s) => p) // 选择托盘物料表的数据
.ToList();
//只查询立库的货位
var locList = db.Queryable().Where(p => p.S_AREA_CODE == reservoirs.areaCode && p.S_LOCK_STATE == "无").Select(s => s.S_CODE.Trim()).ToList();
locCntrs = locCntrs.Where(p => locList.Contains(p.S_LOC_CODE.Trim())).ToList();
if (!locCntrs.Any())
{
return null;
}
return locCntrs.FirstOrDefault();
}
///
/// 根据托盘查询货位明细
///
/// 托盘号
///
internal static LocCntrRel GetCntrLoc(string trayCode)
{
var db = new SqlHelper().GetInstance();
var locCntr = db.Queryable().Where(a => a.S_CNTR_CODE.Trim() == trayCode).First();
var locList = db.Queryable().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)
{
return null;
}
return locCntr;
}
///
/// 任务状态转换
///
/// 状态号
///
internal static string GetStateStr(int state)
{
//0 等待/1 执行/2 完成/3 错误/4 启劢失败 /5 暂停启劢
var status = "";
switch (state)
{
case 0: status = "等待"; break;
case 1: status = "执行"; break;
case 2: status = "完成"; break;
case 3: status = "错误"; break;
case 4: status = "启劢失败"; break;
case 5: status = "暂停启劢"; break;
}
return status;
}
///
/// 查询配盘单
///
/// 配盘单状态
///
internal static List GetPickingListByState(int state)
{
var db = new SqlHelper().GetInstance();
var distributionCntr = db.Queryable().Where(a => a.N_B_STATE == state).ToList();
return distributionCntr;
}
///
/// 配盘单状态转换
///
/// 状态号
///
internal static string GetDistributionStateStr(int state)
{
var status = "";
switch (state)
{
case 1: status = "已配货"; break;
case 2: status = "出库中"; break;
case 3: status = "已出库"; break;
case 4: status = "分拣完成 "; break;
}
return status;
}
///
/// 修改配盘单状态
///
/// 作业状态
/// 状态
/// 托盘号
///
internal static bool UpdateDistributionCntrState(int taskState, int state, string trayCode)
{
var db = new SqlHelper().GetInstance();
var distributionCntr = db.Queryable().Where(a => a.N_B_STATE == taskState && a.S_CNTR_CODE == trayCode).First();
if (distributionCntr != null)
{
distributionCntr.N_B_STATE = state;
distributionCntr.S_B_STATE = GetDistributionStateStr(state);
return db.Updateable(distributionCntr).UpdateColumns(it => new { it.S_B_STATE, it.N_B_STATE }).ExecuteCommand() > 0;
}
return false;
}
#endregion
#region 杭氧作业逻辑方法
///
/// 根据状态查询作业
///
///
///
internal static List GetWmsTaskListByState(string status)
{
var db = new SqlHelper().GetInstance();
return db.Queryable().Where(a => a.S_B_STATE.Trim() == status).OrderBy(s => s.T_CREATE).ToList();
}
///
/// 根据状态和托盘号查询作业
///
///
///
///
internal static WMSTask GetWmsTaskList(string status, string trayCode)
{
var db = new SqlHelper().GetInstance();
return db.Queryable().Where(a => a.S_CNTR_CODE == trayCode && a.S_B_STATE == status).OrderBy(s => s.T_CREATE).First();
}
///
/// 作业状态修改
///
/// 任务
/// 状态
///
internal static bool UpdateStatus(WMSTask task, int status)
{
var res = false;
var db = new SqlHelper().GetInstance();
task.N_B_STATE = status;
task.S_B_STATE = GetStateStr(status);
db.Updateable(task).UpdateColumns(it => new { it.N_B_STATE, it.S_B_STATE }).ExecuteCommand();
return res;
}
///
/// 根据作业编码修改状态
///
/// 任务号
/// 状态
///
internal static bool UpdateTaskStatus(string taskNo, int status)
{
var res = false;
var db = new SqlHelper().GetInstance();
var task = db.Queryable().Where(a => a.S_CODE == taskNo).First();
task.N_B_STATE = status;
task.S_B_STATE = GetStateStr(status);
db.Updateable(task).UpdateColumns(it => new { it.N_B_STATE, it.S_B_STATE }).ExecuteCommand();
return res;
}
///
/// 根据任务的作业编码修改状态和终点
///
///
///
///
internal static bool UpdateWmsTask(WCSTask wcsTask, int state)
{
var res = false;
var db = new SqlHelper().GetInstance();
var task = db.Queryable().Where(a => a.S_CODE == wcsTask.S_OP_CODE).First();
if (task != null)
{
task.N_B_STATE = state;
task.S_B_STATE = GetStateStr(state);
task.S_END_AREA = wcsTask.S_END_AREA;
task.S_END_LOC = wcsTask.S_END_LOC;
//需要判断任务是否失败或者已完成,不允许再修改
res = db.Updateable(task).UpdateColumns(it => new { it.N_B_STATE, it.S_B_STATE, it.S_END_LOC, it.S_END_AREA }).ExecuteCommand() > 0;
}
return res;
}
internal static bool UpdateTask(WMSTask a, int state)
{
var db = new SqlHelper().GetInstance();
a.T_MODIFY = DateTime.Now;
a.N_B_STATE = state;
a.S_B_STATE = GetStateStr(state);
return db.Updateable(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 杭氧立库出入库逻辑算法
///
/// 立库入库封装算法
///
public class Warehouse
{
private readonly List _locations;
private readonly Dictionary _depthOuterMap = new Dictionary
{
{ 1, 2 }, // 第1排深位对应第2排外侧
{ 4, 3 } // 第4排深位对应第3排外侧
};
public Warehouse(Inbound inbound)
{
var db = new SqlHelper().GetInstance();
var locations = db.Queryable().Where(a => a.S_AREA_CODE == inbound.areaCode).ToList();
if (inbound.roadWay != 0)
{
locations.RemoveAll(s => s.N_ROADWAY != inbound.roadWay);
}
_locations = new List();
// 初始化所有货位
for (int r = 0; r < locations.Count; r++)
{
bool isDepthRow = _depthOuterMap.ContainsKey(locations[r].N_ROW);
bool isOuterRow = _depthOuterMap.Values.Contains(locations[r].N_ROW);
// 深位排有双深位
if (isDepthRow)
{
_locations.Add(new LocationParams
{
Row = locations[r].N_ROW,
Column = locations[r].N_COL,
Layer = locations[r].N_LAYER,
roadWay = locations[r].N_ROADWAY,
loationCode = locations[r].S_CODE,
IsOccupied = locations[r].N_CURRENT_NUM > 0 ? true : false,
HasOutboundLock = locations[r].S_LOCK_STATE == "无" ? false : true,
Depth = 1,
IsStorageDepth = true,
IsOuterPosition = false
});
}
// 其他排只有单深位
else
{
_locations.Add(new LocationParams
{
Row = locations[r].N_ROW,
Column = locations[r].N_COL,
Layer = locations[r].N_LAYER,
roadWay = locations[r].N_ROADWAY,
loationCode = locations[r].S_CODE,
Depth = 0,
IsStorageDepth = false,
IsOuterPosition = isOuterRow
});
}
}
}
///
/// 货位锁定(算法内锁定不实际锁定货位)
///
///
///
///
public void SetOutboundLock(int row, int col, int layer)
{
var loc = _locations.FirstOrDefault(l =>
l.Row == row && l.Column == col &&
l.Layer == layer && l.IsStorageDepth);
if (loc != null) loc.HasOutboundLock = true;
}
public LocationParams FindNextStorageLocation()
{
// 阶段1: 查找所有可用深位(后深位)
var depthLocs = _locations
.Where(l => l.IsStorageDepth && !l.IsOccupied && !l.HasOutboundLock)
.OrderBy(l => l.Row)
.ThenBy(l => l.Column)
.ThenBy(l => l.Layer);
foreach (var loc in depthLocs)
{
int outerRow = _depthOuterMap.First(x => x.Key == loc.Row).Value;
// 检查前深位是否被占用
var frontLoc = _locations.FirstOrDefault(l =>
l.Row == outerRow && l.Column == loc.Column &&
l.Layer == loc.Layer && l.Depth == 0);
if (frontLoc == null || !frontLoc.IsOccupied)
return loc;
}
// 阶段2: 查找普通排(非深位非外侧)
var normalLoc = _locations
.FirstOrDefault(l => !l.IsOuterPosition && !l.IsStorageDepth &&
!l.IsOccupied && !_depthOuterMap.Values.Contains(l.Row));
if (normalLoc != null) return normalLoc;
// 阶段3: 最后查找外侧排(确保对应深位无锁)
foreach (var outerRow in _depthOuterMap.Values.OrderBy(r => r))
{
// 查找该外侧排所有可用货位(按巷道均衡排序)
var availableOuterLocs = _locations
.Where(l => l.IsOuterPosition &&
l.Row == outerRow &&
!l.IsOccupied)
.GroupBy(l => l.roadWay)
.SelectMany(g => g.OrderBy(l => l.Row)
.ThenBy(l => l.Column)
.ThenBy(l => l.Layer));
foreach (var outerLoc in availableOuterLocs)
{
// 获取对应的深位排(第1排或第4排)
var depthRow = _depthOuterMap.First(x => x.Value == outerLoc.Row).Key;
// 查找对应深位货位(相同列和层)
var depthLoc = _locations.FirstOrDefault(l =>
l.Row == depthRow &&
l.Column == outerLoc.Column &&
l.Layer == outerLoc.Layer &&
l.IsStorageDepth);
// 只有对应深位没有出库锁时才使用此外侧货位
if (depthLoc != null && !depthLoc.HasOutboundLock)
{
return outerLoc;
}
}
}
return null; // 无可用位置
}
private readonly ConcurrentDictionary _locationLocks = new ConcurrentDictionary();
public async Task StoreItemAsync()
{
LocationParams location = null;
try
{
// 1. 查找可用货位(不需要锁)
location = FindNextStorageLocation();
if (location == null) return location;
// 2. 获取该货位的专用锁
var locationLock = _locationLocks.GetOrAdd(
$"{location.loationCode}",
_ => new SemaphoreSlim(1, 1));
await locationLock.WaitAsync();
// 3. 执行入库
location.IsOccupied = true;
Console.WriteLine($"入库I:排号{location.Row},列号{location.Column},层号{location.Layer},货位{location.loationCode}");
return location;
}
finally
{
if (location != null)
{
var key = $"{location.loationCode}";
if (_locationLocks.TryGetValue(key, out var locLock))
{
locLock.Release();
}
}
}
}
}
///
/// 配盘出库封装算法
///
public class DoubleDeepOutboundScheduler
{
private readonly List _allLocations;// 所有货位
public DoubleDeepOutboundScheduler(List allLocations)
{
var db = new SqlHelper().GetInstance();
//查询这个库区的货位
var list = db.Queryable().Where(a => a.S_AREA_CODE == allLocations.First().S_AREA_CODE).ToList();
_allLocations = list;
}
// 生成出库任务队列(含移库任务)
///
/// 立库WMS货位出库算法(运用于配盘单出库)
///
/// 出库参数
///
public List GenerateOutboundTasks(List outbound)
{
// 阶段1: 标记未来需要保留的货位
MarkReservedLocations(outbound);
var tasks = new List();
foreach (var outboundItem in outbound)
{
// 1. 查找目标货位
var targetLoc = FindTargetLocation(outboundItem.locCode);
if (targetLoc == null) continue;
var wmsTask = WMSHelper.GetWmsTaskList("执行", outboundItem.trayCode);
if (wmsTask == null)
{
//LogHelper.Info($"未查询到在执行中的作业:{outboundItem.trayCode}!", "杭氧");
outboundItem.opCode = "";
}
// 2. 处理双深位逻辑(第1排或第4排)
if (IsDoubleDeepRow(targetLoc.N_ROW))
{
// 2.1 深位出库需先移出外侧物料
var outerLoc = FindOuterLocation(targetLoc);
//如果找不到,则跳过此条出库任务
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 = wmsTask.S_CODE,
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;
}
}
else
{
continue;
}
// 2.2 生成出库任务(深位)
tasks.Add(CreateOutboundTask(targetLoc, outboundItem));
}
else
{
// 3. 非双深位直接出库
tasks.Add(CreateOutboundTask(targetLoc, outboundItem));
}
break;
}
// 4. 任务排序:移库任务优先 + 高优先级优先
return tasks;
}
// 关键辅助方法
private Location FindTargetLocation(string code) =>
_allLocations.FirstOrDefault(loc =>
loc.S_CODE == code && loc.N_LOCK_STATE == 0);
private bool IsDoubleDeepRow(int row) => row == 1 || row == 4;
//查询深位外侧的货位
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 WCSTask CreateOutboundTask(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 void MarkReservedLocations(List 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
}
}
}
}
}
public class EmptyPalletOutboundScheduler
{
private readonly List _allLocations;
public EmptyPalletOutboundScheduler(string areaCode)
{
var db = new SqlHelper().GetInstance();
_allLocations = db.Queryable()
.Where(a => a.S_AREA_CODE == areaCode)
.ToList();
}
///
/// 空托盘出库算法(自动寻找可用空托并生成任务)
///
public List GenerateEmptyPalletTasks(Outbound outbound)
{
var tasks = new List();
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 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)
{
//查询已经被锁住的货位
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 杭氧升降量表帮助方法
///
/// MD5加密
///
///
///
public static string GetMd5FromString(string sInput)
{
var lstData = Encoding.GetEncoding("utf-8").GetBytes(sInput);
var lstHash = new MD5CryptoServiceProvider().ComputeHash(lstData);
var result = new StringBuilder(32);
for (int i = 0; i < lstHash.Length; i++)
{
result.Append(lstHash[i].ToString("x2").ToUpper());
}
return result.ToString();
}
///
/// 根据任务升量
///
///
internal static bool AddChange(WCSTask task)
{
var db = new SqlHelper().GetInstance();
var result = true;
try
{
var itemlist = db.Queryable().Where(a => a.S_CNTR_CODE == task.S_CNTR_CODE).ToList();
if (itemlist.Count > 0)
{
var url = Settings.MoboxSeverUrl + "inventory/AddChange";
//仓库量表升量
var req = new AddChangeModel { op_type = 5 };
//库区量表升量
var req2 = new AddChangeModel { op_type = 6 };
itemlist.ForEach(a =>
{
LogHelper.Info($"填充数据");
LogHelper.Info($"添加仓库量表数据 仓库{task.S_END_WH} 物料编码{a.S_ITEM_CODE} 物料名称{a.S_ITEM_NAME} 数量{a.F_QTY}");
req.item_info.Add(new AddChangeModel.itemModel
{
wh_code = task.S_END_WH,
item_code = a.S_ITEM_CODE,
item_name = a.S_ITEM_NAME,
qty = a.F_QTY
});
LogHelper.Info($"添加库区量表数据 库区{task.S_END_AREA} 物料编码{a.S_ITEM_CODE} 物料名称{a.S_ITEM_NAME} 数量{a.F_QTY}");
req2.item_info.Add(new AddChangeModel.itemModel
{
wh_code = task.S_END_WH,
area_code = task.S_END_AREA,
item_code = a.S_ITEM_CODE,
item_name = a.S_ITEM_NAME,
qty = a.F_QTY
});
});
var reqData = JsonConvert.SerializeObject(req);
var AppKey = Settings.AppKey;
var AppSecret = Settings.AppSecret;
var ReqTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
//var ReqTime = GetTimeStamp(DateTime.Now.AddHours(-8), 1, 2);
LogHelper.Info($"加密前 AppKey={AppKey} AppSecret={AppSecret} ReqTime={ReqTime}");
var ReqVerify = GetMd5FromString(AppKey + AppSecret + ReqTime);
LogHelper.Info($"加密后 AppKey={AppKey} AppSecret={AppSecret} ReqTime={ReqTime} ReqVerify={ReqVerify} url={url} req={reqData}");
var res = new HttpHelper().WebPost(url, reqData, "application/json", AppKey, ReqTime, ReqVerify);
if (!string.IsNullOrEmpty(res))
{
LogHelper.Info($"mobox 仓库升量接口返回 {res}");
var moboxres = JsonConvert.DeserializeObject(res);
if (moboxres.err_code != 0)
{
result = false;
}
}
else
{
LogHelper.Info($"mobox 仓库升量接口返回为空");
result = false;
}
var reqData2 = JsonConvert.SerializeObject(req2);
var ReqTime2 = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
//var ReqTime2 = GetTimeStamp(DateTime.Now.AddHours(-8), 1, 2);
LogHelper.Info($"加密前 AppKey={AppKey} AppSecret={AppSecret} ReqTime={ReqTime2}");
var ReqVerify2 = GetMd5FromString(AppKey + AppSecret + ReqTime2);
LogHelper.Info($"加密后 AppKey={AppKey} AppSecret={AppSecret} ReqTime={ReqTime2} ReqVerify={ReqVerify2} url={url} req={reqData2}");
var res2 = new HttpHelper().WebPost(url, reqData2, "application/json", AppKey, ReqTime2, ReqVerify2);
if (!string.IsNullOrEmpty(res2))
{
LogHelper.Info($"mobox 库区升量接口返回 {res2}");
var moboxres = JsonConvert.DeserializeObject(res);
if (moboxres.err_code != 0)
{
result = false;
}
}
else
{
LogHelper.Info($"mobox 库区升量接口返回为空");
result = false;
}
}
else LogHelper.Info($"托盘{task.S_CNTR_CODE} 在容器货品明细中找不到数据");
}
catch (Exception ex)
{
LogHelper.Error($"仓库升量异常 异常信息={ex.Message}", ex);
result = false;
}
return result;
}
///
/// 根据任务降量
///
///
internal static bool DeleteChange(WCSTask wmstask)
{
var db = new SqlHelper().GetInstance();
var result = true;
try
{
//var wmstask = db.Queryable().Where(a => a == task.S_OP_CODE).First();
if (wmstask != null)
{
var itemlist = db.Queryable().Where(a => a.S_CNTR_CODE == wmstask.S_CNTR_CODE).ToList();
if (itemlist.Count > 0)
{
var url = Settings.MoboxSeverUrl + "inventory/AddChange";
//仓库量表降量
var req = new AddChangeModel { op_type = 8 };
//库区量表降量
var req2 = new AddChangeModel { op_type = 9 };
itemlist.ForEach(a =>
{
LogHelper.Info($"填充数据");
LogHelper.Info($"减仓库量表数据 仓库{wmstask.S_START_WH} 物料编码{a.S_ITEM_CODE} 物料名称{a.S_ITEM_NAME} 数量{a.F_QTY}");
req.item_info.Add(new AddChangeModel.itemModel
{
wh_code = wmstask.S_START_WH,
item_code = a.S_ITEM_CODE,
item_name = a.S_ITEM_NAME,
qty = a.F_QTY
});
LogHelper.Info($"减库区量表数据 库区{wmstask.S_START_AREA} 物料编码{a.S_ITEM_CODE} 物料名称{a.S_ITEM_NAME} 数量{a.F_QTY}");
req2.item_info.Add(new AddChangeModel.itemModel
{
wh_code = wmstask.S_START_WH,
area_code = wmstask.S_START_AREA,
item_code = a.S_ITEM_CODE,
item_name = a.S_ITEM_NAME,
qty = a.F_QTY
});
});
var reqData = JsonConvert.SerializeObject(req);
var AppKey = Settings.AppKey;
var AppSecret = Settings.AppSecret;
var ReqTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
//var ReqTime = GetTimeStamp(DateTime.Now.AddHours(-8), 1, 2);
LogHelper.Info($"加密前 AppKey={AppKey} AppSecret={AppSecret} ReqTime={ReqTime}");
var ReqVerify = GetMd5FromString(AppKey + AppSecret + ReqTime);
LogHelper.Info($"加密后 AppKey={AppKey} AppSecret={AppSecret} ReqTime={ReqTime} ReqVerify={ReqVerify} url={url} req={reqData}");
var res = new HttpHelper().WebPost(url, reqData, "application/json", AppKey, ReqTime, ReqVerify);
if (!string.IsNullOrEmpty(res))
{
LogHelper.Info($"mobox 仓库降量接口返回 {res}");
var moboxres = JsonConvert.DeserializeObject(res);
if (moboxres.err_code != 0)
{
result = false;
}
}
else
{
LogHelper.Info($"mobox 仓库降量接口返回为空");
result = false;
}
var reqData2 = JsonConvert.SerializeObject(req2);
var ReqTime2 = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
//var ReqTime2 = GetTimeStamp(DateTime.Now.AddHours(-8), 1, 2);
LogHelper.Info($"加密前 AppKey={AppKey} AppSecret={AppSecret} ReqTime={ReqTime2}");
var ReqVerify2 = GetMd5FromString(AppKey + AppSecret + ReqTime2);
LogHelper.Info($"加密后 AppKey={AppKey} AppSecret={AppSecret} ReqTime={ReqTime2} ReqVerify={ReqVerify2} url={url} req={reqData2}");
var res2 = new HttpHelper().WebPost(url, reqData2, "application/json", AppKey, ReqTime2, ReqVerify2);
if (!string.IsNullOrEmpty(res2))
{
LogHelper.Info($"mobox 库区降量接口返回 {res2}");
var moboxres = JsonConvert.DeserializeObject(res);
if (moboxres.err_code != 0)
{
result = false;
}
}
else
{
LogHelper.Info($"mobox 库区降量接口返回为空");
result = false;
}
}
else LogHelper.Info($"托盘{wmstask.S_CNTR_CODE} 在容器货品明细中找不到数据");
}
else LogHelper.Info($"未找到任务{wmstask.S_CODE} 对应的任务");
}
catch (Exception ex)
{
LogHelper.Error($"仓库降量异常 异常信息={ex.Message}", ex);
result = false;
}
return result;
}
#endregion
}
}