|
using HH.WMS.BLL.Common;
|
using HH.WMS.Common;
|
using HH.WMS.Common.Algorithm;
|
using HH.WMS.DAL.Algorithm;
|
using HH.WMS.DAL.Basic;
|
using HH.WMS.Entitys.Algorithm;
|
using HH.WMS.Entitys.Basic;
|
using HH.WMS.Entitys.Common;
|
using System;
|
using System.Collections.Generic;
|
using System.Linq;
|
using System.Text;
|
using System.Threading.Tasks;
|
using HH.WMS.DAL.InStock;
|
using Newtonsoft.Json;
|
using HH.WMS.Entitys.Entitys;
|
|
|
namespace HH.WMS.BLL.Algorithm
|
{
|
/// <summary>
|
/// 补料算法业务层
|
/// </summary>
|
public class Feed_AlgorBLL : DapperBaseBLL
|
{
|
|
#region 补料
|
/// <summary>
|
/// 补料算法
|
/// </summary>
|
/// <param name="model"></param>
|
/// <returns></returns>
|
public FeedResultEntity FeedEmptyLocation(FeedAlgorEntity model)
|
{
|
|
FeedResultEntity feedResult = new FeedResultEntity();
|
List<LogicTrue> lstLogic = new List<LogicTrue>();
|
string logicCheckMsg = string.Empty;
|
lstLogic = GetLogicList(model.itemCode, model.stockAreaCode, out logicCheckMsg);
|
if (!string.IsNullOrEmpty(logicCheckMsg))
|
{
|
feedResult.Success = false;
|
feedResult.Msg = logicCheckMsg;
|
return feedResult;
|
}
|
locationEntity locE = FeedAlgorIn(lstLogic, model.lockLocation);
|
if (locE != null)
|
{
|
feedResult.Success = true;
|
feedResult.Msg = "";
|
feedResult.lstLocation = new List<feedLocationEntity>();
|
feedLocationEntity feedE = new feedLocationEntity();
|
feedE.locationCode = locE.locationCode;
|
feedResult.lstLocation.Add(feedE);
|
}
|
else
|
{
|
feedResult.Success = false;
|
feedResult.Msg = "未计算到可用货位";
|
}
|
return feedResult;
|
}
|
public OutFeedResultEntity Feed(FeedAlgorEntity model)
|
{
|
|
List<string> lstResult = new List<string>();
|
//定义返回实体
|
OutFeedResultEntity outResult = new OutFeedResultEntity();
|
Log.AlgorInfo("Feed算法开始", "");
|
#region 传递参数判断
|
if (string.IsNullOrEmpty(model.stockArea))
|
{
|
outResult.Success = false;
|
outResult.Msg = "请传递库区编码!";
|
return outResult;
|
}
|
if (model.lstItem == null || model.lstItem.Count == 0)
|
{
|
outResult.Success = false;
|
outResult.Msg = "请传递需要补料的物料!";
|
return outResult;
|
}
|
//判断是否有未维护物料对应的容器信息
|
List<string> lstItem = model.lstItem.Select(o => o.itemCode).Distinct().ToList();
|
List<TN_WM_B_TRAY_CAPACITYEntity> lstCapactity = new List<TN_WM_B_TRAY_CAPACITYEntity>();
|
#endregion
|
#region 获取基础配置数据
|
List<STRATEGYALGOREntity> lstStrategy = new List<STRATEGYALGOREntity>();
|
lstStrategy = CreateDAL<TN_WM_B_STRATEGYDAL>().GetStrateListByAreaOrStock("", model.stockArea, "入库");
|
List<string> lstStrate = lstStrategy.Select(o => o.CN_S_NAME).ToList();
|
string cfg = SysCache.GetStrategyValue(Constants.CL_IsAllowMixing);
|
//混放策略 1:相同物料不同批次可以混放 2:相同物料不同批次不能混放 默认为不同批次不能混放
|
int mixedType = 2;
|
if (cfg.Length > 0)
|
{
|
if (cfg == "Y")
|
{
|
mixedType = 1;
|
}
|
}
|
#endregion
|
if (lstStrate.Contains("按重量补料"))
|
{
|
Log.AlgorInfo("Feed按重量补料开始", "");
|
outResult = FridOneItemCodeByWeight(model, mixedType, lstStrate);
|
Log.AlgorInfo("Feed按重量补料结束", outResult.Success.ToString());
|
return outResult;
|
}
|
else if (lstStrate.Contains("未满托盘补料"))
|
{
|
Log.AlgorInfo("Feed未满托盘补料开始", "");
|
outResult = FridByNotFullTray(model, mixedType, lstStrate);
|
Log.AlgorInfo("Feed未满托盘补料结束", outResult.Success.ToString());
|
return outResult;
|
}
|
else
|
{
|
outResult.Success = false;
|
outResult.Msg = "未找到相应的补料算法,请检查策略是否配置正确";
|
return outResult;
|
}
|
}
|
|
#endregion
|
#region 上海汇聚补料补未满托盘
|
public OutFeedResultEntity FridByNotFullTray(FeedAlgorEntity model, int mixedType, List<string> lstStrate)
|
{
|
try
|
{
|
OutFeedResultEntity outResult = new OutFeedResultEntity();
|
outResult.Msg = "";
|
string CN_S_SPEC = "";
|
if (model.lstItem.Count > 1)
|
{
|
outResult.Success = false;
|
outResult.Msg = "只能传入一种物料数据";
|
}
|
//获取该物料对应的托盘规格,若无则所有托盘都可以补料
|
List<TN_WM_B_TRAY_CAPACITYEntity> lstCapaE = CreateDAL<HH.WMS.DAL.SysMgr.TN_WM_B_TRAY_CAPACITYDAL>().GetListByItemCode(model.lstItem[0].itemCode);
|
if (lstCapaE.Count > 0)
|
{
|
CN_S_SPEC = lstCapaE[0].CN_S_SPEC;
|
}
|
itemInClass fridItem = AddWeightAndCanMixInfo(model.lstItem, mixedType);
|
List<FridTray> lstFridTrayWeight = GetNotFullTray(fridItem, model.stockArea, CN_S_SPEC, "");
|
Log.AlgorInfo("FridByNotFullTray", "lstFridTrayWeight数据:" + JsonConvert.SerializeObject(lstFridTrayWeight));
|
//获取最优的补料方案
|
if (lstFridTrayWeight.Count > 0)
|
{
|
outResult.Success = true;
|
List<FeedItemEntity> lstFeedItem = new List<FeedItemEntity>();
|
FeedItemEntity feedItem = new FeedItemEntity();
|
feedItem.locationCode = lstFridTrayWeight[0].locationCode;
|
feedItem.trayCode = lstFridTrayWeight[0].trayCode;
|
feedItem.itemCode = fridItem.itemCode;
|
feedItem.itemQty = model.lstItem[0].itemQty;
|
outResult.lstFeedItem.AddRange(lstFeedItem);
|
}
|
else
|
{
|
outResult.Success = false;
|
outResult.Msg = "库区中没有可补料托盘";
|
}
|
|
if (outResult.Success)
|
{
|
List<string> lstNeedLockLoc = outResult.lstFeedItem.Select(o => o.locationCode).Distinct().ToList();
|
bool result = LockFeedLocation(model.lockLocation, lstNeedLockLoc);
|
Log.AlgorInfo("FridOneItemCodeByWeight", "锁定货位结果:" + result);
|
if (!result)
|
{
|
outResult.Success = false;
|
outResult.Msg = "锁定补料出库货位失败";
|
}
|
}
|
|
return outResult;
|
}
|
catch (Exception ex)
|
{
|
Log.AlgorInfo("FridOneItemCodeByWeight", ex.Message + ex.StackTrace);
|
OutFeedResultEntity outResult = new OutFeedResultEntity();
|
outResult.Success = false;
|
outResult.Msg = ex.Message;
|
return outResult;
|
}
|
|
|
}
|
public List<FridTray> GetNotFullTray(itemInClass fridItem, string areaCode, string traySpec, string locationCodes)
|
{
|
List<FridTray> lstFridTray = CreateDAL<Out_AlgorDAL>().GetNotFullTrayBySpec(areaCode, fridItem.itemCode, traySpec);
|
List<string> needRemoveItem = new List<string>();
|
//获取系统设置的物料混放上限
|
foreach (FridTray itemTray in lstFridTray)
|
{
|
//获取该托盘下的物料信息(物料、批次号、数量)
|
itemTray.lstItem = CreateDAL<Out_AlgorDAL>().GetItemByTray(itemTray.trayCode);
|
//如果该托盘下物料种类大于等于物料混放上限则不可以继续向其补料
|
if (itemTray.lstItem.Select(o => o.itemCode).Distinct().ToList().Count > itemTray.CN_N_GOODUPLINE - 1)
|
{
|
needRemoveItem.Add(itemTray.trayCode);
|
continue;
|
}
|
|
}
|
//去除不可补料的托盘
|
lstFridTray.RemoveAll(o => needRemoveItem.Contains(o.trayCode));
|
|
if (lstFridTray.Count == 0)
|
{
|
//如果库区中
|
needRemoveItem.Clear();
|
lstFridTray = CreateDAL<Out_AlgorDAL>().GetNotFullTrayBySpec(areaCode, "", traySpec);
|
foreach (FridTray itemTray in lstFridTray)
|
{
|
//获取该托盘下的物料信息(物料、批次号、数量)
|
itemTray.lstItem = CreateDAL<Out_AlgorDAL>().GetItemByTray(itemTray.trayCode);
|
//如果该托盘下物料种类大于等于5则不可以继续向其补料
|
if (itemTray.lstItem.Select(o => o.itemCode).Distinct().ToList().Count > itemTray.CN_N_GOODUPLINE - 1)
|
{
|
needRemoveItem.Add(itemTray.trayCode);
|
continue;
|
}
|
|
}
|
//根据可补料重量倒序
|
lstFridTray.RemoveAll(o => needRemoveItem.Contains(o.trayCode));
|
}
|
return lstFridTray;
|
}
|
#endregion
|
|
#region 按重量补料只考虑传入一种物料数据的场景
|
public OutFeedResultEntity FridOneItemCodeByWeight(FeedAlgorEntity model, int mixedType, List<string> lstStrate)
|
{
|
try
|
{
|
OutFeedResultEntity outResult = new OutFeedResultEntity();
|
outResult.Msg = "";
|
string CN_S_SPEC = "";
|
if (model.lstItem.Count > 1)
|
{
|
outResult.Success = false;
|
outResult.Msg = "只能传入一种物料数据";
|
}
|
//获取该物料对应的托盘规格,若无则所有托盘都可以补料
|
List<TN_WM_B_TRAY_CAPACITYEntity> lstCapaE = CreateDAL<HH.WMS.DAL.SysMgr.TN_WM_B_TRAY_CAPACITYDAL>().GetListByItemCode(model.lstItem[0].itemCode);
|
if (lstCapaE.Count > 0)
|
{
|
CN_S_SPEC = lstCapaE[0].CN_S_SPEC;
|
}
|
itemInClass fridItem = AddWeightAndCanMixInfo(model.lstItem, mixedType);
|
if (fridItem.itemWeight == 0)
|
{
|
outResult.Success = false;
|
outResult.Msg = "请维护物料的重量";
|
return outResult;
|
}
|
fridItem.needFridWeight = fridItem.itemWeight * fridItem.itemQty;
|
List<FridTrayWeight> lstFridTrayWeight = GetNotFullTrayWeight(fridItem, model.stockArea, CN_S_SPEC, "");
|
List<FeedEmptyTrayEntity> lstEmptyTray = GetEmptyTrayWeight(fridItem, model.stockArea, CN_S_SPEC);
|
Log.AlgorInfo("FridOneItemCodeByWeight", "lstFridTrayWeight数据:" + JsonConvert.SerializeObject(lstFridTrayWeight));
|
Log.AlgorInfo("FridOneItemCodeByWeight", "lstEmptyTray数据:" + JsonConvert.SerializeObject(lstEmptyTray));
|
//获取最优的补料方案
|
if (model.FeedType == FeedGoodType.None)
|
{
|
Log.AlgorInfo("Feed按重量补料", "正常出入库");
|
if (lstStrate.Contains("补料优先空托"))
|
{
|
Log.AlgorInfo("Feed按重量补料", "正常出入库-补料优先空托");
|
outResult = FridToBestEmptyTrayFirst(fridItem, lstFridTrayWeight, lstEmptyTray);
|
}
|
else if (lstStrate.Contains("补料优先未满托盘"))
|
{
|
Log.AlgorInfo("Feed按重量补料", "正常出入库-补料优先未满托盘");
|
outResult = FridToBestNotFullTrayFirst(fridItem, lstFridTrayWeight, lstEmptyTray);
|
}
|
else
|
{
|
outResult.Success = false;
|
outResult.Msg = "请维护补料优先策略";
|
}
|
}
|
else
|
{
|
Log.AlgorInfo("Feed按重量补料", "简易出库");
|
if (model.FeedType == FeedGoodType.EmptyTrayFirst)
|
{
|
Log.AlgorInfo("Feed按重量补料", "简易出库-优先空托");
|
outResult = FridToMaxEmptyTrayFirst(fridItem, lstFridTrayWeight, lstEmptyTray);
|
}
|
else if (model.FeedType == FeedGoodType.FridTrayFirst)
|
{
|
Log.AlgorInfo("Feed按重量补料", "简易出库-优先补料");
|
outResult = FridToMaxNotFullTrayFirst(fridItem, lstFridTrayWeight, lstEmptyTray);
|
}
|
else
|
{
|
outResult.Success = false;
|
outResult.Msg = "请维护补料优先策略";
|
}
|
}
|
|
if (outResult.Success)
|
{
|
List<string> lstNeedLockLoc = outResult.lstFeedItem.Select(o => o.locationCode).Distinct().ToList();
|
bool result = LockFeedLocation(model.lockLocation, lstNeedLockLoc);
|
Log.AlgorInfo("FridOneItemCodeByWeight", "锁定货位结果:" + result);
|
if (!result)
|
{
|
outResult.Success = false;
|
outResult.Msg = "锁定补料出库货位失败";
|
}
|
}
|
|
return outResult;
|
}
|
catch (Exception ex)
|
{
|
Log.AlgorInfo("FridOneItemCodeByWeight", ex.Message + ex.StackTrace);
|
OutFeedResultEntity outResult = new OutFeedResultEntity();
|
outResult.Success = false;
|
outResult.Msg = ex.Message;
|
return outResult;
|
}
|
|
|
}
|
private OutFeedResultEntity FridToBestEmptyTrayFirst(itemInClass fridItem, List<FridTrayWeight> lstFridTrayWeight, List<FeedEmptyTrayEntity> lstEmptyTray)
|
{
|
OutFeedResultEntity outResult = new OutFeedResultEntity();
|
outResult.lstFeedItem = new List<FeedItemEntity>();
|
List<FeedItemEntity> lstFeedItem = new List<FeedItemEntity>();
|
List<FridTrayWeight[]> tmpListCombination = new List<WMS.Common.Algorithm.FridTrayWeight[]>();
|
decimal totalNotFullWeight = lstFridTrayWeight.Sum(o => o.fridWeight);
|
decimal tmpCombinWeight = 0;
|
decimal nearFridWight = -1;
|
decimal tpmWeight = 0;
|
int nearIndex = 0;
|
List<decimal> lstFridWeight = new List<decimal>();
|
//优先补空托盘
|
if (lstEmptyTray.Count > 0)
|
{
|
lstFeedItem = FridEmptyTray(fridItem, lstEmptyTray);
|
}
|
if (fridItem.needFridWeight > 0)
|
{
|
//首先判断组合未满托盘是否有足够的可补重量
|
if (fridItem.needFridWeight <= totalNotFullWeight)
|
{
|
#region 组合未满托盘补料
|
FridTrayWeight[] arrFridTray = lstFridTrayWeight.ToArray();
|
for (int i = 1; i <= lstFridTrayWeight.Count; i++)
|
{
|
//将所有不满托盘组合,获取与补料最接近的方案
|
List<FridTrayWeight[]> ListCombination = PermutationAndCombination<FridTrayWeight>.GetCombination(arrFridTray, i);
|
foreach (FridTrayWeight[] arrInFrid in ListCombination)
|
{
|
//首先根据策略是否能混放来检查该方案是否可以补料
|
if (CanFridToTray(fridItem, arrInFrid))
|
{
|
tmpCombinWeight = arrInFrid.Sum(o => o.fridWeight);
|
lstFridWeight.Add(tmpCombinWeight);
|
tmpListCombination.Add(arrInFrid);
|
}
|
}
|
#region 寻找最接近补料重量的托盘组合
|
if (lstFridWeight.Count > 0)
|
{
|
if (lstFridWeight.Max() >= fridItem.needFridWeight)
|
{
|
for (int j = 0; j < lstFridWeight.Count; j++)
|
{
|
tpmWeight = lstFridWeight[j] - fridItem.needFridWeight;
|
if (nearFridWight == -1 && tpmWeight >= 0)
|
{
|
nearFridWight = tpmWeight;
|
}
|
if (tpmWeight <= nearFridWight && tpmWeight >= 0)
|
{
|
nearFridWight = tpmWeight;
|
nearIndex = j;
|
}
|
}
|
FridTrayWeight[] arrWeight = tmpListCombination[nearIndex];
|
foreach (FridTrayWeight ftw in arrWeight)
|
{
|
FeedItemEntity feedItem = new FeedItemEntity();
|
feedItem.locationCode = ftw.locationCode;
|
feedItem.trayCode = ftw.trayCode;
|
feedItem.itemCode = fridItem.itemCode;
|
if (fridItem.needFridWeight <= ftw.fridWeight)
|
{
|
feedItem.itemQty = fridItem.needFridWeight / fridItem.itemWeight;
|
}
|
else
|
{
|
feedItem.itemQty = ftw.fridWeight / fridItem.itemWeight;
|
}
|
fridItem.needFridWeight = fridItem.needFridWeight - ftw.fridWeight;
|
lstFeedItem.Add(feedItem);
|
}
|
// fridItem.needFridWeight = fridItem.needFridWeight - arrWeight.Sum(o => o.fridWeight);
|
break;
|
}
|
}
|
#endregion
|
}
|
#endregion
|
if (fridItem.needFridWeight <= 0)
|
{
|
outResult.Success = true;
|
outResult.lstFeedItem.AddRange(lstFeedItem);
|
}
|
else
|
{
|
outResult.Success = false;
|
outResult.Msg = "未找到合适的补料托盘";
|
}
|
|
}
|
else
|
{
|
outResult.Success = false;
|
outResult.Msg = "未找到合适的补料托盘";
|
}
|
}
|
else
|
{
|
outResult.Success = true;
|
outResult.lstFeedItem = new List<FeedItemEntity>();
|
outResult.lstFeedItem.AddRange(lstFeedItem);
|
}
|
return outResult;
|
}
|
private OutFeedResultEntity FridToMaxEmptyTrayFirst(itemInClass fridItem, List<FridTrayWeight> lstFridTrayWeight, List<FeedEmptyTrayEntity> lstEmptyTray)
|
{
|
OutFeedResultEntity outResult = new OutFeedResultEntity();
|
outResult.lstFeedItem = new List<FeedItemEntity>();
|
List<FeedItemEntity> lstFeedItem = new List<FeedItemEntity>();
|
List<FridTrayWeight[]> tmpListCombination = new List<WMS.Common.Algorithm.FridTrayWeight[]>();
|
decimal totalNotFullWeight = lstFridTrayWeight.Sum(o => o.fridWeight);
|
decimal tmpCombinWeight = 0;
|
int nearIndex = 0;
|
List<decimal> lstFridWeight = new List<decimal>();
|
//优先补空托盘
|
if (lstEmptyTray.Count > 0)
|
{
|
lstFeedItem = FridEmptyTray(fridItem, lstEmptyTray);
|
}
|
if (fridItem.needFridWeight > 0)
|
{
|
//首先判断组合未满托盘是否有足够的可补重量
|
if (fridItem.needFridWeight <= totalNotFullWeight)
|
{
|
#region 组合未满托盘补料
|
FridTrayWeight[] arrFridTray = lstFridTrayWeight.ToArray();
|
for (int i = 1; i <= lstFridTrayWeight.Count; i++)
|
{
|
//将所有不满托盘组合,获取与补料最接近的方案
|
List<FridTrayWeight[]> ListCombination = PermutationAndCombination<FridTrayWeight>.GetCombination(arrFridTray, i);
|
foreach (FridTrayWeight[] arrInFrid in ListCombination)
|
{
|
//首先根据策略是否能混放来检查该方案是否可以补料
|
if (CanFridToTray(fridItem, arrInFrid))
|
{
|
tmpCombinWeight = arrInFrid.Sum(o => o.fridWeight);
|
lstFridWeight.Add(tmpCombinWeight);
|
tmpListCombination.Add(arrInFrid);
|
}
|
}
|
#region 寻找最大补料重量的托盘组合
|
if (lstFridWeight.Count > 0)
|
{
|
if (lstFridWeight.Max() >= fridItem.needFridWeight)
|
{
|
for (int j = 0; j < lstFridWeight.Count; j++)
|
{
|
if (lstFridWeight.Max() == lstFridWeight[j])
|
{
|
nearIndex = j;
|
}
|
}
|
FridTrayWeight[] arrWeight = tmpListCombination[nearIndex];
|
foreach (FridTrayWeight ftw in arrWeight)
|
{
|
FeedItemEntity feedItem = new FeedItemEntity();
|
feedItem.locationCode = ftw.locationCode;
|
feedItem.trayCode = ftw.trayCode;
|
feedItem.itemCode = fridItem.itemCode;
|
if (fridItem.needFridWeight <= ftw.fridWeight)
|
{
|
feedItem.itemQty = fridItem.needFridWeight / fridItem.itemWeight;
|
}
|
else
|
{
|
feedItem.itemQty = ftw.fridWeight / fridItem.itemWeight;
|
}
|
fridItem.needFridWeight = fridItem.needFridWeight - ftw.fridWeight;
|
lstFeedItem.Add(feedItem);
|
}
|
// fridItem.needFridWeight = fridItem.needFridWeight - arrWeight.Sum(o => o.fridWeight);
|
break;
|
}
|
}
|
#endregion
|
}
|
#endregion
|
if (fridItem.needFridWeight <= 0)
|
{
|
outResult.Success = true;
|
outResult.lstFeedItem.AddRange(lstFeedItem);
|
}
|
else
|
{
|
outResult.Success = false;
|
outResult.Msg = "未找到合适的补料托盘";
|
}
|
|
}
|
else
|
{
|
outResult.Success = false;
|
outResult.Msg = "未找到合适的补料托盘";
|
}
|
}
|
else
|
{
|
outResult.Success = true;
|
outResult.lstFeedItem = new List<FeedItemEntity>();
|
outResult.lstFeedItem.AddRange(lstFeedItem);
|
}
|
return outResult;
|
}
|
/// <summary>
|
/// 按重量补料优先组合未满托盘补料
|
/// </summary>
|
/// <param name="fridItem"></param>
|
/// <param name="lstFridTrayWeight"></param>
|
/// <param name="lstEmptyTray"></param>
|
/// <returns></returns>
|
private OutFeedResultEntity FridToBestNotFullTrayFirst(itemInClass fridItem, List<FridTrayWeight> lstFridTrayWeight, List<FeedEmptyTrayEntity> lstEmptyTray)
|
{
|
OutFeedResultEntity outResult = new OutFeedResultEntity();
|
outResult.lstFeedItem = new List<FeedItemEntity>();
|
List<FeedItemEntity> lstFeedItem = new List<FeedItemEntity>();
|
List<FridTrayWeight[]> tmpListCombination = new List<WMS.Common.Algorithm.FridTrayWeight[]>();
|
decimal totalNotFullWeight = lstFridTrayWeight.Sum(o => o.fridWeight);
|
decimal tmpCombinWeight = 0;
|
decimal nearFridWight = -1;
|
decimal tpmWeight = 0;
|
int nearIndex = 0;
|
List<decimal> lstFridWeight = new List<decimal>();
|
|
//首先判断组合未满托盘是否有足够的可补重量
|
if (fridItem.needFridWeight <= totalNotFullWeight)
|
{
|
#region 组合未满托盘补料
|
FridTrayWeight[] arrFridTray = lstFridTrayWeight.ToArray();
|
for (int i = 1; i <= lstFridTrayWeight.Count; i++)
|
{
|
//将所有不满托盘组合,获取与补料最接近的方案
|
List<FridTrayWeight[]> ListCombination = PermutationAndCombination<FridTrayWeight>.GetCombination(arrFridTray, i);
|
foreach (FridTrayWeight[] arrInFrid in ListCombination)
|
{
|
//首先根据策略是否能混放来检查该方案是否可以补料
|
if (CanFridToTray(fridItem, arrInFrid))
|
{
|
tmpCombinWeight = arrInFrid.Sum(o => o.fridWeight);
|
lstFridWeight.Add(tmpCombinWeight);
|
tmpListCombination.Add(arrInFrid);
|
}
|
}
|
#region 寻找最接近补料重量的托盘组合
|
if (lstFridWeight.Count > 0)
|
{
|
if (lstFridWeight.Max() >= fridItem.needFridWeight)
|
{
|
for (int j = 0; j < lstFridWeight.Count; j++)
|
{
|
tpmWeight = lstFridWeight[j] - fridItem.needFridWeight;
|
if (nearFridWight == -1 && tpmWeight >= 0)
|
{
|
nearFridWight = tpmWeight;
|
}
|
if (tpmWeight <= nearFridWight && tpmWeight >= 0)
|
{
|
nearFridWight = tpmWeight;
|
nearIndex = j;
|
}
|
}
|
FridTrayWeight[] arrWeight = tmpListCombination[nearIndex];
|
foreach (FridTrayWeight ftw in arrWeight)
|
{
|
FeedItemEntity feedItem = new FeedItemEntity();
|
feedItem.locationCode = ftw.locationCode;
|
feedItem.trayCode = ftw.trayCode;
|
feedItem.itemCode = fridItem.itemCode;
|
if (fridItem.needFridWeight <= ftw.fridWeight)
|
{
|
feedItem.itemQty = fridItem.needFridWeight / fridItem.itemWeight;
|
}
|
else
|
{
|
feedItem.itemQty = ftw.fridWeight / fridItem.itemWeight;
|
}
|
fridItem.needFridWeight = fridItem.needFridWeight - ftw.fridWeight;
|
lstFeedItem.Add(feedItem);
|
}
|
break;
|
}
|
}
|
#endregion
|
}
|
#endregion
|
}
|
if (fridItem.needFridWeight > 0)
|
{
|
//再用空托盘补料
|
if (lstEmptyTray.Count > 0)
|
{
|
lstFeedItem = FridEmptyTray(fridItem, lstEmptyTray);
|
}
|
if (fridItem.needFridWeight > 0)
|
{
|
outResult.Success = false;
|
outResult.Msg = "未找到合适的补料托盘";
|
}
|
else
|
{
|
outResult.Success = true;
|
outResult.lstFeedItem = new List<FeedItemEntity>();
|
outResult.lstFeedItem.AddRange(lstFeedItem);
|
}
|
}
|
else
|
{
|
outResult.Success = true;
|
outResult.lstFeedItem = new List<FeedItemEntity>();
|
outResult.lstFeedItem.AddRange(lstFeedItem);
|
}
|
return outResult;
|
}
|
|
/// <summary>
|
/// 按重量补料优先组合未满托盘补料,优先找可补重量最大的
|
/// </summary>
|
/// <param name="fridItem"></param>
|
/// <param name="lstFridTrayWeight"></param>
|
/// <param name="lstEmptyTray"></param>
|
/// <returns></returns>
|
private OutFeedResultEntity FridToMaxNotFullTrayFirst(itemInClass fridItem, List<FridTrayWeight> lstFridTrayWeight, List<FeedEmptyTrayEntity> lstEmptyTray)
|
{
|
OutFeedResultEntity outResult = new OutFeedResultEntity();
|
outResult.lstFeedItem = new List<FeedItemEntity>();
|
List<FeedItemEntity> lstFeedItem = new List<FeedItemEntity>();
|
List<FridTrayWeight[]> tmpListCombination = new List<WMS.Common.Algorithm.FridTrayWeight[]>();
|
decimal totalNotFullWeight = lstFridTrayWeight.Sum(o => o.fridWeight);
|
decimal tmpCombinWeight = 0;
|
int nearIndex = 0;
|
List<decimal> lstFridWeight = new List<decimal>();
|
|
//首先判断组合未满托盘是否有足够的可补重量
|
if (fridItem.needFridWeight <= totalNotFullWeight)
|
{
|
#region 组合未满托盘补料
|
FridTrayWeight[] arrFridTray = lstFridTrayWeight.ToArray();
|
for (int i = 1; i <= lstFridTrayWeight.Count; i++)
|
{
|
//将所有不满托盘组合,获取与补料最接近的方案
|
List<FridTrayWeight[]> ListCombination = PermutationAndCombination<FridTrayWeight>.GetCombination(arrFridTray, i);
|
foreach (FridTrayWeight[] arrInFrid in ListCombination)
|
{
|
//首先根据策略是否能混放来检查该方案是否可以补料
|
if (CanFridToTray(fridItem, arrInFrid))
|
{
|
tmpCombinWeight = arrInFrid.Sum(o => o.fridWeight);
|
lstFridWeight.Add(tmpCombinWeight);
|
tmpListCombination.Add(arrInFrid);
|
}
|
}
|
#region 寻找最接近补料重量的托盘组合
|
if (lstFridWeight.Count > 0)
|
{
|
if (lstFridWeight.Max() >= fridItem.needFridWeight)
|
{
|
for (int j = 0; j < lstFridWeight.Count; j++)
|
{
|
if (lstFridWeight.Max() == lstFridWeight[j])
|
{
|
nearIndex = j;
|
}
|
}
|
FridTrayWeight[] arrWeight = tmpListCombination[nearIndex];
|
foreach (FridTrayWeight ftw in arrWeight)
|
{
|
FeedItemEntity feedItem = new FeedItemEntity();
|
feedItem.locationCode = ftw.locationCode;
|
feedItem.trayCode = ftw.trayCode;
|
feedItem.itemCode = fridItem.itemCode;
|
if (fridItem.needFridWeight <= ftw.fridWeight)
|
{
|
feedItem.itemQty = fridItem.needFridWeight / fridItem.itemWeight;
|
}
|
else
|
{
|
feedItem.itemQty = ftw.fridWeight / fridItem.itemWeight;
|
}
|
fridItem.needFridWeight = fridItem.needFridWeight - ftw.fridWeight;
|
lstFeedItem.Add(feedItem);
|
}
|
break;
|
}
|
}
|
#endregion
|
}
|
#endregion
|
}
|
if (fridItem.needFridWeight > 0)
|
{
|
//再用空托盘补料
|
if (lstEmptyTray.Count > 0)
|
{
|
lstFeedItem = FridEmptyTray(fridItem, lstEmptyTray);
|
}
|
if (fridItem.needFridWeight > 0)
|
{
|
outResult.Success = false;
|
outResult.Msg = "未找到合适的补料托盘";
|
}
|
else
|
{
|
outResult.Success = true;
|
outResult.lstFeedItem = new List<FeedItemEntity>();
|
outResult.lstFeedItem.AddRange(lstFeedItem);
|
}
|
}
|
else
|
{
|
outResult.Success = true;
|
outResult.lstFeedItem = new List<FeedItemEntity>();
|
outResult.lstFeedItem.AddRange(lstFeedItem);
|
}
|
return outResult;
|
}
|
/// <summary>
|
/// 获取库区中该规格的不满托盘可补料的重量方法
|
/// </summary>
|
/// <param name="areaCode"></param>
|
/// <param name="traySpec"></param>
|
/// <returns></returns>
|
public List<FeedEmptyTrayEntity> GetEmptyTrayWeight(itemInClass fridItem, string areaCode, string traySpec)
|
{
|
List<FeedEmptyTrayEntity> lstEmptyTray = CreateDAL<Out_AlgorDAL>().GetEmptyTrayList(areaCode, traySpec, "");
|
//传入物料数据是否都是整包的
|
bool IsPackage = false;
|
decimal remainder = 0;
|
if (fridItem.itemQty % fridItem.CN_F_MIN_PACK_QTY == 0)
|
{
|
IsPackage = true;
|
}
|
// Log.AlgorInfo("Feed按重量补料-GetEmptyTrayWeight", "IsPackage:" + IsPackage.ToString());
|
foreach (FeedEmptyTrayEntity itemTray in lstEmptyTray)
|
{
|
if (IsPackage)
|
{
|
//将不能放整包的重量去掉,防止计算出的补出重量需要拆包
|
//Log.AlgorInfo("Feed按重量补料-GetEmptyTrayWeight", "bareWeight:" + itemTray.bareWeight.ToString());
|
remainder = itemTray.bareWeight % (fridItem.CN_F_MIN_PACK_QTY * fridItem.itemWeight);
|
// Log.AlgorInfo("Feed按重量补料-GetEmptyTrayWeight", "remainder:" + remainder.ToString());
|
itemTray.bareWeight = itemTray.bareWeight - remainder;
|
// Log.AlgorInfo("Feed按重量补料-GetEmptyTrayWeight", "itemTray.bareWeight:" + itemTray.bareWeight.ToString());
|
}
|
}
|
//根据可补料重量倒序
|
lstEmptyTray.RemoveAll(o => o.bareWeight <= 0);
|
return lstEmptyTray;
|
}
|
/// <summary>
|
/// 获取库区中该规格的不满托盘可补料的重量方法
|
/// </summary>
|
/// <param name="areaCode"></param>
|
/// <param name="traySpec"></param>
|
/// <returns></returns>
|
public List<FridTrayWeight> GetNotFullTrayWeight(itemInClass fridItem, string areaCode, string traySpec, string locationCodes)
|
{
|
List<FridTrayWeight> lstFridTrayWeight = CreateDAL<Out_AlgorDAL>().GetTrayWeightBySpec(areaCode, traySpec, locationCodes);
|
//传入物料数据是否都是整包的
|
bool IsPackage = false;
|
decimal remainder = 0;
|
if (fridItem.itemQty % fridItem.CN_F_MIN_PACK_QTY == 0)
|
{
|
IsPackage = true;
|
}
|
//获取系统设置的物料混放上限
|
int mixGoods = 0;
|
if (fridItem.storeType == "托盘区")
|
{
|
int.TryParse(SysCache.GetStrategyValue(Constants.TPGoodUpLine), out mixGoods);
|
}
|
else if (fridItem.storeType == "料箱区")
|
{
|
int.TryParse(SysCache.GetStrategyValue(Constants.LXGoodUpLine), out mixGoods);
|
}
|
foreach (FridTrayWeight itemTray in lstFridTrayWeight)
|
{
|
//获取该托盘下的物料信息(物料、批次号、数量)
|
itemTray.lstItem = CreateDAL<Out_AlgorDAL>().GetItemWeightByTray(itemTray.trayCode);
|
//如果该托盘下物料种类大于等于5则不可以继续向其补料
|
if (itemTray.lstItem.Select(o => o.itemCode).Distinct().ToList().Count > mixGoods - 1)
|
{
|
itemTray.fridWeight = 0;
|
continue;
|
}
|
if (itemTray.realWeight != 0)
|
{
|
//如果返回了实际称重,根据实际称重值来计算
|
// itemTray.fridWeight = itemTray.loadWeight - (itemTray.realWeight - itemTray.crossWeight);
|
itemTray.fridWeight = itemTray.loadWeight - itemTray.realWeight;
|
|
}
|
else
|
{
|
//如果实际称重值没有维护则根据物料数量和物料毛重计算
|
decimal totalWeight = 0;
|
foreach (ItemInWeight itemWeight in itemTray.lstItem)
|
{
|
AutoBomItemEntity bomEntity = CreateDAL<TN_WMS_ITEMDAL>().GetItemEntity(itemWeight.itemCode);
|
totalWeight = totalWeight + itemWeight.itemQty * bomEntity.CN_F_TW;
|
}
|
itemTray.realWeight = totalWeight;
|
//计算出的实际重量不包括托盘重量,只需用可承重减去实际重量即可
|
itemTray.fridWeight = itemTray.loadWeight - itemTray.realWeight;
|
}
|
if (IsPackage)
|
{
|
//将不能放整包的重量去掉,防止计算出的补出重量需要拆包
|
remainder = itemTray.fridWeight % (fridItem.CN_F_MIN_PACK_QTY * fridItem.itemWeight);
|
itemTray.fridWeight = itemTray.fridWeight - remainder;
|
}
|
}
|
//根据可补料重量倒序
|
lstFridTrayWeight.RemoveAll(o => o.fridWeight <= 0);
|
return lstFridTrayWeight.OrderByDescending(o => o.fridWeight).ToList();
|
}
|
private List<FeedItemEntity> FridEmptyTray(itemInClass fridItem, List<FeedEmptyTrayEntity> lstEmptyTray)
|
{
|
List<FeedItemEntity> lstFeedItem = new List<FeedItemEntity>();
|
FeedEmptyTrayEntity emptyTray = null;
|
//将空托盘按由近及远排序
|
emptyTray = GetEmptyTrayByTrctics(lstEmptyTray);
|
if (emptyTray != null)
|
{
|
FeedItemEntity feedItem = new FeedItemEntity();
|
feedItem.locationCode = emptyTray.locationCode;
|
feedItem.trayCode = emptyTray.trayCode;
|
feedItem.itemCode = fridItem.itemCode;
|
|
if (fridItem.needFridWeight <= emptyTray.bareWeight)
|
{
|
feedItem.itemQty = fridItem.needFridWeight / fridItem.itemWeight;
|
}
|
else
|
{
|
feedItem.itemQty = emptyTray.bareWeight / fridItem.itemWeight;
|
}
|
fridItem.needFridWeight = fridItem.needFridWeight - emptyTray.bareWeight;
|
lstFeedItem.Add(feedItem);
|
lstEmptyTray.Remove(emptyTray);
|
}
|
if (lstEmptyTray.Count > 0 && fridItem.needFridWeight > 0)
|
{
|
//递归循环计算空托盘补料
|
lstFeedItem.AddRange(FridEmptyTray(fridItem, lstEmptyTray));
|
}
|
return lstFeedItem;
|
}
|
public FeedEmptyTrayEntity GetEmptyTrayByTrctics(List<FeedEmptyTrayEntity> lstEmptyTray)
|
{
|
FeedEmptyTrayEntity emptyTray = new FeedEmptyTrayEntity();
|
List<string> locCodes = lstEmptyTray.Select(o => o.locationCode).ToList();
|
//过滤货位状态为报废的货位
|
List<AutoBomLocationAbbreEntity> lstTrueLocation = CreateDAL<TN_WMS_LOCATIONDAL>().GetLocationByLocationCode(locCodes);
|
AlgorTacticsCommon Trctics = new AlgorTacticsCommon();
|
lstTrueLocation = Trctics.RoadWayBalance(lstTrueLocation);
|
List<AutoBomLocationAbbreEntity> nearLoc = Trctics.NearbyBalance(lstTrueLocation);
|
emptyTray = lstEmptyTray.Where(o => o.locationCode == nearLoc[0].CN_S_LOCATION_CODE).FirstOrDefault();
|
return emptyTray;
|
}
|
private bool CanFridToTray(itemInClass fridItem, FridTrayWeight[] arrInFrid)
|
{
|
bool result = true;
|
if (fridItem.canMix == "N")
|
{
|
foreach (FridTrayWeight ftw in arrInFrid)
|
{
|
foreach (ItemInWeight item in ftw.lstItem)
|
{
|
if (item.itemCode == fridItem.itemCode)
|
{
|
if (item.lotNo != fridItem.lotNo)
|
{
|
result = false;
|
return result;
|
}
|
}
|
}
|
}
|
}
|
return result;
|
}
|
private itemInClass AddWeightAndCanMixInfo(List<itemInClass> lstAreaItem, int mixType)
|
{
|
|
itemInClass itemIn = lstAreaItem[0];
|
AutoBomItemEntity autoBomE = CreateDAL<TN_WMS_ITEMDAL>().GetItemEntity(itemIn.itemCode);
|
if (mixType == 1)
|
{
|
//不同批次可以混放
|
//对于有质保要求的物料,即使策略可以混放,但对于有质保要求的物料不同批次仍不可以混放
|
if (autoBomE.CN_C_IS_WARRNTY_PARTS == "Y")
|
{
|
itemIn.canMix = "N";
|
}
|
else
|
{
|
itemIn.canMix = "Y";
|
}
|
}
|
else
|
{
|
//不同批次不可以混放
|
itemIn.canMix = "N";
|
}
|
if (string.IsNullOrEmpty(itemIn.lotNo))
|
{
|
itemIn.lotNo = "";
|
}
|
itemIn.itemWeight = autoBomE.CN_F_TW;
|
itemIn.storeType = autoBomE.CN_S_STORE_TYPE;
|
itemIn.CN_F_MIN_PACK_QTY = autoBomE.CN_F_MIN_PACK_QTY;
|
Log.AlgorInfo("AddWeightAndCanMixInfo", "物料:" + itemIn.itemCode + "数量:" + itemIn.itemQty + "是否允许混放:" + itemIn.canMix + "是否质保件:" + autoBomE.CN_C_IS_WARRNTY_PARTS.ToString() + "重量:" + autoBomE.CN_F_TW + "包装:" + autoBomE.CN_F_MIN_PACK_QTY);
|
return itemIn;
|
}
|
|
private bool LockFeedLocation(bool lockLocation, List<string> lstNeedLockLoc)
|
{
|
bool lockR = true;
|
SqlExecuteResult result = null;
|
StringBuilder sbLocationSuccess = new StringBuilder();
|
if (lockLocation)
|
{
|
foreach (string sLocation in lstNeedLockLoc)
|
{
|
result = CreateDAL<TN_WM_LOCATION_EXTDAL>().UpdateLocationState(sLocation, Constants.Location_State_OutLock, "补料", null);
|
Log.AlgorInfo("Feed_AlgorBLL货位锁定", "货位" + sLocation + "更改为预出库锁定,执行结果为:" + result.Success.ToString() + result.Row.ToString());
|
if (!result.Success || result.Row <= 0)
|
{
|
lockR = false;
|
break;
|
}
|
sbLocationSuccess.Append("'" + sLocation + "',");
|
}
|
}
|
if (!lockR)
|
{
|
//回滚之前锁定的货位
|
if (sbLocationSuccess.Length > 0)
|
{
|
sbLocationSuccess.Length = sbLocationSuccess.Length - 1;
|
result = CreateDAL<TN_WM_LOCATION_EXTDAL>().UpdateLocationNormal(sbLocationSuccess.ToString(), Constants.Location_State_Normal, null);
|
}
|
}
|
return lockR;
|
}
|
#endregion
|
|
|
|
#region 补料计算得到空货位
|
private locationEntity FeedAlgorIn(List<LogicTrue> lstLogic, bool lockLocation)
|
{
|
locationEntity locaE = null;
|
List<locationEntity> lstLocation = new List<locationEntity>();
|
In_AlgorBLL inBll = new In_AlgorBLL();
|
List<Device> lstDevice = new List<Device>();
|
//循环逻辑分区关联的仓库/库区/货位
|
foreach (LogicTrue logicEntity in lstLogic)
|
{
|
//获得真实可用的货位数据
|
Log.AlgorInfo("FeedAlgorIn-AlgorIn", "逻辑分区" + logicEntity.areaCode + logicEntity.locationCode + logicEntity.type);
|
Log.AlgorInfo("FeedAlgorIn-AlgorIn", "获得真实可用的货位数据开始");
|
List<AutoBomLocationAbbreEntity> lstTrueLocation = inBll.GetLocationByArea_R(logicEntity);
|
Log.AlgorInfo("FeedAlgorIn-AlgorIn", "获得真实可用的货位数据结束" + lstTrueLocation.Count);
|
if (lstTrueLocation == null || lstTrueLocation.Count == 0)
|
{
|
continue;
|
}
|
lstLocation = inBll.GetLocationsByStrategyAlgor(logicEntity.areaCode, 1, ref lstDevice, ref lstTrueLocation, lockLocation);
|
if (lstLocation.Count > 0)
|
{
|
locaE = lstLocation[0];
|
break;
|
}
|
}
|
return locaE;
|
}
|
#endregion
|
#region 公用方法 根据物料、库区与逻辑分区的对应关系求交集
|
public List<LogicTrue> GetLogicList(string itemCode, string stockAreaCode, out string errMsg)
|
{
|
errMsg = "";
|
#region 通过物料取逻辑分区 逻辑分区关联物料
|
//需要纳入计算的逻辑分区集合
|
List<LogicTrue> lstLogic = new List<LogicTrue>();
|
//逻辑分区集合
|
List<AutoBomPartition_Item_REntity> lstLogicEntityByItem = CreateDAL<TN_WMS_AREADAL>().GetPartitionItem(itemCode, 2);
|
|
if (lstLogicEntityByItem == null || lstLogicEntityByItem.Count == 0)
|
{
|
//根据物料编码获得物料信息
|
AutoBomItemEntity itemEntity = CreateDAL<TN_WMS_ITEMDAL>().GetItemEntity(itemCode);
|
if (itemEntity != null && !string.IsNullOrEmpty(itemEntity.CN_S_STORE_TYPE))
|
{
|
//获得物料类型
|
string itemType = itemEntity.CN_S_STORE_TYPE;
|
lstLogicEntityByItem = CreateDAL<TN_WMS_AREADAL>().GetPartitionItem(itemType, 1);
|
|
}
|
}
|
#endregion
|
//如果物料未分配逻辑分区则根据库区编码计算货位
|
if (lstLogicEntityByItem == null || lstLogicEntityByItem.Count == 0)
|
{
|
//errMsg = "根据物料编码未获得对应的逻辑分区!";
|
LogicTrue logicModel = new LogicTrue();
|
logicModel.areaCode = stockAreaCode;
|
logicModel.item_position = "1";
|
logicModel.locationCode = "";
|
logicModel.stockarea_position = "1";
|
logicModel.stockCode = "";
|
logicModel.type = 2;
|
lstLogic.Add(logicModel);
|
}
|
else
|
{
|
#region 通过库区取逻辑分区 逻辑分区关联仓库/库区/货位
|
List<AutoBomAreaLocation_REntity> lstLogicEntityByArea = CreateDAL<TN_WMS_AREADAL>().GetLogicListByAreaCode(stockAreaCode);
|
//如果物料未分配逻辑分区则返回空货位
|
if (lstLogicEntityByArea == null || lstLogicEntityByArea.Count == 0)
|
{
|
errMsg = "根据库区编码未获得对应的逻辑分区!";
|
}
|
#endregion
|
|
#region 取交集
|
//取交集
|
lstLogic = lstLogicEntityByItem.Join(lstLogicEntityByArea,
|
u => u.CN_S_AREA_CODE, d => d.CN_S_AREA_CODE, (u, d) => new { u, d })
|
.Select(o => new LogicTrue
|
{
|
stockCode = o.d.CN_S_STOCK_CODE,
|
areaCode = o.d.CN_S_STOCK_AREA,
|
locationCode = o.d.CN_S_LOCATION_CODE,
|
item_position = o.u.CN_S_PRIORITY,
|
stockarea_position = o.d.CN_S_PRIORITY,
|
type = o.d.CN_N_TYPE
|
}).ToList();
|
|
if (lstLogic == null || lstLogic.Count == 0)
|
{
|
errMsg = "逻辑分区物料关联和逻辑分区库区关联中未获得交集!";
|
}
|
//排序 先根据物料优先级 再根据仓库/库区/货位优先级
|
lstLogic = lstLogic.OrderBy(o => o.item_position).OrderBy(o => o.stockarea_position).ToList();
|
#endregion
|
}
|
return lstLogic;
|
}
|
#endregion
|
}
|
}
|