using HH.WMS.Entitys;
|
using HH.WMS.Entitys.Common;
|
using System;
|
using System.Collections.Generic;
|
using System.Linq;
|
using System.Text;
|
using System.Threading.Tasks;
|
using Newtonsoft.Json;
|
using HH.WMS.Entitys.Basic;
|
using System.Threading;
|
using HH.WMS.Common;
|
using HH.WMS.BLL;
|
using HH.WMS.BLL.CoreServer;
|
using HH.WMS.Entitys.Sys;
|
using HH.WMS.BLL.OutStock;
|
using HH.WMS.BLL.Algorithm;
|
using HH.WMS.Common.Algorithm;
|
using HH.WMS.Common.Algorithm.Out;
|
using HH.WMS.BLL.Common;
|
|
namespace HH.WMS.CoreServer.Service
|
{
|
public class WaveService
|
{
|
#region 线程
|
//内销线程
|
public Thread thNxCreateWave = null;
|
//外销线程
|
public Thread thWxCreateWave = null;
|
//执行分拣单线程
|
public Thread thExecuteSorting = null;
|
//执行分拣出,盘点出的任务
|
public Thread thExecuteTask = null;
|
#endregion
|
|
#region 开始
|
/// <summary>
|
/// 开始
|
/// </summary>
|
public void Start()
|
{
|
|
|
|
|
#region 执行分拣单
|
//执行分拣单
|
thExecuteSorting = thExecuteSorting ?? new Thread(() =>
|
{
|
DoRepeatAction(ExecuteSorting);
|
});
|
thExecuteSorting.IsBackground = true;
|
thExecuteSorting.Start();
|
#endregion
|
|
#region 执行任务
|
//执行任务
|
thExecuteTask = thExecuteTask ?? new Thread(() =>
|
{
|
DoRepeatAction(ExecuteTask);
|
});
|
thExecuteTask.IsBackground = true;
|
thExecuteTask.Start();
|
#endregion
|
}
|
#endregion
|
|
#region 重复执行一个action
|
/// <summary>
|
/// 重复执行一个action
|
/// </summary>
|
/// <param name="action"></param>
|
private void DoRepeatAction(Func<OperateResult> func)
|
{
|
while (true)
|
{
|
try
|
{
|
var result = func();
|
if (!result.Success)
|
{
|
Log.Info("error", result.Msg);
|
}
|
}
|
catch (Exception ex)
|
{
|
Log.Info("error", ex.Message);
|
}
|
finally
|
{
|
//每次执行完休眠2秒钟
|
Thread.Sleep(2000);
|
}
|
}
|
}
|
#endregion
|
|
#region 执行分拣单,生成分拣明细
|
/// <summary>
|
/// 执行分拣单,生成分拣明细
|
/// </summary>
|
/// <returns></returns>
|
public OperateResult ExecuteSorting()
|
{
|
//获取待分拣分拣单
|
var staySortings = BLLCreator.CreateDapper<TN_WM_SORTING_LISTEntity>().GetTopList(5, new
|
{
|
CN_S_STATE = Constants.Sorting_Stay
|
});
|
//分拣单子表
|
var staySortingDtl = new List<TN_WM_SORTING_DTLEntity>();
|
if (staySortings.Any())
|
{
|
//获取分拣单子表
|
staySortingDtl = BLLCreator.CreateDapper<TN_WM_SORTING_DTLEntity>().GetList(new
|
{
|
CN_S_SORTING_NO = staySortings.Select(s => s.CN_S_SORTING_NO).ToList()
|
});
|
}
|
//逐个执行,单个失败不会return
|
foreach (var staySorting in staySortings)
|
{
|
staySorting.SortingDtlList = staySortingDtl.FindAll(f => f.CN_S_SORTING_NO == staySorting.CN_S_SORTING_NO);
|
//调用算法获取分拣明细
|
var sortingLocationResult = GetSortingLocation(staySorting);
|
if (!sortingLocationResult.Success)
|
{
|
//消息
|
Log.Info("分拣单获取分拣明细失败:", JsonConvert.SerializeObject(sortingLocationResult));
|
continue;
|
}
|
//分拣明细
|
var sortingLocations = sortingLocationResult.GetData<List<TN_WM_SORTING_LOCATIONEntity>>();
|
staySorting.SortingLocationList.AddRange(sortingLocations);
|
|
|
var currentArea = BLLCreator.Create<MongoBLL>().GetArea(staySorting.CN_S_STOCK_AREA);
|
//自动库时生成任务
|
if (currentArea.CN_C_IS_AUTO == Constants.Y)
|
{
|
//生成任务
|
List<string> endAreas = new List<string>();
|
var outTaskResult = GetOutTask(staySorting, ref endAreas);
|
if (!outTaskResult.Success)
|
{
|
Log.Info("分拣单生成出库任务失败:", JsonConvert.SerializeObject(outTaskResult));
|
continue;
|
}
|
var tasks = outTaskResult.GetData<List<TN_WM_TASKEntity>>();
|
staySorting.Tasks = tasks;
|
staySorting.EndBits = endAreas;
|
}
|
|
var executeResult = BLLCreator.Create<WaveServiceBLL>().ExecuteSorting(staySorting);
|
if (!executeResult.Success)
|
{
|
//消息
|
Log.Info("执行分拣executeResult失败:", JsonConvert.SerializeObject(executeResult));
|
continue;
|
}
|
}
|
return OperateResult.Succeed();
|
}
|
#endregion
|
|
#region 执行任务
|
/// <summary>
|
/// 执行任务
|
/// </summary>
|
/// <returns></returns>
|
public OperateResult ExecuteTask()
|
{
|
var noExecuteTask = BLLCreator.CreateDapper<TN_WM_TASKEntity>().GetList(new
|
{
|
CN_B_SENDAMS = false,
|
CN_S_TASK_TYPE = new List<string>() { Constants.TaskType_SortingOut, Constants.TaskType_CheckOut },
|
}).OrderBy(o=>o.CN_T_CREATE);
|
foreach (var task in noExecuteTask)
|
{
|
//需要调作业区入库算法,算出货位给出库任务
|
var inAssignEntity = new InAssignEntity
|
{
|
objectType = InAssignEntity.ObjectType.托盘,
|
objectCode = task.CN_S_TRAY_CODE,
|
lockLocation = false,
|
lstAreaPrior = new List<areaPriorClass>()
|
{
|
{
|
new areaPriorClass{
|
areaCode = task.CN_S_END_AREA,
|
Prior = 1
|
}
|
}
|
}
|
};
|
Log.Info("ExecuteTask调用算法InAssign参数", JsonConvert.SerializeObject(inAssignEntity));
|
InAssignResultEntity inAssignResultEntity = BLLCreator.Create<In_AlgorBLL>().InAssign(inAssignEntity);
|
Log.Info("ExecuteTask调用算法InAssign结果", JsonConvert.SerializeObject(inAssignResultEntity));
|
if (!inAssignResultEntity.Success) continue;
|
|
//当前分拣单库区
|
task.EndAreaEntity = BLLCreator.Create<MongoBLL>().GetArea(task.CN_S_END_AREA);
|
|
task.CN_S_END_BIT = inAssignResultEntity.locationCode;
|
var executeTaskResult = BLLCreator.Create<WaveServiceBLL>().ExecuteTask(task);
|
if (!executeTaskResult.Success)
|
Log.Info("executeTaskResult Msg", executeTaskResult.Msg);
|
}
|
return OperateResult.Succeed();
|
}
|
#endregion
|
|
#region 获取任务
|
/// <summary>
|
/// 获取任务
|
/// </summary>
|
/// <param name="sortingEntity"></param>
|
/// <param name="endAreas"></param>
|
/// <returns></returns>
|
public OperateResult GetOutTask(TN_WM_SORTING_LISTEntity sortingEntity, ref List<string> endAreas)
|
{
|
try
|
{
|
//当前分拣单库区
|
var currentArea = BLLCreator.Create<MongoBLL>().GetArea(sortingEntity.CN_S_STOCK_AREA);
|
if (currentArea == null)
|
return OperateResult.Error("未找到库区:" + sortingEntity.CN_S_STOCK_AREA);
|
//托盘货位分组
|
var trayGroup = sortingEntity.SortingLocationList.GroupBy(g => new
|
{
|
g.CN_S_SORTING_NO,
|
g.CN_S_TRAY_CODE,
|
g.CN_S_LOCATION_CODE
|
}).Select(s => new
|
{
|
s.Key.CN_S_SORTING_NO,
|
s.Key.CN_S_TRAY_CODE,
|
s.Key.CN_S_LOCATION_CODE,
|
CN_F_QUANTITY = s.Sum(m => m.CN_F_QUANTITY)
|
});
|
//整托托盘
|
List<string> fullTrayCode = new List<string>();
|
//托盘区的需要考虑整托
|
if (currentArea.CN_S_AREA_NAME == Constants.TrayArea)
|
{
|
//托盘数量汇总
|
var trayQtyGroup = trayGroup.GroupBy(g => g.CN_S_TRAY_CODE).Select(s => new
|
{
|
CN_S_TRAY_CODE = s.Key,
|
CN_F_QUANTITY = s.Sum(m => m.CN_F_QUANTITY)
|
});
|
//所有托盘物料关联明细
|
var trayItems = BLLCreator.CreateDapper<TN_WM_B_TRAY_ITEM_MSTEntity>().GetList(new
|
{
|
CN_S_TRAY_CODE = trayQtyGroup.Select(s => s.CN_S_TRAY_CODE).ToList()
|
});
|
//比较算法返回的托盘物料数量与明细数量是否相等(整托)
|
foreach (var tray in trayQtyGroup)
|
{
|
//整托
|
if (tray.CN_F_QUANTITY ==
|
trayItems.Where(s => s.CN_S_TRAY_CODE == tray.CN_S_TRAY_CODE)
|
.Sum(m => m.CN_F_QUANTITY))
|
{
|
fullTrayCode.Add(tray.CN_S_TRAY_CODE);
|
}
|
}
|
}
|
List<TN_WM_TASKEntity> taskList = new List<TN_WM_TASKEntity>();
|
//存在非整托货位托盘
|
var pickTrays = trayGroup.Where(s => !fullTrayCode.Contains(s.CN_S_TRAY_CODE))
|
.Select(e => new TN_WM_SORTING_LOCATIONEntity()
|
{
|
CN_S_SORTING_NO = e.CN_S_SORTING_NO,
|
CN_S_TRAY_CODE = e.CN_S_TRAY_CODE,
|
CN_S_LOCATION_CODE = e.CN_S_LOCATION_CODE,
|
}).ToList();
|
if (pickTrays.Any())
|
{
|
//非整托任务
|
WorkflowEntity workflowEntity = new WorkflowEntity(Constants.Workflow_Out)
|
{
|
CirObj = Constants.Tray,
|
StartArea = currentArea.CN_S_AREA_CODE,
|
ReturnList = true
|
};
|
//找站点 GetWorkPosition
|
var trayWorkflow = BLLCreator.Create<WorkflowBLL>().GetWorkPosition(workflowEntity);
|
if (!trayWorkflow.Success) return trayWorkflow;
|
var workAreaPro = trayWorkflow.GetData<List<TN_WM_WORK_AREA_PROEntity>>();
|
//分拣出库任务
|
var pickTasks = GetTaskEntity(workAreaPro[0], pickTrays);
|
endAreas.AddRange(workAreaPro.Select(s => s.CN_S_END_AREA_CODE).ToList());
|
taskList.AddRange(pickTasks);
|
}
|
//存在非整托货位托盘
|
var fullTrays = trayGroup.Where(s => fullTrayCode.Contains(s.CN_S_TRAY_CODE))
|
.Select(e => new TN_WM_SORTING_LOCATIONEntity()
|
{
|
CN_S_SORTING_NO = e.CN_S_SORTING_NO,
|
CN_S_TRAY_CODE = e.CN_S_TRAY_CODE,
|
CN_S_LOCATION_CODE = e.CN_S_LOCATION_CODE,
|
}).ToList();
|
if (fullTrays.Any())
|
{
|
//整托任务
|
WorkflowEntity workflowEntity = new WorkflowEntity(Constants.Workflow_Out)
|
{
|
CirObj = Constants.Tray,
|
StartArea = currentArea.CN_S_AREA_CODE,
|
IsFullTray = Constants.Y,
|
};
|
//找站点 GetWorkPosition
|
var trayWorkflow = BLLCreator.Create<WorkflowBLL>().GetWorkPosition(workflowEntity);
|
if (!trayWorkflow.Success) return trayWorkflow;
|
var workAreaPro = trayWorkflow.GetData<TN_WM_WORK_AREA_PROEntity>();
|
|
//需要调作业区入库算法,算出货位给出库任务
|
var inAssignEntity = new InAssignEntity
|
{
|
objectType = InAssignEntity.ObjectType.托盘,
|
objectCode = fullTrays[0].CN_S_TRAY_CODE,
|
lockLocation = false,
|
lstAreaPrior = new List<areaPriorClass>()
|
{
|
{
|
new areaPriorClass{
|
areaCode = workAreaPro.CN_S_END_AREA_CODE,
|
Prior = 1
|
}
|
}
|
}
|
};
|
Log.Info("调用算法InAssign参数", JsonConvert.SerializeObject(inAssignEntity));
|
InAssignResultEntity inAssignResultEntity = BLLCreator.Create<In_AlgorBLL>().InAssign(inAssignEntity);
|
Log.Info("调用算法InAssign结果", JsonConvert.SerializeObject(inAssignResultEntity));
|
if (!inAssignResultEntity.Success) return OperateResult.Error(inAssignResultEntity.Msg);
|
//整托出库任务
|
var fullTasks = GetTaskEntity(workAreaPro, pickTrays, inAssignResultEntity.locationCode);
|
|
taskList.AddRange(fullTasks);
|
}
|
return OperateResult.Succeed(null, taskList);
|
}
|
catch (Exception ex)
|
{
|
return OperateResult.Error(ex.Message);
|
}
|
}
|
#endregion
|
|
#region 获取任务实体
|
/// <summary>
|
/// 获取任务实体
|
/// </summary>
|
/// <param name="workArea"></param>
|
/// <param name="sortingEntity"></param>
|
/// <returns></returns>
|
private List<TN_WM_TASKEntity> GetTaskEntity(TN_WM_WORK_AREA_PROEntity workArea, List<TN_WM_SORTING_LOCATIONEntity> sortingLocations, string endBit = "")
|
{
|
var trayLocationGroup = sortingLocations.GroupBy(g => new
|
{
|
g.CN_S_TRAY_CODE,
|
g.CN_S_LOCATION_CODE,
|
g.CN_S_SORTING_NO
|
}).Select(s => new
|
{
|
s.Key.CN_S_TRAY_CODE,
|
s.Key.CN_S_LOCATION_CODE,
|
s.Key.CN_S_SORTING_NO
|
});
|
List<TN_WM_TASKEntity> taskList = new List<TN_WM_TASKEntity>();
|
foreach (var task in trayLocationGroup)
|
{
|
string postData = "{\"appCode\":\"" + Constants.appCode + "\",\"ruleName\":\"" + Constants.Rule_TaskNo + "\",\"orgId\":\"\",\"orgFlag\":\"\"}";
|
string taskNo = WebApiManager.HttpAutoBom_Post("api/BillRule/GenBillNo", postData);
|
|
TN_WM_TASKEntity taskEntity = new TN_WM_TASKEntity()
|
{
|
CN_S_TASK_NO = taskNo,
|
CN_S_TASK_TYPE = Constants.TaskType_SortingOut,
|
CN_S_FROM_OP = Constants.Out,
|
CN_S_FROM_NO = task.CN_S_SORTING_NO,
|
CN_S_TRAY_CODE = task.CN_S_TRAY_CODE,
|
CN_S_STOCK_CODE = "",
|
CN_S_START_AREA = workArea.CN_S_START_AREA_CODE,
|
CN_S_START_BIT = task.CN_S_LOCATION_CODE,
|
CN_S_END_AREA = workArea.CN_S_END_AREA_CODE,
|
CN_S_END_BIT = endBit,
|
CN_S_STATE = Constants.TaskState_NoExecuted,
|
CN_T_CREATE = DateTime.Now,
|
CN_T_MODIFY = DateTime.Now,
|
EndAreaEntity = BLLCreator.Create<MongoBLL>().GetArea(workArea.CN_S_END_AREA_CODE)
|
};
|
taskList.Add(taskEntity);
|
}
|
return taskList;
|
}
|
#endregion
|
|
#region 是否在上班时间
|
/// <summary>
|
/// 是否在上班时间
|
/// </summary>
|
/// <returns></returns>
|
private WaveConfig WaveConfig()
|
{
|
//没加仓库条件
|
var waveStrategyValues = BLLCreator.CreateDapper<TN_WM_B_STRATEGY_VALUEEntity>().GetList(new
|
{
|
CN_S_CODE = new List<string>
|
{
|
Constants.Strategy_StartTime,
|
Constants.Strategy_EndTime,
|
Constants.Strategy_SeveralMaxCreateWave,
|
Constants.Strategy_LessMinute
|
}
|
});
|
WaveConfig waveConfig = new WaveConfig();
|
//上班时间
|
var startTime = waveStrategyValues.Find(f => f.CN_S_CODE == Constants.Strategy_StartTime);
|
string _startTime = startTime != null ? startTime.CN_S_VALUE : Constants.WaveDefault_StartTime;
|
_startTime = string.IsNullOrEmpty(_startTime) ? Constants.WaveDefault_StartTime : _startTime;
|
waveConfig.StartTime = DateTime.Parse(_startTime);
|
//下班时间
|
var endTime = waveStrategyValues.Find(f => f.CN_S_CODE == Constants.Strategy_EndTime);
|
string _endTime = endTime != null ? endTime.CN_S_VALUE : Constants.WaveDefault_EndTime;
|
_endTime = string.IsNullOrEmpty(_endTime) ? Constants.WaveDefault_EndTime : _endTime;
|
waveConfig.EndTime = DateTime.Parse(_endTime);
|
//内销几单一波
|
var severalMaxCreateWave = waveStrategyValues.Find(f => f.CN_S_CODE == Constants.Strategy_SeveralMaxCreateWave);
|
string _severalMaxCreateWave = severalMaxCreateWave != null ? severalMaxCreateWave.CN_S_VALUE : Constants.WaveDefault_SeveralMaxCreateWave;
|
_severalMaxCreateWave = string.IsNullOrEmpty(_severalMaxCreateWave) ? Constants.WaveDefault_SeveralMaxCreateWave : _severalMaxCreateWave;
|
waveConfig.SeveralMaxCreateWave = int.Parse(_severalMaxCreateWave);
|
//内销几分钟后生成
|
var lessMinute = waveStrategyValues.Find(f => f.CN_S_CODE == Constants.Strategy_LessMinute);
|
string _lessMinute = lessMinute != null ? lessMinute.CN_S_VALUE : Constants.WaveDefault_LessMinute;
|
_lessMinute = string.IsNullOrEmpty(_lessMinute) ? Constants.WaveDefault_LessMinute : _lessMinute;
|
waveConfig.LessMinute = int.Parse(_lessMinute);
|
//result
|
return waveConfig;
|
}
|
#endregion
|
|
#region 是否在上班时间
|
/// <summary>
|
/// 是否在上班时间
|
/// </summary>
|
/// <param name="waveConfig"></param>
|
/// <returns></returns>
|
private bool IsWorkTime(WaveConfig waveConfig)
|
{
|
try
|
{
|
TimeSpan dspWorkingDayAM = waveConfig.StartTime.TimeOfDay;
|
TimeSpan dspWorkingDayPM = waveConfig.EndTime.TimeOfDay;
|
TimeSpan dspNow = DateTime.Now.TimeOfDay;
|
if (dspNow >= dspWorkingDayAM && dspNow <= dspWorkingDayPM)
|
{
|
return true;
|
}
|
return false;
|
}
|
catch
|
{
|
return false;
|
}
|
}
|
#endregion
|
|
#region 创建波次分拣单
|
/// <summary>
|
/// 创建波次分拣单
|
/// </summary>
|
/// <param name="outList"></param>
|
/// <param name="isNX">是否内销</param>
|
/// <returns></returns>
|
public OperateResult WaveCreate(List<TN_WM_OUT_MSTEntity> outList)
|
{
|
//登陆信息&生成单号
|
UserRuleEntity user = new UserRuleEntity()
|
{
|
RuleCode = Constants.Rule_WaveNo
|
};
|
//生成波次号
|
string waveNo = user.GenerateNo();
|
|
return ChangeOutOperate(outList, (outMstList, batchesList, stockList, lstStrate) =>
|
{
|
|
//批分分配量
|
var bResult = stockList.BatchesStockAllocQty(batchesList, lstStrate);
|
if (!bResult.Success)
|
return bResult;
|
//批分结果数据
|
var batchesResult = bResult.GetData<BatchesResult>();
|
if (batchesResult == null)
|
return OperateResult.Error("生成打单数据异常");
|
//算库区
|
OperateResult outAreaResult = OutArea(batchesResult.OutMstList);
|
if (!outAreaResult.Success) return outAreaResult;
|
//算法库区返回结果
|
var algorResultEntity = outAreaResult.GetData<OutAreaResultAllEntity>();
|
Log.Info("WaveCreate 调用OutArea返回值algorResultEntity", JsonConvert.SerializeObject(algorResultEntity));
|
//算法库区返回结果
|
if (!algorResultEntity.Success)
|
return OperateResult.Error("算法异常:" + algorResultEntity.Msg);
|
//算法结果
|
var algorResult = algorResultEntity.itemOutAreaResult;
|
//算法结果
|
if (algorResult == null)
|
return OperateResult.Error("库存不足,请先完成来料区的上架");
|
//获取分拣单数据
|
var sortingDataResult = GetSortingData(algorResult, user, waveNo);
|
if (!sortingDataResult.Success)
|
return sortingDataResult;
|
//生成的分拣单数据
|
var sortingList = sortingDataResult.GetData<List<TN_WM_SORTING_LISTEntity>>();
|
List<string> itemCodes = new List<string>();
|
sortingList.ForEach(x =>
|
{
|
if (x.SortingDtlList != null)
|
x.SortingDtlList.ForEach(y =>
|
{
|
itemCodes.Add(y.CN_S_ITEM_CODE);
|
});
|
});
|
//分拣单的所有库区量表数据
|
var stockAreaList = BLLCreator.Create<DapperBLL<TN_WM_B_AREA_QTYEntity>>().GetList(new
|
{
|
CN_S_STOCK_AREA = sortingList.Select(x => x.CN_S_STOCK_AREA).ToList(),
|
CN_S_ITEM_CODE = itemCodes
|
});
|
|
Log.Info("批分库区参数 sortingList:", JsonConvert.SerializeObject(sortingList));
|
Log.Info("批分库区参数 lstStrate:", JsonConvert.SerializeObject(lstStrate));
|
//批分库区量表
|
var batchesAreaResult = stockAreaList.BatchesAreaQty(sortingList, lstStrate);
|
if (!batchesAreaResult.Success)
|
return batchesAreaResult;
|
Log.Info("批分库区结果 batchesAreaResult:", JsonConvert.SerializeObject(batchesAreaResult));
|
|
//波次实体
|
batchesResult.WaveMst = GetWaveEntity(batchesResult, waveNo);
|
if (batchesResult.ShortageOutList.Any())
|
{
|
//新的发货单赋值波次号
|
batchesResult.ShortageOutList.ForEach(f =>
|
{
|
if (f.CN_S_STATE != Constants.State_Shortage)
|
f.CN_S_WAVE_CODE = batchesResult.WaveMst.CN_S_WAVE_NO;
|
});
|
}
|
//分拣单实体
|
batchesResult.SortingList = sortingList;
|
//库区量表实体
|
batchesResult.StockAreaList = stockAreaList;
|
//创建波次分拣单
|
return BLLCreator.Create<WaveServiceBLL>().WaveCreate(batchesResult);
|
});
|
}
|
#endregion
|
|
#region 获取分拣单数据
|
/// <summary>
|
/// 获取分拣单数据
|
/// </summary>
|
/// <param name="outAreaResultList"></param>
|
/// <param name="user"></param>
|
/// <param name="waveNo"></param>
|
/// <returns></returns>
|
public OperateResult GetSortingData(List<OutAreaResultEntity> outAreaResultList, UserRuleEntity user, string waveNo)
|
{
|
try
|
{
|
//分拣单
|
List<TN_WM_SORTING_LISTEntity> sortingList = new List<TN_WM_SORTING_LISTEntity>();
|
//分拣单组别
|
user.RuleCode = Constants.SortingGroup;
|
string sortingGroup = user.GenerateNo();
|
//分拣单号
|
user.RuleCode = Constants.SortingNo;
|
int sortingIndex = 1;
|
IEnumerable<IGrouping<dynamic, OutAreaResultEntity>> igOutAreaResult = outAreaResultList.GroupBy(x => new { x.stockCode, x.areaCode });
|
foreach (var _igOutAreaResult in igOutAreaResult)
|
{
|
//分拣单
|
TN_WM_SORTING_LISTEntity sortingEntity = new TN_WM_SORTING_LISTEntity()
|
{
|
CN_GUID = Guid.NewGuid().ToString(),
|
CN_S_SORTING_NO = waveNo + "-" + sortingIndex++,
|
CN_S_OP_FROM = Constants.Rule_WaveNo,
|
CN_S_FROM_NO = waveNo,
|
CN_T_OPERATE = DateTime.Now.ToString(),// "", //丢里咯木
|
CN_S_STOCK_CODE = _igOutAreaResult.Key.stockCode,
|
CN_S_STOCK_AREA = _igOutAreaResult.Key.areaCode,
|
CN_S_STATE = Constants.Sorting_Stay,
|
CN_S_SEEDING_MODE = "",
|
CN_S_GROUP = sortingGroup,
|
CN_S_CREATOR = user.LoginCode,
|
CN_S_CREATOR_BY = user.LoginName,
|
CN_T_CREATE = DateTime.Now,
|
CN_S_MODIFY = user.LoginCode,
|
CN_S_MODIFY_BY = user.LoginName,
|
CN_T_MODIFY = DateTime.Now
|
};
|
//分拣单子表行索引
|
int sortingDtlindex = 1;
|
//分拣单子表
|
sortingEntity.SortingDtlList = new List<TN_WM_SORTING_DTLEntity>();
|
//分拣明细
|
sortingEntity.SortingLocationList = new List<TN_WM_SORTING_LOCATIONEntity>();
|
foreach (var outArea in _igOutAreaResult.AsEnumerable())
|
{
|
foreach (var d in outArea.lstItem)
|
{
|
//物料详细
|
AutoBomItemEntity item = BLLCreator.Create<MongoBLL>().GetItemEntity(d.itemCode) ?? new AutoBomItemEntity();
|
//分拣单子行
|
TN_WM_SORTING_DTLEntity sortingDtlEntity = new TN_WM_SORTING_DTLEntity()
|
{
|
CN_GUID = Guid.NewGuid().ToString(),
|
CN_S_SORTING_NO = sortingEntity.CN_S_SORTING_NO,
|
CN_N_ROW_NO = sortingDtlindex++,
|
CN_F_QUANTITY = d.itemQty,
|
CN_S_ITEM_CODE = d.itemCode,
|
CN_S_ITEM_NAME = item.CN_S_ITEM_NAME,
|
CN_S_STATE = Constants.Sorting_Stay,
|
CN_S_LOT_NO = outArea.batchCode,
|
CN_S_PRODUCTION_BATCH = outArea.prodBatchCode,
|
CN_S_ITEM_STATE = outArea.itemState,
|
CN_S_OWNER = outArea.ownerName,
|
CN_S_MODEL = item.CN_S_MODEL,
|
CN_S_FIGURE_NO = item.CN_S_FIGURE_NO,
|
CN_S_MEASURE_UNIT = item.CN_S_MEASURE_UNIT
|
};
|
sortingEntity.SortingDtlList.Add(sortingDtlEntity);
|
}
|
}
|
sortingList.Add(sortingEntity);
|
}
|
return OperateResult.Succeed(null, sortingList);
|
}
|
catch (Exception ex)
|
{
|
return OperateResult.Error(ex.Message);
|
}
|
}
|
#endregion
|
|
#region 获取波次实体
|
/// <summary>
|
/// 获取波次实体
|
/// </summary>
|
/// <param name="bResult"></param>
|
/// <returns></returns>
|
private TN_WM_WAVE_MSTEntity GetWaveEntity(BatchesResult bResult, string waveNo)
|
{
|
TN_WM_WAVE_MSTEntity waveMstEntity = new TN_WM_WAVE_MSTEntity()
|
{
|
CN_S_CREATE_TYPE = Constants.CreateType_Manual,
|
CN_S_WAVE_NO = waveNo,
|
CN_S_FROM = "WMS",
|
CN_GUID = Guid.NewGuid().ToString(),
|
CN_S_STATE = Constants.Sorting_Stay,
|
CN_T_CREATE = DateTime.Now,
|
CN_T_MODIFY = DateTime.Now,
|
CN_T_OPERATE = DateTime.Now,
|
CN_S_CREATOR = "自动",
|
CN_S_CREATOR_BY = "auto",
|
CN_S_MODIFY = "自动",
|
CN_S_MODIFY_BY = "auto",
|
CN_C_NEED_SEEDING = Constants.Y,
|
CN_S_STATUS = Constants.State_New,
|
CN_S_SEEDING_MODE = "",
|
CN_S_STOCK_CODE = bResult.OutMstList[0].CN_S_STOCK_CODE
|
};
|
waveMstEntity.WaveDtlList = new List<TN_WM_WAVE_DTLEntity>();
|
int index = 1;
|
foreach (var outDtl in bResult.OutMstList)
|
{
|
TN_WM_WAVE_DTLEntity waveDtlEntity = new TN_WM_WAVE_DTLEntity()
|
{
|
CN_GUID = Guid.NewGuid().ToString(),
|
CN_S_WAVE_NO = waveNo,
|
CN_S_ORDER_NO = outDtl.CN_S_OP_NO,
|
CN_S_STATE = Constants.State_New,
|
CN_T_CREATE = DateTime.Now,
|
CN_T_MODIFY = DateTime.Now,
|
CN_S_CREATOR = "自动",
|
CN_S_CREATOR_BY = "auto",
|
CN_S_MODIFY = "自动",
|
CN_S_MODIFY_BY = "auto",
|
CN_S_MSTGUID = waveMstEntity.CN_GUID,
|
CN_N_ORDER = index++
|
};
|
waveMstEntity.WaveDtlList.Add(waveDtlEntity);
|
}
|
return waveMstEntity;
|
}
|
#endregion
|
|
#region 获取分拣明细
|
/// <summary>
|
///获取分拣明细
|
/// </summary>
|
/// <param name="sortingEntity"></param>
|
/// <param name="user"></param>
|
/// <returns></returns>
|
private OperateResult GetSortingLocation(TN_WM_SORTING_LISTEntity sortingEntity)
|
{
|
try
|
{
|
//所有库区
|
var allAreas = BLLCreator.Create<MongoBLL>().GetArea();
|
//所有自动库区
|
var autoAreas = allAreas.FindAll(f => f.CN_C_IS_AUTO == Constants.Y);
|
//当前库区
|
var currentArea = allAreas.Find(f => f.CN_S_AREA_CODE == sortingEntity.CN_S_STOCK_AREA);
|
if (currentArea == null)
|
{
|
//未找到库区
|
string notFoundArea = "分拣单:{0}的库区:{1}在Mongo中未找到";
|
notFoundArea = string.Format(notFoundArea,
|
sortingEntity.CN_S_SORTING_NO,
|
sortingEntity.CN_S_STOCK_AREA);
|
return OperateResult.Error(notFoundArea);
|
}
|
//自动库分拣处理
|
if (currentArea.CN_C_IS_AUTO == Constants.Y)
|
{
|
if (currentArea.CN_S_CURRENT_MODE != Constants.CurrentMode_Out)
|
{
|
//当前库区不为出库模式,不允许生成明细
|
string notOutMode = "库区:{0}的模式为:{1},不允许执行分拣,请切换到:{2}";
|
notOutMode = string.Format(notOutMode,
|
currentArea.CN_S_AREA_CODE,
|
currentArea.CN_S_CURRENT_MODE,
|
Constants.CurrentMode_Out);
|
return OperateResult.Error(notOutMode);
|
}
|
// if (currentArea.CN_N_PICK_MAX > 0)
|
if(true)
|
{
|
//当前库区分拣中的分拣单
|
var beingSorting = BLLCreator.CreateDapper<TN_WM_SORTING_LISTEntity>().GetList(new
|
{
|
CN_S_STOCK_AREA = sortingEntity.CN_S_STOCK_AREA,
|
CN_S_STATE = Constants.Sorting_Being
|
});
|
//当前库区分拣中的分拣单数达到最大数
|
//if (beingSorting.Count >= currentArea.CN_N_PICK_MAX)
|
if (beingSorting.Count >=0)
|
{
|
//错误消息
|
string pickMaxError = "库区:{0}中正在分拣的分拣单数已达到可同时分拣的最大数:{1},请先分拣:{2}";
|
//pickMaxError = string.Format(pickMaxError, sortingEntity.CN_S_STOCK_AREA,
|
// currentArea.CN_N_PICK_MAX,
|
// string.Join(",", beingSorting.Select(s => s.CN_S_SORTING_NO))
|
// );
|
return OperateResult.Error(pickMaxError);
|
}
|
}
|
//调算法获取明细
|
return AlgorithmLocation(sortingEntity);
|
}
|
else
|
{
|
//收货区和暂存区
|
if (currentArea.CN_S_AREA_CLASS == Constants.ReceivingArea || currentArea.CN_S_AREA_CLASS == Constants.TempStorageArea)
|
{
|
return OperateResult.Error("收货区和暂存区的分拣明细待开发");
|
}
|
else//大件和平库的处理
|
{
|
return OperateResult.Error("大件和平库的分拣明细待开发");
|
}
|
}
|
}
|
catch (Exception ex)
|
{
|
return OperateResult.Error(ex.Message);
|
}
|
}
|
#endregion
|
|
#region 算法获取明细
|
/// <summary>
|
/// 算法获取明细
|
/// </summary>
|
/// <param name="sortingEntity"></param>
|
/// <returns></returns>
|
private OperateResult AlgorithmLocation(TN_WM_SORTING_LISTEntity sortingEntity)
|
{
|
try
|
{
|
//分拣明细
|
List<TN_WM_SORTING_LOCATIONEntity> sortingLocationList = new List<TN_WM_SORTING_LOCATIONEntity>();
|
//调算法算货位
|
OperateResult outNewResult = OutNew(sortingEntity);
|
if (!outNewResult.Success) return outNewResult;
|
//算法结果
|
var outResult = outNewResult.GetData<OutResultEntityNew>();
|
//算法结果
|
if (outResult.lstItemNotEnough.Any())
|
{
|
outResult.lstItemNotEnough.ForEach(x =>
|
{
|
var currentNotEnough = sortingEntity.SortingDtlList.Find(s => s.CN_S_ITEM_CODE.Equals(x.itemCode));
|
if (currentNotEnough != null)
|
x.itemName = currentNotEnough.CN_S_ITEM_NAME;
|
});
|
return OperateResult.Warning("物料:" + string.Join(";", outResult.lstItemNotEnough.Select(x => x.itemCode)) + "库存不足,请先完成来料区的上架", outResult.lstItemNotEnough);
|
}
|
if (!outResult.Success)
|
return OperateResult.Error("算法异常:" + outResult.Msg);
|
|
if (sortingEntity.SortingDtlList.Sum(x => x.CN_F_QUANTITY) != Convert.ToDecimal(outResult.itemLocations.Sum(x => x.itemQty)))
|
{
|
return OperateResult.Error("算法异常:返回的物料信息不完整!");
|
}
|
var locations = outResult.itemLocations.Select(m =>
|
{
|
AutoBomItemEntity mm = BLLCreator.Create<MongoBLL>().GetItemEntity(m.itemCode) ?? new AutoBomItemEntity();
|
return new TN_WM_SORTING_LOCATIONEntity()
|
{
|
CN_GUID = Guid.NewGuid().ToString(),
|
CN_S_LOT_NO = m.batchCode,
|
CN_S_ITEM_STATE = m.itemState,
|
CN_S_OWNER = m.ownerName,
|
CN_F_QUANTITY = m.itemQty,
|
CN_F_PICKED_QTY = 0,
|
CN_S_ITEM_CODE = m.itemCode,
|
CN_S_LOCATION_CODE = m.locationCode,
|
CN_S_TRAY_CODE = m.trayCode,
|
CN_S_TRAY_GRID = m.trayGrid,
|
CN_S_SORTING_NO = sortingEntity.CN_S_SORTING_NO,
|
CN_S_STATE = sortingEntity.CN_S_STATE,
|
CN_S_ORDER_NO = "",
|
CN_S_CREATOR = "自动",
|
CN_S_CREATOR_BY = "auto",
|
CN_S_ITEM_NAME = mm.CN_S_ITEM_NAME,
|
CN_S_MODEL = mm.CN_S_MODEL,
|
CN_S_FIGURE_NO = mm.CN_S_FIGURE_NO,
|
CN_S_MEASURE_UNIT = mm.CN_S_MEASURE_UNIT,
|
CN_S_PACKING_UNIT = m.packUnit,
|
CN_F_PACKING_QTY = m.packQty,
|
CN_S_PRODUCTION_BATCH = m.prodBatchCode,
|
CN_T_CREATE = DateTime.Now,
|
CN_T_MODIFY = DateTime.Now
|
};
|
}).ToList();
|
sortingLocationList.AddRange(locations);
|
return OperateResult.Succeed(null, sortingLocationList);
|
}
|
catch (Exception ex)
|
{
|
return OperateResult.Error(ex.Message);
|
}
|
}
|
#endregion
|
|
#region 带出出库订单,批分,仓库实体
|
/// <summary>
|
/// 带出出库订单实体
|
/// </summary>
|
/// <param name="opNo"></param>
|
/// <param name="funcOut"></param>
|
/// <returns></returns>
|
public OperateResult ChangeOutOperate(List<TN_WM_OUT_MSTEntity> outMst, Func<List<TN_WM_OUT_MSTEntity>, List<BatchesEntity>, List<TN_WM_B_STOCK_QTYEntity>, List<string>, OperateResult> funcOut)
|
{
|
try
|
{
|
List<BatchesEntity> batchesList = new List<BatchesEntity>();
|
|
foreach (var _out in outMst)
|
{
|
|
_out.OutDtlList = BLLCreator.Create<DapperBLL<TN_WM_OUT_DTLEntity>>().GetList(new
|
{
|
CN_S_OP_NO = _out.CN_S_OP_NO,
|
CN_C_AUTO_INOUT = false
|
});
|
//.FindAll(a =>
|
//{
|
// if (_out.CN_S_OUT_TYPE == Constants.OutType_BigOut) return true;
|
// //物料详细
|
// AutoBomItemEntity item = BLLCreator.Create<MongoBLL>().GetItemEntity(a.CN_S_ITEM_CODE);
|
// if (item == null)
|
// throw new Exception("autobom中未找到物料:" + a.CN_S_ITEM_CODE);
|
// if (item.CN_S_STORE_TYPE == Constants.BigArea)
|
// a.CN_C_BIG = true;
|
// return true;
|
//});
|
batchesList.AddRange(_out.OutDtlList.Select(p =>
|
{
|
return new BatchesEntity()
|
{
|
ItemCode = p.CN_S_ITEM_CODE,
|
ItemState = p.CN_S_ITEM_STATE,
|
ItemArrivalLot = p.CN_S_LOT_CODE,
|
ItemProductionLot = p.CN_S_PRODUCTION_BATCH,
|
Owner = _out.CN_S_OWNER,
|
Qty = p.CN_F_QUANTITY,
|
OpNo = _out.CN_S_OP_NO,
|
OutMst = _out,
|
IsFilter = p.CN_C_AUTO_INOUT
|
};
|
}).ToList());
|
}
|
//当前子表物料库存
|
var stockList = BLLCreator.Create<DapperBLL<TN_WM_B_STOCK_QTYEntity>>().GetList(new
|
{
|
CN_S_ITEM_CODE = batchesList.Select(p => p.ItemCode).ToList()
|
});
|
//算法策略
|
List<string> lstStrate = BLLCreator.Create<TN_WM_B_STRATEGYBLL>().GetStrateListByAreaOrStock("", "", Constants.Out)
|
.OrderByDescending(a => a.CN_N_PRIORITY).Select(o => o.CN_S_CODE).ToList();
|
return funcOut(outMst, batchesList, stockList, lstStrate);
|
}
|
catch (Exception ex)
|
{
|
return OperateResult.Error(ex.Message);
|
}
|
}
|
#endregion
|
|
#region 算库区
|
/// <summary>
|
/// 算库区
|
/// </summary>
|
/// <param name="outMstList"></param>
|
/// <returns></returns>
|
private OperateResult OutArea(List<TN_WM_OUT_MSTEntity> outMstList)
|
{
|
try
|
{
|
//所有出库物料
|
var outItemList = new List<TN_WM_OUT_DTLEntity>();
|
outMstList.ForEach(p =>
|
{
|
//子表货主赋值
|
p.OutDtlList.ForEach(q => q.CN_S_OWNER = p.CN_S_OWNER);
|
outItemList.AddRange(p.OutDtlList);
|
});
|
//初始化调用算法数据
|
List<itemQueryClass> itemQuerys =
|
outItemList.GroupBy(p => new
|
{
|
p.CN_S_ITEM_CODE,
|
p.CN_S_ITEM_STATE,
|
p.CN_S_OWNER,
|
p.CN_S_LOT_CODE,
|
p.CN_S_PRODUCTION_BATCH
|
}).Select(x => new itemQueryClass()
|
{
|
itemCode = x.Key.CN_S_ITEM_CODE,
|
batchCode = x.Key.CN_S_LOT_CODE,
|
prodBatchCode = x.Key.CN_S_PRODUCTION_BATCH,
|
itemState = x.Key.CN_S_ITEM_STATE,
|
ownerName = x.Key.CN_S_OWNER,
|
stockCode = outMstList[0].CN_S_STOCK_CODE,
|
itemQty = x.Sum(y => y.CN_F_QUANTITY)
|
}).ToList();
|
//初始化调用算法库区
|
var allArea = BLLCreator.Create<MongoBLL>().GetArea();
|
List<areaPriorClass> lstAreaPrior = allArea.Select(p => new areaPriorClass()
|
{
|
areaCode = p.CN_S_AREA_CODE,
|
Prior = p.CN_N_PRIORITY
|
}).ToList();
|
|
Log.Info("调用OutArea参数 itemQuerys", JsonConvert.SerializeObject(itemQuerys));
|
Log.Info("调用OutArea参数 lstAreaPrior", JsonConvert.SerializeObject(lstAreaPrior));
|
//调用出库算法
|
string outAreaParams = JsonConvert.SerializeObject(new
|
{
|
itemQuerys = itemQuerys,
|
lstAreaPrior = lstAreaPrior
|
});
|
OutAreaResultAllEntity algorResultEntity = BLLCreator.Create<Out_AlgorBLL>().OutArea(itemQuerys, lstAreaPrior);
|
Log.Info("algorResultEntity", JsonConvert.SerializeObject(algorResultEntity));
|
|
return OperateResult.Succeed(null, algorResultEntity);
|
}
|
catch (Exception ex)
|
{
|
return OperateResult.Error("WaveService-OutArea:" + ex.Message);
|
}
|
}
|
#endregion
|
|
#region 算货位
|
/// <summary>
|
/// 算货位
|
/// </summary>
|
/// <param name="sortingEntity"></param>
|
/// <returns></returns>
|
private OperateResult OutNew(TN_WM_SORTING_LISTEntity sortingEntity)
|
{
|
try
|
{
|
//初始化调用算法数据
|
List<itemQueryClass> algorItems = sortingEntity.SortingDtlList.GroupBy(x => new
|
{
|
x.CN_S_ITEM_CODE,
|
x.CN_S_ITEM_STATE,
|
x.CN_S_OWNER,
|
x.CN_S_LOT_NO,
|
x.CN_S_PRODUCTION_BATCH
|
}).Select(y => new itemQueryClass()
|
{
|
stockCode = sortingEntity.CN_S_STOCK_CODE,
|
areaCode = sortingEntity.CN_S_STOCK_AREA,
|
itemCode = y.Key.CN_S_ITEM_CODE,
|
batchCode = y.Key.CN_S_LOT_NO,
|
prodBatchCode = y.Key.CN_S_PRODUCTION_BATCH,
|
itemState = y.Key.CN_S_ITEM_STATE,
|
ownerName = y.Key.CN_S_OWNER,
|
itemQty = y.Sum(z => z.CN_F_QUANTITY)
|
}).ToList();
|
var lstAreaPrior = algorItems.Select(x => new areaPriorClass()
|
{
|
areaCode = x.areaCode,
|
Prior = 1
|
}).ToList();
|
OutAlgorEnitty outNewParams = new OutAlgorEnitty
|
{
|
lstDevice = new List<Device>(),
|
lstAreaPrior = lstAreaPrior,
|
lstQueryItem = algorItems,
|
lockLocation = false
|
};
|
Log.Info("调用OutNew参数 outNewParams", JsonConvert.SerializeObject(outNewParams));
|
|
OutResultEntityNew outResult = BLLCreator.Create<Out_AlgorBLL>().OutNew(outNewParams);
|
|
Log.Info("调用OutNew返回值 outResult", JsonConvert.SerializeObject(outResult));
|
return OperateResult.Succeed(null, outResult);
|
}
|
catch (Exception ex)
|
{
|
return OperateResult.Error("WaveService-OutNew:" + ex.Message);
|
}
|
}
|
#endregion
|
}
|
|
#region 波次配置
|
/// <summary>
|
/// 波次配置
|
/// </summary>
|
public class WaveConfig
|
{
|
public DateTime StartTime { get; set; }
|
public DateTime EndTime { get; set; }
|
public int SeveralMaxCreateWave { get; set; }
|
public int LessMinute { get; set; }
|
}
|
#endregion
|
}
|