|
|
@@ -11,6 +11,8 @@ using System.Text;
|
|
|
using System.Threading.Tasks;
|
|
|
using Volo.Abp.Application.Services;
|
|
|
using Volo.Abp.Domain.Repositories;
|
|
|
+using Business.Dto;
|
|
|
+using Business.Model.Sale;
|
|
|
|
|
|
namespace Business.ResourceExamineManagement
|
|
|
{
|
|
|
@@ -21,24 +23,34 @@ namespace Business.ResourceExamineManagement
|
|
|
{
|
|
|
#region 服务
|
|
|
/// <summary>
|
|
|
- /// 工艺路径
|
|
|
+ /// 生产线明细表
|
|
|
/// </summary>
|
|
|
- public List<mo_mes_technique> techs = new List<mo_mes_technique>();
|
|
|
+ public List<ProdLineDetail> prodLines = new List<ProdLineDetail>();
|
|
|
|
|
|
/// <summary>
|
|
|
- /// 工艺关联工序
|
|
|
+ /// 标准工艺路径表
|
|
|
/// </summary>
|
|
|
- public List<mo_mes_tech_process> tech_Processes = new List<mo_mes_tech_process>();
|
|
|
+ public List<RoutingOpDetail> routingOps = new List<RoutingOpDetail>();
|
|
|
|
|
|
/// <summary>
|
|
|
- /// 工序
|
|
|
+ /// 排产记录表
|
|
|
/// </summary>
|
|
|
- public List<mo_mes_process> process = new List<mo_mes_process>();
|
|
|
+ public List<PeriodSequenceDet> periodSequences = new List<PeriodSequenceDet>();
|
|
|
|
|
|
/// <summary>
|
|
|
- /// 工艺工序关联工位
|
|
|
+ /// 工作日历
|
|
|
/// </summary>
|
|
|
- public List<mo_mes_tech_proc_workshop> tech_Proc_Workshops = new List<mo_mes_tech_proc_workshop>();
|
|
|
+ public List<ShopCalendarWorkCtr> calendarWorks = new List<ShopCalendarWorkCtr>();
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 休息时间段
|
|
|
+ /// </summary>
|
|
|
+ public List<QualityLineWorkDetail> qualityLineWorks = new List<QualityLineWorkDetail>();
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 节假日
|
|
|
+ /// </summary>
|
|
|
+ public List<HolidayMaster> holidays = new List<HolidayMaster>();
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
@@ -50,255 +62,164 @@ namespace Business.ResourceExamineManagement
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// 产能计算
|
|
|
+ /// 产能计算-批量
|
|
|
/// </summary>
|
|
|
- /// <param name="packages">件数</param>
|
|
|
- /// <returns>生产时长</returns>
|
|
|
- public decimal ProductiveExamine(ProdExamineParamDto param)
|
|
|
+ /// <param name="param">产能检查入参</param>
|
|
|
+ /// <returns>生产时长(天)</returns>
|
|
|
+ public DateTime ProductiveExamine(ProdExamineParamDto param)
|
|
|
{
|
|
|
- if (param.packages <= 0)
|
|
|
+ //生产结束时间
|
|
|
+ DateTime planEnd = param.PlanStart.Date;
|
|
|
+ if (param.QtyOrd <= 0)
|
|
|
{
|
|
|
- //throw new NotImplementedException("产能计算参数有误!");
|
|
|
+ return planEnd;
|
|
|
}
|
|
|
- //过滤数据
|
|
|
- //1.1、获取工艺路径数据
|
|
|
- mo_mes_technique curTech = techs.FirstOrDefault(p => p.bom == param.bom_number && p.bomver == param.version);
|
|
|
- if (curTech == null)
|
|
|
+ //获取当前产品的工艺路线:主产线,最后一道工序
|
|
|
+ var curRoutingOps = routingOps.Where(p => p.RoutingCode == param.ItemNum && p.ParentOp == 0).OrderByDescending(p => p.Op).ToList();
|
|
|
+ if (curRoutingOps.Count() == 0)
|
|
|
{
|
|
|
- return param.packages * 20;
|
|
|
- //throw new NotImplementedException("工艺路径不存在,请调整!");
|
|
|
+ return planEnd;
|
|
|
}
|
|
|
- //1.2、获取工艺关联工序数据
|
|
|
- List<mo_mes_tech_process> curTechProcess = tech_Processes.Where(p => p.tech_id == curTech.mysql_id).ToList();
|
|
|
- if (curTechProcess.Count == 0)
|
|
|
+ var lastOp = curRoutingOps.Last();
|
|
|
+ //获取产线
|
|
|
+ var curProdLine = prodLines.FirstOrDefault(p => p.Part == param.ItemNum && p.Op == lastOp.Op);
|
|
|
+ if (curProdLine == null)
|
|
|
{
|
|
|
- return param.packages * 20;
|
|
|
- //throw new NotImplementedException("当前工艺路径没有配置工序,请调整!");
|
|
|
+ return planEnd;
|
|
|
}
|
|
|
- //1.3、获取工序数据
|
|
|
- List<mo_mes_process> curProcess = process.Where(p => curTechProcess.Select(m => m.proc_id).Contains(p.mysql_id)).ToList();
|
|
|
- if (curProcess.Count == 0)
|
|
|
+ //获取产线工作日历
|
|
|
+ var curCalendars = calendarWorks.Where(p => p.ProdLine == curProdLine.Line).ToList();
|
|
|
+ if (curCalendars.Count() == 0 || curCalendars.Count() != 0)
|
|
|
{
|
|
|
- return param.packages * 20;
|
|
|
- //throw new NotImplementedException("工序数据不存在,请调整!");
|
|
|
+ return planEnd;
|
|
|
}
|
|
|
- //1.4、获取工位数据
|
|
|
- List<mo_mes_tech_proc_workshop> curWorkShops = tech_Proc_Workshops.Where(p=> curTechProcess.Select(m=>m.mysql_id).Contains(p.tech_proc_id)).ToList(); ;
|
|
|
-
|
|
|
- //2、获取工艺路径下的第一层级工序:目前只考虑第一层级
|
|
|
- List<mo_mes_tech_process> fistLevels = curTechProcess.Where(p => p.parentprocid == curTech.mysql_id).ToList();
|
|
|
- if (fistLevels.Count == 0)
|
|
|
+ //获取产线休息时间
|
|
|
+ var curqualityLines = qualityLineWorks.Where(p=>p.ProdLine == curProdLine.Line).ToList();
|
|
|
+ //生产数量
|
|
|
+ decimal sumAmount = 0m;
|
|
|
+ do
|
|
|
{
|
|
|
- return param.packages * 20;
|
|
|
- //throw new NotImplementedException("当前工艺路径没有配置工序,请调整!");
|
|
|
- }
|
|
|
- //工艺预处理
|
|
|
- List<TechProcDto> teches = TechProcPretreatment(fistLevels,param.packages);
|
|
|
- decimal sumTimes = teches.OrderByDescending(p => p.sumTimes).First().sumTimes;
|
|
|
- return sumTimes;
|
|
|
+ //获取工作日
|
|
|
+ planEnd = GetNextWorkDay(planEnd, curCalendars);
|
|
|
+ //计算工作日的产能
|
|
|
+ sumAmount += CalcCapacity(planEnd, curProdLine, curCalendars, curqualityLines);
|
|
|
+ planEnd.AddDays(1);
|
|
|
+ } while (sumAmount < param.QtyOrd);
|
|
|
+ return planEnd.AddDays(-1);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// 工序预处理,按照多分支生成多条单独的工艺路径
|
|
|
+ /// 获取工作日
|
|
|
/// </summary>
|
|
|
- /// <param name="proc">当前工序</param>
|
|
|
- /// <param name="processes">工艺关联工序list</param>
|
|
|
- /// <param name="packages">生产件数</param>
|
|
|
+ /// <param name="weekDay">当前周几</param>
|
|
|
+ /// <param name="startTime">开始时间:年-月-日</param>
|
|
|
+ /// <param name="curCalendars">当前产线的工作日历</param>
|
|
|
/// <returns></returns>
|
|
|
- private List<TechProcDto> TechProcPretreatment(List<mo_mes_tech_process> processes,int packages)
|
|
|
- {
|
|
|
- //工艺路径预处理dto
|
|
|
- List<TechProcDto> techProcDtos = new List<TechProcDto>();
|
|
|
- TechProcDto dto;
|
|
|
- //获取下一步工序id
|
|
|
- List<long> nextProcIds = processes.Where(p=>p.nextprocid != null).Select(p => p.nextprocid.GetValueOrDefault()).ToList();
|
|
|
- //获取起点工序
|
|
|
- var startProcs = processes.Where(p => !nextProcIds.Contains(p.proc_id)).ToList();
|
|
|
- //递归处理工序返回值
|
|
|
- List<mo_mes_tech_process> rtnList;
|
|
|
- for (int i = 0; i < startProcs.Count; i++)
|
|
|
- {
|
|
|
- dto = new TechProcDto();
|
|
|
- rtnList = new List<mo_mes_tech_process>();
|
|
|
- GetNextProc(startProcs[i], processes, rtnList);
|
|
|
- dto.serialno = i + 1;
|
|
|
- dto.processes = rtnList;
|
|
|
- //dto.details = CalcTakeTimeByLq(rtnList, packages);//通过Lq计算
|
|
|
- dto.details = CalcTakeTimeByLqt(rtnList, packages);//通过Lqt计算
|
|
|
- dto.sumTimes = dto.details.Sum(p=>p.wait_time);
|
|
|
- techProcDtos.Add(dto);
|
|
|
- }
|
|
|
- return techProcDtos;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 递归:根据起始工序,获取后续工序
|
|
|
- /// </summary>
|
|
|
- /// <param name="proc"></param>
|
|
|
- /// <param name="processes"></param>
|
|
|
- /// <param name="rtnList"></param>
|
|
|
- private void GetNextProc(mo_mes_tech_process proc, List<mo_mes_tech_process> processes, List<mo_mes_tech_process> rtnList)
|
|
|
+ public DateTime GetNextWorkDay(DateTime startTime, List<ShopCalendarWorkCtr> curCalendars)
|
|
|
{
|
|
|
- rtnList.Add(proc);
|
|
|
- //下一工序id为null,终止
|
|
|
- if (proc?.nextprocid == null)
|
|
|
+ DateTime rtnData = startTime;
|
|
|
+ int weekDay = (int)startTime.DayOfWeek;
|
|
|
+ var calendar = curCalendars.FirstOrDefault(p => p.WeekDay == weekDay);
|
|
|
+ //判断当天是否是工作日
|
|
|
+ if (weekDay == 0 || weekDay == 6)//周六,周日
|
|
|
{
|
|
|
- return;
|
|
|
+ if (!holidays.Exists(p => p.Dated.GetValueOrDefault().Date == startTime && p.Ufld1 == "调班"))//今天是周末
|
|
|
+ {
|
|
|
+ //递归继续找下一个工作日
|
|
|
+ rtnData = GetNextWorkDay(startTime.AddDays(1), curCalendars);
|
|
|
+ return rtnData;
|
|
|
+ }
|
|
|
+ return rtnData;
|
|
|
}
|
|
|
- //获取下一个工序
|
|
|
- var nextProc = processes.FirstOrDefault(p=>p.proc_id == proc.nextprocid);
|
|
|
- if (nextProc == null)
|
|
|
+ //今天不是周六周日,需要判断是不是节假日
|
|
|
+ if (holidays.Exists(p => p.Dated.GetValueOrDefault().Date == startTime && p.Ufld1 == "休假"))//是节假日
|
|
|
{
|
|
|
- return;
|
|
|
+ //递归继续找下一个工作日
|
|
|
+ rtnData = GetNextWorkDay(startTime.AddDays(1), curCalendars);
|
|
|
+ return rtnData;
|
|
|
}
|
|
|
- GetNextProc(nextProc, processes, rtnList);
|
|
|
+ return rtnData;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// 通过Lq计算工艺耗时
|
|
|
+ /// 计算当天的产能
|
|
|
/// </summary>
|
|
|
- /// <param name="Processes"></param>
|
|
|
- /// <param name="packages"></param>
|
|
|
+ /// <param name="startTime"></param>
|
|
|
+ /// <param name="prodLine"></param>
|
|
|
+ /// <param name="curCalendars"></param>
|
|
|
+ /// <param name="curQualityLines"></param>
|
|
|
/// <returns></returns>
|
|
|
- private List<StartTimeDto> CalcTakeTimeByLq(List<mo_mes_tech_process> Processes, int packages)
|
|
|
+ public decimal CalcCapacity(DateTime startTime,ProdLineDetail prodLine, List<ShopCalendarWorkCtr> curCalendars, List<QualityLineWorkDetail> curQualityLines)
|
|
|
{
|
|
|
- //工序需要等待时间记录
|
|
|
- List<StartTimeDto> starts = new List<StartTimeDto>();
|
|
|
- StartTimeDto dto;
|
|
|
- foreach (var chd in Processes)
|
|
|
- {
|
|
|
- dto = new StartTimeDto();
|
|
|
- if (chd.nextprocid == null)//最后一个工序
|
|
|
- {
|
|
|
- //计算最后一个工序耗时
|
|
|
- dto = CalcProcTakeTimeByLq(chd, packages, packages);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- dto = CalcProcTakeTimeByLq(chd, chd.lq.GetValueOrDefault(), packages);
|
|
|
- }
|
|
|
- //添加记录
|
|
|
- starts.Add(dto);
|
|
|
- }
|
|
|
- return starts;
|
|
|
+ decimal sumQty = 0m;
|
|
|
+ //获取休息时长(分钟)
|
|
|
+ decimal sumResrt = curQualityLines.Sum(p => p.RestTime);
|
|
|
+ //获取当天的工作时长(分钟)
|
|
|
+ decimal workTime = curCalendars.First(p => p.WeekDay == (int)startTime.DayOfWeek).ShiftsHours1 * 60;
|
|
|
+ //计算产能
|
|
|
+ sumQty = (workTime - sumResrt) / 60 * prodLine.Rate;
|
|
|
+ return sumQty;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// 通过Lq计算当前工序前置准备时间
|
|
|
+ /// 计算订单行的建议交期(产能/物料)
|
|
|
/// </summary>
|
|
|
- /// <param name="proc"></param>
|
|
|
- /// <param name="quantity">LeadQuantity to Start Next</param>
|
|
|
- /// <param name="packages">件数</param>
|
|
|
+ /// <param name="sentrys"></param>
|
|
|
+ /// <param name="kittingTimes"></param>
|
|
|
/// <returns></returns>
|
|
|
- private StartTimeDto CalcProcTakeTimeByLq(mo_mes_tech_process proc, decimal quantity, int packages)
|
|
|
+ public List<crm_seorderentry> CalcSuggestTime(List<crm_seorderentry> sentrys, List<KittingTimeDto> kittingTimes)
|
|
|
{
|
|
|
- //记录当前工序耗时
|
|
|
- StartTimeDto dto = new StartTimeDto();
|
|
|
- //添加耗时记录
|
|
|
- dto.tech_id = proc.tech_id;
|
|
|
- dto.proc_id = proc.proc_id;
|
|
|
- dto.nextproc_id = proc.nextprocid;
|
|
|
- if (proc.wctype == 1)//人工型:数量/uph(一小时生产数量)*60(小时转换为分钟)/wsinuse(工位数)
|
|
|
+ ProdExamineParamDto param;
|
|
|
+ foreach (var item in sentrys)
|
|
|
{
|
|
|
- if ( proc.uph.GetValueOrDefault() == 0 || proc.wsinuse.GetValueOrDefault() == 0)
|
|
|
+ var dto = kittingTimes.FirstOrDefault(p=>p.sentry_id == item.Id);
|
|
|
+ if (dto == null)
|
|
|
{
|
|
|
- throw new NotImplementedException("当前工序uph或wsinuse参数配置错误,请调整!");
|
|
|
+ continue;
|
|
|
}
|
|
|
- dto.wait_time = quantity / proc.uph.GetValueOrDefault() * 60 / proc.wsinuse.GetValueOrDefault();
|
|
|
- dto.take_time = packages / proc.uph.GetValueOrDefault() * 60 / proc.wsinuse.GetValueOrDefault();
|
|
|
- }
|
|
|
- else if (proc.wctype == 2)//流水线型:数量*ct(生产一件所需时间)/wsinuse(工位数)
|
|
|
- {
|
|
|
- if (proc.ct.GetValueOrDefault() == 0 || proc.wsinuse.GetValueOrDefault() == 0)
|
|
|
+ if (dto.LackQty == 0)//当前订单行库存足够
|
|
|
{
|
|
|
- throw new NotImplementedException("当前工序ct或wsinuse参数配置错误,请调整!");
|
|
|
+ item.sys_material_date = dto.kitting_time;
|
|
|
+ item.sys_capacity_date = dto.kitting_time;
|
|
|
}
|
|
|
- dto.wait_time = quantity * proc.ct.GetValueOrDefault() / proc.wsinuse.GetValueOrDefault();
|
|
|
- dto.take_time = packages * proc.ct.GetValueOrDefault() / proc.wsinuse.GetValueOrDefault();
|
|
|
- }
|
|
|
- else if (proc.wctype == 3)//设备型:向上取整(数量/一次可加工数量/wsinuse(工位数))*ct(老化一次所需时间)
|
|
|
- {
|
|
|
- if (proc.upe.GetValueOrDefault() == 0 || proc.wsinuse.GetValueOrDefault() == 0|| proc.ct.GetValueOrDefault() == 0)
|
|
|
+ //计算系统建议交期(物料)
|
|
|
+ param = new ProdExamineParamDto
|
|
|
{
|
|
|
- throw new NotImplementedException("当前工序upe或ct或wsinuse参数配置错误,请调整!");
|
|
|
- }
|
|
|
- dto.wait_time = Math.Ceiling(quantity / proc.upe.GetValueOrDefault() / proc.wsinuse.GetValueOrDefault()) * proc.ct.GetValueOrDefault();
|
|
|
- dto.take_time = Math.Ceiling(packages / proc.upe.GetValueOrDefault() / proc.wsinuse.GetValueOrDefault()) * proc.ct.GetValueOrDefault();
|
|
|
- }
|
|
|
- return dto;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 通过Lqt计算工艺耗时
|
|
|
- /// </summary>
|
|
|
- /// <param name="Processes"></param>
|
|
|
- /// <param name="packages"></param>
|
|
|
- /// <returns></returns>
|
|
|
- private List<StartTimeDto> CalcTakeTimeByLqt(List<mo_mes_tech_process> Processes, int packages)
|
|
|
- {
|
|
|
- //工序需要等待时间记录
|
|
|
- List<StartTimeDto> starts = new List<StartTimeDto>();
|
|
|
- StartTimeDto dto;
|
|
|
- foreach (var chd in Processes)
|
|
|
- {
|
|
|
- dto = new StartTimeDto();
|
|
|
- //添加耗时记录
|
|
|
- dto.tech_id = chd.tech_id;
|
|
|
- dto.proc_id = chd.proc_id;
|
|
|
- dto.nextproc_id = chd.nextprocid;
|
|
|
-
|
|
|
- //计算当前工序生产耗时
|
|
|
- dto.take_time = CalcProcTakeTime(chd, packages);
|
|
|
- if (chd.nextprocid == null)//最后一个工序
|
|
|
+ ItemNum = item.item_number,
|
|
|
+ QtyOrd = dto.LackQty,
|
|
|
+ PlanStart = dto.kitting_time.Date.AddDays(1)
|
|
|
+ };
|
|
|
+ item.sys_material_date = ProductiveExamine(param);
|
|
|
+ item.sys_capacity_date = item.sys_material_date;
|
|
|
+ //计算系统交期(产能)
|
|
|
+ //获取主线最后一道工序
|
|
|
+ var routings = routingOps.Where(p => p.RoutingCode == item.item_number && p.ParentOp == 0).ToList();
|
|
|
+ if (routings.Count() == 0)
|
|
|
{
|
|
|
- dto.wait_time = dto.take_time;
|
|
|
+ continue;
|
|
|
}
|
|
|
- else
|
|
|
+ //获取产线
|
|
|
+ var prodLine = prodLines.FirstOrDefault(p=>p.Part == item.item_number && p.Op == routings.Last().Op);
|
|
|
+ if (prodLine == null)
|
|
|
{
|
|
|
- dto.wait_time = chd.lqt.Value;
|
|
|
+ continue;
|
|
|
}
|
|
|
- //添加记录
|
|
|
- starts.Add(dto);
|
|
|
- }
|
|
|
- return starts;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 计算当前工序生产时间
|
|
|
- /// </summary>
|
|
|
- /// <param name="proc"></param>
|
|
|
- /// <param name="packages">件数</param>
|
|
|
- /// <returns></returns>
|
|
|
- private decimal CalcProcTakeTime(mo_mes_tech_process proc, int packages)
|
|
|
- {
|
|
|
- //当前工序生产时间
|
|
|
- decimal takeTiem = 0.00m;
|
|
|
-
|
|
|
- if (proc.wctype == 1)//人工型:数量/uph(一小时生产数量)*60(小时转换为分钟)/wsinuse(工位数)
|
|
|
- {
|
|
|
- if (proc.uph.GetValueOrDefault() == 0 || proc.wsinuse.GetValueOrDefault() == 0)
|
|
|
+ //获取产线空闲开始时间
|
|
|
+ var schedules= periodSequences.Where(p => p.ItemNum == item.item_number && p.Op == prodLine.Op && p.Line == prodLine.Line).OrderByDescending(p => p.PlanDate).ToList();
|
|
|
+ if (schedules.Count() == 0)
|
|
|
{
|
|
|
- throw new NotImplementedException("当前工序uph或wsinuse参数配置错误,请调整!");
|
|
|
+ continue;
|
|
|
}
|
|
|
- takeTiem = packages / proc.uph.GetValueOrDefault() * 60 / proc.wsinuse.GetValueOrDefault();
|
|
|
- }
|
|
|
- else if (proc.wctype == 2)//流水线型:数量*ct(生产一件所需时间)/wsinuse(工位数)
|
|
|
- {
|
|
|
- if (proc.ct.GetValueOrDefault() == 0 || proc.wsinuse.GetValueOrDefault() == 0)
|
|
|
- {
|
|
|
- throw new NotImplementedException("当前工序ct或wsinuse参数配置错误,请调整!");
|
|
|
- }
|
|
|
- takeTiem = packages * proc.ct.GetValueOrDefault() / proc.wsinuse.GetValueOrDefault();
|
|
|
- }
|
|
|
- else if (proc.wctype == 3)//设备型:向上取整(数量/一次可加工数量/wsinuse(工位数))*ct(老化一次所需时间)
|
|
|
- {
|
|
|
- if (proc.upe.GetValueOrDefault() == 0 || proc.wsinuse.GetValueOrDefault() == 0 || proc.ct.GetValueOrDefault() == 0)
|
|
|
+ //产线空闲开始时间
|
|
|
+ var lastTime = schedules.First().PlanDate.GetValueOrDefault().Date;
|
|
|
+ if (lastTime >= param.PlanStart)//如果产线空闲时间大于或者等于系统建议交期(物料)的开工时间
|
|
|
{
|
|
|
- throw new NotImplementedException("当前工序upe或ct或wsinuse参数配置错误,请调整!");
|
|
|
+ //取下一天开始计算生产结束时间
|
|
|
+ param.PlanStart = lastTime.AddDays(1);
|
|
|
+ item.sys_capacity_date = ProductiveExamine(param);
|
|
|
}
|
|
|
- takeTiem = Math.Ceiling(packages / proc.upe.GetValueOrDefault() / proc.wsinuse.GetValueOrDefault()) * proc.ct.GetValueOrDefault();
|
|
|
}
|
|
|
- return takeTiem;
|
|
|
+ return sentrys;
|
|
|
}
|
|
|
}
|
|
|
}
|