|
|
@@ -8,11 +8,14 @@ using Business.StructuredDB.Production;
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
using System.Collections.Immutable;
|
|
|
+using System.Configuration;
|
|
|
using System.Linq;
|
|
|
using System.Threading.Tasks;
|
|
|
using System.Transactions;
|
|
|
using Volo.Abp.Application.Services;
|
|
|
using Volo.Abp.MultiTenancy;
|
|
|
+using Microsoft.Extensions.Configuration;
|
|
|
+using MongoDB.Driver.Linq;
|
|
|
|
|
|
namespace Business.ResourceExamineManagement
|
|
|
{
|
|
|
@@ -80,6 +83,10 @@ namespace Business.ResourceExamineManagement
|
|
|
/// </summary>
|
|
|
private ISqlRepository<ResourceOccupancyTime> _resourceOccupancyTime;
|
|
|
/// <summary>
|
|
|
+ /// 加班设置表
|
|
|
+ /// </summary>
|
|
|
+ private ISqlRepository<GeneralizedCodeMaster> _generalizedCodeMaster;
|
|
|
+ /// <summary>
|
|
|
/// 节假日记录表
|
|
|
/// </summary>
|
|
|
private ISqlRepository<HolidayMaster> _holidayMaster;
|
|
|
@@ -130,6 +137,7 @@ namespace Business.ResourceExamineManagement
|
|
|
ISqlRepository<ShopCalendarWorkCtr> shopCalendarWorkCtr,
|
|
|
ISqlRepository<QualityLineWorkDetail> qualityLineWorkDetail,
|
|
|
ISqlRepository<HolidayMaster> holidayMaster,
|
|
|
+ ISqlRepository<GeneralizedCodeMaster> generalizedCodeMaster,
|
|
|
ICurrentTenant currentTenant
|
|
|
)
|
|
|
{
|
|
|
@@ -146,6 +154,7 @@ namespace Business.ResourceExamineManagement
|
|
|
_shopCalendarWorkCtr = shopCalendarWorkCtr;
|
|
|
_qualityLineWorkDetail = qualityLineWorkDetail;
|
|
|
_holidayMaster = holidayMaster;
|
|
|
+ _generalizedCodeMaster = generalizedCodeMaster;
|
|
|
_currentTenant = currentTenant;
|
|
|
}
|
|
|
#endregion
|
|
|
@@ -155,8 +164,21 @@ namespace Business.ResourceExamineManagement
|
|
|
/// </summary>
|
|
|
public async void DoExt()
|
|
|
{
|
|
|
- List<WorkOrdMaster> workOrds = new List<WorkOrdMaster>();
|
|
|
- domain = "1001";//定时任务跑资源检查,需要定义工厂id
|
|
|
+ //定时任务排产:获取工厂id
|
|
|
+ IConfiguration configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).Build();
|
|
|
+ domain = configuration.GetConnectionString("Factory_id");
|
|
|
+
|
|
|
+ //获取提前期
|
|
|
+ var generalizedCodeMaster = _generalizedCodeMaster.Select(x => x.FldName == "SystemConfig" && x.Val == "WorkOrderLockPeriod" && x.Domain == domain).FirstOrDefault();
|
|
|
+ decimal Udecil = 0;
|
|
|
+ if (generalizedCodeMaster != null)
|
|
|
+ {
|
|
|
+ Udecil = generalizedCodeMaster.UDeci1;
|
|
|
+ }
|
|
|
+ //排产取4周工单排产
|
|
|
+ DateTime dateTime = DateTime.Now.AddDays(30);
|
|
|
+ DateTime date = DateTime.Now.AddDays((double)Udecil);
|
|
|
+ var workOrds = _workOrdMaster.Select(x => x.IsActive && x.Domain == domain && x.OrdDate < dateTime && x.OrdDate > date && x.Status == "初始").ToList();
|
|
|
await DoProductShcedule(workOrds, domain);
|
|
|
}
|
|
|
|
|
|
@@ -168,30 +190,18 @@ namespace Business.ResourceExamineManagement
|
|
|
/// <returns></returns>
|
|
|
public async Task DoProductShcedule(List<WorkOrdMaster> workOrds, string factoryid)
|
|
|
{
|
|
|
+ //记录工厂id
|
|
|
domain = factoryid;
|
|
|
- if (workOrds.Count == 0)//定时任务调用时,需要获取工单数据
|
|
|
- {
|
|
|
- //1、获取需要排产的工单:Status为空且IsActive==1
|
|
|
- workOrds = _workOrdMaster.Select(p => string.IsNullOrEmpty(p.Status) && p.IsActive && p.Domain == domain);
|
|
|
- }
|
|
|
if (workOrds.Count == 0)//没有工单需要排产
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
//获取排产工单的最早计划开工日期
|
|
|
- DateTime earlist;
|
|
|
- if (workOrds.Min(p => p.OrdDate) == null)
|
|
|
- {
|
|
|
- earlist = DateTime.Now.Date.AddDays(7);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- earlist = workOrds.Min(p => p.OrdDate.GetValueOrDefault()).Date;
|
|
|
- }
|
|
|
+ DateTime earlist = workOrds.Min(p => p.OrdDate.GetValueOrDefault()).Date;
|
|
|
|
|
|
//2、获取数据
|
|
|
//获取工单工艺路径数据
|
|
|
- List<WorkOrdRouting> workOrdRoutings = _workOrdRouting.Select(p => workOrds.Select(m => m.WorkOrd).Contains(p.WorkOrd) && p.Domain == domain && p.Status != "C" && p.IsActive);
|
|
|
+ List<WorkOrdRouting> workOrdRoutings = _workOrdRouting.Select(p => workOrds.Select(m => m.WorkOrd).Contains(p.WorkOrd) && p.ParentOp == 0 && p.Domain == domain && p.Status != "C" && p.IsActive);
|
|
|
//获取物料对应的生产线信息:物料、工序对应的生产线
|
|
|
List<ProdLineDetail> prodLines = _prodLineDetail.Select(p => workOrds.Select(m => m.ItemNum).Contains(p.Part) && p.Domain == domain && p.IsActive);
|
|
|
List<string> lines = prodLines.Select(p => p.Line).ToList();
|
|
|
@@ -228,32 +238,39 @@ namespace Business.ResourceExamineManagement
|
|
|
//var curSchedules = dbSchedules.Where(p => curLines.Contains(p.Line)).ToList();
|
|
|
allResults.AddRange(scheduleMasters);
|
|
|
|
|
|
- //工序预处理:确定每层级工序对应的产线
|
|
|
- List<WorkOrdRoutingDto> routingDtos = ProcPretreatment(item, workOrdRoutings.Where(p => p.WorkOrd == item.WorkOrd).ToList(), prodLines, allResults);
|
|
|
-
|
|
|
- //排产前的数据校验
|
|
|
- if (routingDtos.Count == 0)//没有维护主工序
|
|
|
+ #region 校验
|
|
|
+ //工单工艺多产线关键工序、物料对应的生产线信息:物料,工序对应的生产线、工作日历数据
|
|
|
+ var workOrdRouting = workOrdRoutings.Where(x => x.WorkOrd == item.WorkOrd && x.ParentOp == 0).ToList();
|
|
|
+ if (workOrdRouting.Count == 0)
|
|
|
{
|
|
|
//记录排产异常原因
|
|
|
- new NLogHelper("ProductionScheduleAppService").WriteLog("DoProductShcedule", "工单<" + item.WorkOrd + ">的工序数据维护错误", _currentTenant.Id.ToString());
|
|
|
+ new NLogHelper("ProductionScheduleAppService").WriteLog("DoProductShcedule", "工单<" + item.WorkOrd + ">的工单工艺流程数据维护为空", _currentTenant.Id.ToString());
|
|
|
continue;
|
|
|
}
|
|
|
- //校验每层级工序是否都维护了产线
|
|
|
- if (routingDtos.Exists(p => string.IsNullOrEmpty(p.Line)))
|
|
|
+ //TODO:多个关键工序校验
|
|
|
+
|
|
|
+ //物料对应生产线校验
|
|
|
+ var ProdLineDetails = prodLines.Where(x => x.Part == item.ItemNum).ToList();
|
|
|
+ if (ProdLineDetails.Count == 0)
|
|
|
{
|
|
|
//记录排产异常原因
|
|
|
- new NLogHelper("ProductionScheduleAppService").WriteLog("DoProductShcedule", "工单<" + item.WorkOrd + ">的产线数据维护错误", _currentTenant.Id.ToString());
|
|
|
+ new NLogHelper("ProductionScheduleAppService").WriteLog("DoProductShcedule", "工单<" + item.WorkOrd + ">的生产线物料数据维护为空", _currentTenant.Id.ToString());
|
|
|
continue;
|
|
|
}
|
|
|
+ var calendarsList = calendars.Where(x => ProdLineDetails.Select(p => p.Line).Contains(x.ProdLine) || string.IsNullOrEmpty(x.ProdLine)).ToList();
|
|
|
//校验每个层级是否维护了工作日历
|
|
|
bool flag = false;
|
|
|
- foreach (var rut in routingDtos)
|
|
|
+ foreach (var rut in ProdLineDetails)
|
|
|
{
|
|
|
- var lineCals = calendars.Where(p => p.ProdLine == rut.Line).ToList();
|
|
|
+ var lineCals = calendarsList.Where(p => p.ProdLine == rut.Line).ToList();
|
|
|
//当前产线未配置工作日历取标准无产线工作日历使用
|
|
|
if (lineCals.Count == 0)
|
|
|
{
|
|
|
lineCals = calendars.Where(p => string.IsNullOrEmpty(p.ProdLine)).ToList();
|
|
|
+ if (lineCals.Count == 0)
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
if (lineCals.Select(p => p.WeekDay).Distinct().Count() != 7)
|
|
|
{
|
|
|
@@ -264,9 +281,14 @@ namespace Business.ResourceExamineManagement
|
|
|
if (flag)
|
|
|
{
|
|
|
//记录排产异常原因
|
|
|
- new NLogHelper("ProductionScheduleAppService").WriteLog("DoProductShcedule", "工单<" + item.WorkOrd + ">的产线工作日历数据维护错误", _currentTenant.Id.ToString());
|
|
|
+ new NLogHelper("ProductionScheduleAppService").WriteLog("DoProductShcedule", "工单<" + item.WorkOrd + ">的<" + item.ProdLine + ">工作日历数据维护错误", _currentTenant.Id.ToString());
|
|
|
continue;
|
|
|
}
|
|
|
+ #endregion
|
|
|
+
|
|
|
+
|
|
|
+ //计算当前物料排产开始产线
|
|
|
+ List<WorkOrdRoutingDto> routingDtos = ProcPretreatment(item, workOrdRoutings.Where(p => p.WorkOrd == item.WorkOrd).ToList(), prodLines, allResults);
|
|
|
|
|
|
//产线排产
|
|
|
LineSchedule(item, routingDtos.OrderBy(p => p.level).ToList(), periodSequenceDtls, scheduleMasters);
|
|
|
@@ -860,130 +882,42 @@ namespace Business.ResourceExamineManagement
|
|
|
/// <returns></returns>
|
|
|
public List<WorkOrdRoutingDto> ProcPretreatment(WorkOrdMaster workOrd, List<WorkOrdRouting> woRuntings, List<ProdLineDetail> prodLines, List<ScheduleResultOpMaster> schedules)
|
|
|
{
|
|
|
- #region 注释旧逻辑
|
|
|
- //List<WorkOrdRoutingDto> routingDtos = new List<WorkOrdRoutingDto>();
|
|
|
- ////当前工单计划开始时间(默认加两天)
|
|
|
- //DateTime planStart = workOrd.OrdDate.GetValueOrDefault().AddDays(2);
|
|
|
- ////取主工序(第一层级工序)
|
|
|
- //var firsts = woRuntings.Where(p => p.ParentOp == 0).OrderByDescending(p => p.OP).ToList();
|
|
|
- //if (firsts.Count == 0)
|
|
|
- //{
|
|
|
- // return routingDtos;
|
|
|
- //}
|
|
|
- //WorkOrdRoutingDto dto = new WorkOrdRoutingDto();
|
|
|
- ////主工序按照Op排序,取最大Op
|
|
|
- //var lastOp = firsts.First();
|
|
|
- //dto.ParentOp = lastOp.ParentOp;
|
|
|
- //dto.level = 1;
|
|
|
- //dto.Op = lastOp.OP;
|
|
|
- //dto.ChdParentOps = new List<int>();
|
|
|
- ////获取当前层级工序中有子级的工序集合
|
|
|
- //var childs = woRuntings.Where(p => firsts.Select(m => m.OP).Contains(p.ParentOp)).Select(m => m.ParentOp).Distinct().ToList();
|
|
|
- //if (childs.Count > 0)
|
|
|
- //{
|
|
|
- // dto.ChdParentOps = childs;
|
|
|
- //}
|
|
|
- ////主工序对应的产线(目前只考虑一个产品对应一条产线的情况)
|
|
|
- //var line = prodLines.Where(p => p.Part == lastOp.ItemNum && p.Op == lastOp.OP).FirstOrDefault();
|
|
|
- //if (line != null)
|
|
|
- //{
|
|
|
- // dto.Line = line.Line;
|
|
|
- // dto.Rate = line.Rate;
|
|
|
- // dto.SetupTime = 0;
|
|
|
- // //获取产线占用结束时间
|
|
|
- // var schedule = schedules.Where(p => p.Line == line.Line).OrderByDescending(p => p.WorkEndTime).FirstOrDefault();
|
|
|
- // dto.StartTime = schedule == null ? planStart : schedule.WorkEndTime <= planStart ? planStart : schedule.WorkEndTime;
|
|
|
- //}
|
|
|
- //routingDtos.Add(dto);
|
|
|
-
|
|
|
- ////递归处理其他层级工序
|
|
|
- //RecursionProc(woRuntings, firsts, 1, routingDtos, prodLines);
|
|
|
- #endregion
|
|
|
- #region 新逻辑
|
|
|
List<WorkOrdRoutingDto> routingDtos = new List<WorkOrdRoutingDto>();
|
|
|
//当前工单计划开始时间(默认加两天)
|
|
|
- DateTime planStart = workOrd.OrdDate.GetValueOrDefault().AddDays(2);
|
|
|
-
|
|
|
- var firsts = woRuntings.Where(x => x.WorkOrd == workOrd.WorkOrd && x.MilestoneOp).ToList();
|
|
|
+ DateTime planStart = workOrd.OrdDate.GetValueOrDefault();
|
|
|
+ //取主工序(第一层级工序)
|
|
|
+ var firsts = woRuntings.Where(p => p.ParentOp == 0 && p.MilestoneOp).OrderByDescending(p => p.OP).ToList();
|
|
|
if (firsts.Count == 0)
|
|
|
{
|
|
|
return routingDtos;
|
|
|
}
|
|
|
- foreach (var item in firsts)
|
|
|
+ WorkOrdRoutingDto dto = new WorkOrdRoutingDto();
|
|
|
+ //主工序按照Op排序,取最大Op
|
|
|
+ var lastOp = firsts.First();
|
|
|
+ dto.ParentOp = lastOp.ParentOp;
|
|
|
+ dto.level = 1;
|
|
|
+ dto.Op = lastOp.OP;
|
|
|
+ dto.ChdParentOps = new List<int>();
|
|
|
+ //获取当前层级工序中有子级的工序集合
|
|
|
+ var childs = woRuntings.Where(p => firsts.Select(m => m.OP).Contains(p.ParentOp)).Select(m => m.ParentOp).Distinct().ToList();
|
|
|
+ if (childs.Count > 0)
|
|
|
{
|
|
|
- //多产线处理
|
|
|
- var line = prodLines.Where(p => p.Part == item.ItemNum && p.Op == item.OP).ToList();
|
|
|
- foreach (var data in line)
|
|
|
- {
|
|
|
- WorkOrdRoutingDto dto = new WorkOrdRoutingDto();
|
|
|
- dto.Line = data.Line;
|
|
|
- dto.Rate = data.Rate;
|
|
|
- dto.SetupTime = data.SetupTime;
|
|
|
- dto.sequence = data.Sequence;
|
|
|
- dto.Op = data.Op.Value;
|
|
|
- //获取产线占用结束时间
|
|
|
- var schedule = schedules.Where(p => p.Line == data.Line).OrderByDescending(p => p.WorkEndTime).FirstOrDefault();
|
|
|
- dto.StartTime = schedule == null ? planStart : schedule.WorkEndTime <= planStart ? planStart : schedule.WorkEndTime;
|
|
|
- routingDtos.Add(dto);
|
|
|
- }
|
|
|
+ dto.ChdParentOps = childs;
|
|
|
}
|
|
|
- //多生产线优先级处理
|
|
|
- //var
|
|
|
- //var Lines = routingDtos.GroupBy(x => x.Line);
|
|
|
- //递归处理其他层级工序
|
|
|
- //RecursionProc(woRuntings, firsts, 1, routingDtos, prodLines);
|
|
|
- #endregion
|
|
|
- return routingDtos;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 递归处理工序
|
|
|
- /// </summary>
|
|
|
- /// <param name="woRuntings">工单工序</param>
|
|
|
- /// <param name="preLevels">上-层级工序</param>
|
|
|
- /// <param name="level">层级</param>
|
|
|
- /// <param name="routingDtos">返回结果</param>
|
|
|
- /// <param name="prodLines">产线</param>
|
|
|
- public void RecursionProc(List<WorkOrdRouting> woRuntings, List<WorkOrdRouting> preLevels, int level, List<WorkOrdRoutingDto> routingDtos, List<ProdLineDetail> prodLines)
|
|
|
- {
|
|
|
- //获取当前层级工序
|
|
|
- var curLevels = woRuntings.Where(p => preLevels.Select(m => m.OP).Contains(p.ParentOp)).ToList();
|
|
|
- if (curLevels.Count == 0)
|
|
|
+ //主工序对应的产线(目前只考虑一个产品对应一条产线的情况)
|
|
|
+ var line = prodLines.Where(p => p.Part == lastOp.ItemNum && p.Op == lastOp.OP).FirstOrDefault();
|
|
|
+ if (line != null)
|
|
|
{
|
|
|
- return;
|
|
|
+ dto.Line = line.Line;
|
|
|
+ dto.Rate = line.Rate;
|
|
|
+ dto.SetupTime = 0;
|
|
|
+ //获取产线占用结束时间
|
|
|
+ var schedule = schedules.Where(p => p.Line == line.Line).OrderByDescending(p => p.WorkEndTime).FirstOrDefault();
|
|
|
+ dto.StartTime = schedule == null ? planStart : schedule.WorkEndTime <= planStart ? planStart : schedule.WorkEndTime;
|
|
|
}
|
|
|
- //获取父级Op-当前层级有几条子产线
|
|
|
- var parentOps = curLevels.Select(m => m.ParentOp).Distinct().ToList();
|
|
|
- foreach (var item in parentOps)
|
|
|
- {
|
|
|
- var dto = new WorkOrdRoutingDto();
|
|
|
- var lastOp = curLevels.Where(p => p.ParentOp == item).OrderByDescending(m => m.OP).FirstOrDefault();
|
|
|
- if (lastOp == null)
|
|
|
- {
|
|
|
- continue;
|
|
|
- }
|
|
|
- dto.Op = lastOp.OP;
|
|
|
- dto.ParentOp = lastOp.ParentOp;
|
|
|
- dto.level = level + 1;
|
|
|
- dto.ChdParentOps = new List<int>();
|
|
|
- //获取当前层级工序中有子级的工序集合
|
|
|
- var childs = woRuntings.Where(p => curLevels.Where(p => p.ParentOp == item).Select(m => m.OP).Contains(p.ParentOp)).Select(m => m.ParentOp).Distinct().ToList();
|
|
|
- if (childs.Count > 0)
|
|
|
- {
|
|
|
- dto.ChdParentOps = childs;
|
|
|
- }
|
|
|
- //当前层级工序对应的产线
|
|
|
- var maxRateLine = prodLines.Where(p => p.Part == lastOp.ItemNum && p.Op == lastOp.OP).OrderByDescending(p => p.Rate).FirstOrDefault();
|
|
|
- if (maxRateLine != null)
|
|
|
- {
|
|
|
- dto.Line = maxRateLine.Line;
|
|
|
- dto.Rate = maxRateLine.Rate;
|
|
|
- dto.SetupTime = maxRateLine.SetupTime;
|
|
|
- }
|
|
|
- routingDtos.Add(dto);
|
|
|
- }
|
|
|
- //递归
|
|
|
- RecursionProc(woRuntings, curLevels, level + 1, routingDtos, prodLines);
|
|
|
+ routingDtos.Add(dto);
|
|
|
+
|
|
|
+ return routingDtos;
|
|
|
}
|
|
|
}
|
|
|
}
|