| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752 |
- using Business.Business.Dto;
- using Business.Core.Utilities;
- using Business.Dto;
- using Business.EntityFrameworkCore.SqlRepositories;
- using Business.Model.MES.IC;
- using Business.Model.Production;
- using MongoDB.Driver;
- using MongoDB.Driver.Linq;
- using System;
- using System.Collections.Generic;
- using System.Collections.Immutable;
- using System.Linq;
- using System.Threading.Tasks;
- using Volo.Abp.Application.Services;
- namespace Business.Quartz
- {
- /// <summary>
- /// 生产排产服务
- /// </summary>
- public class ProductionScheduleAppService : ApplicationService
- {
- #region 服务
- /// <summary>
- /// 物料
- /// </summary>
- private ISqlRepository<ItemMaster> _itemMaster;
- /// <summary>
- /// 工单
- /// </summary>
- private ISqlRepository<WorkOrdMaster> _workOrdMaster;
- /// <summary>
- /// 工单物料明细
- /// </summary>
- private ISqlRepository<WorkOrdDetail> _workOrdDetail;
- /// <summary>
- /// 工单工艺路线明细
- /// </summary>
- private ISqlRepository<WorkOrdRouting> _workOrdRouting;
- /// <summary>
- /// 库存主数据
- /// </summary>
- private ISqlRepository<InvMaster> _invMaster;
- /// <summary>
- /// 生产线明细
- /// </summary>
- private ISqlRepository<ProdLineDetail> _prodLineDetail;
- /// <summary>
- /// 生产周期明细
- /// </summary>
- private ISqlRepository<PeriodSequenceDet> _periodSequenceDet;
- /// <summary>
- /// 排产结果明细
- /// </summary>
- private ISqlRepository<ScheduleResultOpMaster> _scheduleResultOpMaster;
- /// <summary>
- /// 工作日历数据
- /// </summary>
- private ISqlRepository<ShopCalendarWorkCtr> _shopCalendarWorkCtr;
- /// <summary>
- /// 排产异常记录表
- /// </summary>
- private ISqlRepository<ScheduleExceptionMaster> _scheduleExceptionMaster;
- /// <summary>
- /// 产线休息时间记录表
- /// </summary>
- private ISqlRepository<QualityLineWorkDetail> _qualityLineWorkDetail;
- /// <summary>
- /// 节假日记录表
- /// </summary>
- private ISqlRepository<HolidayMaster> _holidayMaster;
- /// <summary>
- /// 雪花算法
- /// </summary>
- SnowFlake help = new SnowFlake();
- /// <summary>
- /// 工作日历数据
- /// </summary>
- private List<ShopCalendarWorkCtr> calendars;
- /// <summary>
- /// 产线休息记录数据
- /// </summary>
- private List<QualityLineWorkDetail> qualityLines;
- /// <summary>
- /// 节假日记录数据
- /// </summary>
- private List<HolidayMaster> holidays;
- #endregion
- #region 构造函数
- /// <summary>
- /// 构造函数
- /// </summary>
- public ProductionScheduleAppService(
- ISqlRepository<ItemMaster> itemMaster,
- ISqlRepository<WorkOrdMaster> workOrdMaster,
- ISqlRepository<WorkOrdDetail> workOrdDetail,
- ISqlRepository<WorkOrdRouting> workOrdRouting,
- ISqlRepository<ProdLineDetail> prodLineDetail,
- ISqlRepository<PeriodSequenceDet> periodSequenceDet,
- ISqlRepository<ScheduleResultOpMaster> scheduleResultOpMaster,
- ISqlRepository<InvMaster> invMaster,
- ISqlRepository<ShopCalendarWorkCtr> shopCalendarWorkCtr,
- ISqlRepository<ScheduleExceptionMaster> scheduleExceptionMaster,
- ISqlRepository<QualityLineWorkDetail> qualityLineWorkDetail,
- ISqlRepository<HolidayMaster> holidayMaster
- )
- {
- _itemMaster= itemMaster;
- _workOrdMaster= workOrdMaster;
- _workOrdDetail= workOrdDetail;
- _workOrdRouting= workOrdRouting;
- _prodLineDetail = prodLineDetail;
- _periodSequenceDet = periodSequenceDet;
- _scheduleResultOpMaster= scheduleResultOpMaster;
- _invMaster= invMaster;
- _shopCalendarWorkCtr= shopCalendarWorkCtr;
- _scheduleExceptionMaster= scheduleExceptionMaster;
- _qualityLineWorkDetail= qualityLineWorkDetail;
- _holidayMaster = holidayMaster;
- }
- #endregion
- /// <summary>
- /// 执行生产排产
- /// </summary>
- public async void DoExt()
- {
- await DoProductShcedule();
- }
- /// <summary>
- /// 生产排产
- /// </summary>
- public async Task DoProductShcedule()
- {
- //1、获取需要排产的工单:Status为空且IsActive==1
- List<WorkOrdMaster> workOrds = _workOrdMaster.Select(p => string.IsNullOrEmpty(p.Status) && p.IsActive == 1).Result;
- if (workOrds.Count == 0)
- {
- return;
- }
- //获取排产工单的最早计划开工日期
- 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 == "1001" && p.Status != "C" && p.IsActive == 1).Result;
- //获取物料对应的生产线信息:物料、工序对应的生产线
- List<ProdLineDetail> prodLines = _prodLineDetail.Select(p => workOrds.Select(m => m.ItemNum).Contains(p.Part) && p.Domain == "1001" && p.IsActive == 1).Result;
- //获取生产周期数据
- List<PeriodSequenceDet> dbPeriodSequences = _periodSequenceDet.Select(p=> workOrds.Select(m => m.ItemNum).Contains(p.ItemNum) && p.PlanDate >= earlist && p.Domain == "1001" && p.IsActive == 1).Result;
- //获取当前日期往后的排产记录数据
- List<ScheduleResultOpMaster> dbSchedules = _scheduleResultOpMaster.Select(p => workOrds.Select(m => m.ItemNum).Contains(p.ItemNum) && p.WorkDate >= earlist && p.Domain == "1001").Result;
- //获取工作日历数据
- calendars = _shopCalendarWorkCtr.Select(p=>p.Domain == "1001" && p.IsActive == 1).Result;
- //获取产线休息记录数据
- qualityLines = _qualityLineWorkDetail.Select(p => p.Domain == "1001" && p.IsActive == 1).Result;
- //获取节假日记录数据
- holidays = _holidayMaster.Select(p => p.Domain == "1001" && p.IsActive == 1 && p.Dated >= earlist).Result;
- //3、排产
- //排产异常记录
- List<ScheduleExceptionMaster> scheduleExceptions = new List<ScheduleExceptionMaster>();
- //生产周期
- List<PeriodSequenceDet> periodSequenceDtls = new List<PeriodSequenceDet>();
- //排产记录表
- List<ScheduleResultOpMaster> scheduleMasters = new List<ScheduleResultOpMaster>();
- foreach (var item in workOrds)
- {
- //当前工单的排产计划开始时间:年-月-日
- DateTime planStart = item.OrdDate.GetValueOrDefault().Date;
- //当前工单的对应的产线排产记录
- var curSchedules = dbSchedules.Where(p => p.ItemNum == item.ItemNum).ToList();
- //工序预处理:确定每层级工序对应的产线
- List<WorkOrdRoutingDto> routingDtos = ProcPretreatment(item, workOrdRoutings.Where(p => p.WorkOrd == item.WorkOrd).ToList(), prodLines, curSchedules);
- if (routingDtos.Count() == 0)//没有维护主工序
- {
- //记录排产异常原因
- scheduleExceptions.Add(new ScheduleExceptionMaster
- {
- RecID = help.NextId(),
- Domain = "1001",
- WorkOrd = item.WorkOrd,
- Remark = "工单的工序数据维护错误",
- CreatTime = DateTime.Now
- });
- continue;
- }
- //校验每层级工序是否都维护了产线
- if (routingDtos.Exists(p=> string.IsNullOrEmpty(p.Line)))
- {
- //记录排产异常原因
- scheduleExceptions.Add(new ScheduleExceptionMaster
- {
- RecID = help.NextId(),
- Domain = "1001",
- WorkOrd = item.WorkOrd,
- Remark = "工单的产线数据维护错误",
- CreatTime = DateTime.Now
- });
- continue;
- }
- //校验每个层级是否维护了工作日历
- bool flag = false;
- foreach (var rut in routingDtos)
- {
- var lineCals = calendars.Where(p => p.ProdLine == rut.Line).ToList();
- if (lineCals.Select(p=>p.WeekDay).Distinct().Count() !=7)
- {
- flag = true;
- break;
- }
- }
- if (flag)
- {
- //记录排产异常原因
- scheduleExceptions.Add(new ScheduleExceptionMaster
- {
- RecID = help.NextId(),
- Domain = "1001",
- WorkOrd = item.WorkOrd,
- Remark = "工单产线的工作日历数据维护错误",
- CreatTime = DateTime.Now
- });
- continue;
- }
- //产线排产
- LineSchedule(item, routingDtos.OrderBy(p => p.level).ToList(), periodSequenceDtls, scheduleMasters);
- }
- }
- /// <summary>
- /// 排产
- /// </summary>
- /// <param name="workOrd">工单</param>
- /// <param name="routingDtos">每层级工序对应的产线信息,从小到大排序</param>
- /// <param name="periodsDet">生产周期</param>
- /// <param name="scheduleResults">排产结果</param>
- public void LineSchedule(WorkOrdMaster workOrd,List<WorkOrdRoutingDto> routingDtos,List<PeriodSequenceDet> periodsDet, List<ScheduleResultOpMaster> scheduleResults)
- {
- //生产周期
- List<PeriodSequenceDet> curSequences = new List<PeriodSequenceDet>();
- //排产明细
- List<ScheduleResultOpMaster> curScheduleRsts = new List<ScheduleResultOpMaster>();
- //产线排产开始时间
- List<LineStartDto> lineStarts = new List<LineStartDto>();
-
- //循环产线,排产
- foreach (var item in routingDtos)
- {
- //当前产线的工作日历
- var mLCalendars = calendars.Where(p => p.ProdLine == item.Line).ToList();
- //当前产线的每天休息时间记录
- var mlqtyWorkDtls = qualityLines.Where(p => p.ProdLine == item.Line).ToList();
- //产线已排产数量
- decimal sumQty = 0m;
- //产线实际排产开始时间
- DateTime workStartTime;
- if (item.level == 1)//主产线
- {
- workStartTime = DealStartTime(item.StartTime, mLCalendars, mlqtyWorkDtls);
- }
- else
- {
- //子产线获取实际排产开始日期
- //获取父级排产开始时间
- DateTime parentStartTime = lineStarts.First(p => p.Op == item.ParentOp).StartTime;
- workStartTime = DealChildStartTime(parentStartTime, item.SetupTime, mLCalendars, mlqtyWorkDtls);
- }
- //记录产线排产开始时间
- lineStarts.Add(new LineStartDto {
- level = item.level,
- Line = item.Line,
- Op= item.Op,
- StartTime = workStartTime
- });
- //排产
- while (sumQty < workOrd.QtyOrded)
- {
- //获取当天的产能
- LineScheduledDto dto = GetScheduledPoint(item, workStartTime, mLCalendars, mlqtyWorkDtls);
- //判断已排产数量+当天的产能是否超过工单数量
- if (sumQty + dto.ProductQty <= workOrd.QtyOrded)//当天的产能需要全部排产
- {
- //记录生产周期
- curSequences.Add(new PeriodSequenceDet
- {
- Domain = "1001",
- Line = item.Line,
- ItemNum = workOrd.ItemNum,
- PlanDate = workStartTime.Date,
- Period = 1,//目前只考虑一班制
- OrdQty = dto.ProductQty,
- WorkOrds = workOrd.WorkOrd,
- IsActive = 1
- });
- //记录排产记录
- curScheduleRsts.Add(new ScheduleResultOpMaster
- {
- Domain = "1001",
- WorkOrd = workOrd.WorkOrd,
- Line = item.Line,
- ItemNum = workOrd.ItemNum,
- Op = item.Op,
- WorkDate = workStartTime.Date,
- WorkQty = dto.ProductQty,
- WorkStartTime = dto.StartTime,
- WorkEndTime = dto.EndTime,
- CreatTime = DateTime.Now
- });
- //累计已排产数量
- sumQty += dto.ProductQty;
- //继续排下一个工作日
- workStartTime = GetNextWorkDay((int)workStartTime.DayOfWeek, workStartTime, mLCalendars);
- }
- else// 最后一天的产能只能占用一部分
- {
- //剩余需要排产的数量
- decimal residueQty = workOrd.QtyOrded - sumQty;
- //剩余数量生产需要时长(分钟)
- decimal workTime = residueQty / item.Rate * 60;
- //获取当天的工作时间段
- List<LineWorkPointDto> workPoints = DealWorkDayToLevels(workStartTime, mLCalendars.First(p => p.WeekDay == (int)workStartTime.DayOfWeek), mlqtyWorkDtls);
- var curPoint = workPoints.First(p => p.StartPoint >= workStartTime && workStartTime <= p.EndPoint);
- TimeSpan span = curPoint.EndPoint - workStartTime;
- //当天工作时间段的有效生产时间
- decimal effMins = (decimal)span.TotalMinutes;
- DateTime workEndTime = workStartTime;
- if (effMins >= workTime)//当前工作时间段即可满足产能
- {
- workEndTime = workStartTime.AddMinutes((double)workTime);
- }
- else
- {
- //获取后续生产时间段
- var nextPoints = workPoints.Where(p => p.Level > curPoint.Level).ToList();
- //剩余需要工作时长
- decimal nextMins = workTime - effMins;
- foreach (var p in nextPoints)
- {
- if (p.WorkMinutes >= nextMins)
- {
- workEndTime = p.StartPoint.AddMinutes((double)nextMins);
- break;
- }
- nextMins -= p.WorkMinutes;
- }
- }
- //记录生产周期
- curSequences.Add(new PeriodSequenceDet
- {
- Domain = "1001",
- Line = item.Line,
- ItemNum = workOrd.ItemNum,
- PlanDate = workStartTime.Date,
- Period = 1,//目前只考虑一班制
- OrdQty = residueQty,
- WorkOrds = workOrd.WorkOrd,
- IsActive = 1
- });
- //记录排产记录
- curScheduleRsts.Add(new ScheduleResultOpMaster
- {
- Domain = "1001",
- WorkOrd = workOrd.WorkOrd,
- Line = item.Line,
- ItemNum = workOrd.ItemNum,
- Op = item.Op,
- WorkDate = workStartTime.Date,
- WorkQty = residueQty,
- WorkStartTime = workStartTime,
- WorkEndTime = workEndTime,
- CreatTime = DateTime.Now
- });
- }
- }
- }
- periodsDet.AddRange(curSequences);
- scheduleResults.AddRange(curScheduleRsts);
- }
- /// <summary>
- /// 获取产线当天的开工时间,结束时间,有效工作时长,生产数量
- /// </summary>
- /// <param name="routingDto">产线</param>
- /// <param name="startTime"></param>
- /// <param name="curCalendars"></param>
- /// <param name="curQtyDtls"></param>
- /// <returns></returns>
- public LineScheduledDto GetScheduledPoint(WorkOrdRoutingDto routingDto,DateTime startTime, List<ShopCalendarWorkCtr> curCalendars, List<QualityLineWorkDetail> curQtyDtls)
- {
- LineScheduledDto scheduledDto = new LineScheduledDto();
- //当天排产开始时间
- scheduledDto.StartTime = startTime;
- //开始时间是周几
- int weekDay = (int)startTime.DayOfWeek;
- //当天的工作日历
- var shopCal = curCalendars.Where(p => p.WeekDay == weekDay).First();
- //当前日期的工作时间段
- List<LineWorkPointDto> workPoints = DealWorkDayToLevels(startTime, shopCal, curQtyDtls);
- //当天排产结束时间
- scheduledDto.EndTime = workPoints.Last().EndPoint;
- //计算starttime处于那个工作时间段
- var curPoint = workPoints.Where(p => startTime >= p.StartPoint && startTime <= p.EndPoint).First();
- TimeSpan span = curPoint.EndPoint - startTime;
- scheduledDto.EffTime = (decimal)span.TotalHours;
- //获取后续工作时间段的有效工作时间
- var nextPoints = workPoints.Where(p => p.Level > curPoint.Level).ToList();
- foreach (var item in nextPoints)
- {
- span = item.EndPoint - item.StartPoint;
- scheduledDto.EffTime += (decimal)span.TotalHours;
- }
- //计算当天的产能
- scheduledDto.ProductQty = scheduledDto.EffTime * routingDto.Rate;
- return scheduledDto;
- }
- /// <summary>
- /// 计算主产线实际排产开始时间
- /// </summary>
- /// <param name="startTime">工单排产开始时间</param>
- /// <param name="curCalendars">当前产线工作日历</param>
- /// <param name="curQtyDtls">当前产线休息记录</param>
- /// <returns></returns>
- public DateTime DealStartTime(DateTime startTime, List<ShopCalendarWorkCtr> curCalendars, List<QualityLineWorkDetail> curQtyDtls)
- {
- //实际排产开始时间
- DateTime actStart = startTime;
- //开始时间是周几
- int weekDay = (int)startTime.DayOfWeek;
- //当天的工作日历
- var shopCal = curCalendars.Where(p => p.WeekDay == weekDay).First();
- //当前日期的工作时间段
- List<LineWorkPointDto> workPoints = DealWorkDayToLevels(startTime, shopCal, curQtyDtls);
- //计算starttime处于那个工作时间段
- var curPoint = workPoints.Where(p => startTime >= p.StartPoint && startTime <= p.EndPoint).FirstOrDefault();
- if (startTime != curPoint.EndPoint)
- {
- return actStart;
- }
- //查询下一时间段的开始时间点
- var nextPoint = workPoints.Where(p => p.Level == curPoint.Level + 1).FirstOrDefault();
- if (nextPoint != null)
- {
- return nextPoint.StartPoint;
- }
- //开始时间为今天下班时间,实际排产开始时间为下一个工作日的开始时间
- actStart = GetNextWorkDay(weekDay, startTime, curCalendars);
- return actStart;
- }
- /// <summary>
- /// 计算子产线实际排产开始时间
- /// </summary>
- /// <param name="startTime">父级工单排产开始时间</param>
- /// <param name="setupTime">当前产线提前期(小时)-需要提前生产时长,不包括休息时间</param>
- /// <param name="curCalendars">当前产线工作日历</param>
- /// <param name="curQtyDtls">当前产线休息记录</param>
- /// <returns></returns>
- public DateTime DealChildStartTime(DateTime startTime,decimal setupTime, List<ShopCalendarWorkCtr> curCalendars, List<QualityLineWorkDetail> curQtyDtls)
- {
- //提前期转换成分钟
- decimal needMinute = setupTime * 60;
- //实际排产开始时间
- DateTime actStart = startTime;
- //开始时间是周几
- int weekDay = (int)startTime.DayOfWeek;
- //当天的工作日历
- var shopCal = curCalendars.Where(p => p.WeekDay == weekDay).First();
- //当前日期的工作时间段
- List<LineWorkPointDto> workPoints = DealWorkDayToLevels(startTime, shopCal, curQtyDtls);
- //计算starttime处于那个工作时间段
- var curPoint = workPoints.Where(p => startTime >= p.StartPoint && startTime <= p.EndPoint).FirstOrDefault();
- //当前时间段可用提前期
- TimeSpan span = startTime - curPoint.StartPoint;
- decimal curMins = (decimal)span.TotalMinutes;
- if (curMins >= needMinute)//当前时间段的可用提前期满足
- {
- actStart = startTime.AddMinutes((double)-needMinute);
- return actStart;
- }
- //当前时间段的可用提前期不满足
- //剩余提前期
- needMinute -= curMins;
- //获取前层级时间段
- var prePoints = workPoints.Where(p => p.Level < curPoint.Level).ToList();
- foreach (var item in prePoints)
- {
- if (item.WorkMinutes >= needMinute)//当前时间段的可用提前期满足
- {
- actStart = item.EndPoint.AddMinutes((double)-needMinute);
- break;
- }
- needMinute -= item.WorkMinutes;
- }
- //今天可用提前期不够,往前工作日找
- DateTime perStartTime = startTime;
- bool flag = true;//标志位
- while (flag)
- {
- //获取前一个工作日
- perStartTime = GetPreWorkDay(perStartTime, curCalendars);
- //获取前一个工作日的工作时间段数据,倒序排
- workPoints = DealWorkDayToLevels(perStartTime, shopCal, curQtyDtls).OrderByDescending(p=>p.Level).ToList();
- //当天的工作时长(分钟)
- decimal sumWorkMins = workPoints.Sum(p => p.WorkMinutes);
- if (sumWorkMins >= needMinute)//当天可用提前期满足
- {
- //获取开始时间
- foreach (var item in workPoints)
- {
- if (item.WorkMinutes >= needMinute)//当前时间段满足
- {
- actStart = item.EndPoint.AddMinutes((double)-needMinute);
- break;
- }
- needMinute -= item.WorkMinutes;
- }
- flag = false;
- }
- //当天可用提前期不满足
- needMinute -= sumWorkMins;
- }
- return actStart;
- }
- /// <summary>
- /// 获取下一个工作日开始时间
- /// </summary>
- /// <param name="weekDay">当前周几</param>
- /// <param name="startTime">开始时间</param>
- /// <param name="curCalendars">当前产线的工作日历</param>
- /// <returns></returns>
- public DateTime GetNextWorkDay(int weekDay, DateTime startTime, List<ShopCalendarWorkCtr> curCalendars)
- {
- DateTime rtnData = startTime;
- //下一天
- DateTime nextDate = startTime.Date.AddDays(1);
- //下一天是周几
- int nextWeekDay = (weekDay + 1) % 7;
- var calendar = curCalendars.FirstOrDefault(p=>p.WeekDay == nextWeekDay);
- //判断下一天是否是工作日
- if (nextWeekDay == 0 || nextWeekDay == 6)//下一天是周六或者周日,需要判断是否调休,需要加班
- {
- if (!holidays.Exists(p => p.Dated.GetValueOrDefault().Date == nextDate && p.Ufld1 == "调休"))//下一天是周末
- {
- //递归继续找下一个工作日
- GetNextWorkDay(nextWeekDay, nextDate, curCalendars);
- }
- rtnData = nextDate.AddHours((double)calendar.ShiftsStart1);
- return rtnData;
- }
- //下一天不是周六周日,需要判断是不是节假日
- if (holidays.Exists(p => p.Dated.GetValueOrDefault().Date == nextDate && p.Ufld1 == "休假"))//是节假日
- {
- //递归继续找下一个工作日
- GetNextWorkDay(nextWeekDay, nextDate, curCalendars);
- }
- rtnData = nextDate.AddHours((double)calendar.ShiftsStart1);
- return rtnData;
- }
- /// <summary>
- /// 获取上一个工作日开始时间
- /// </summary>
- /// <param name="startTime">开始时间</param>
- /// <param name="curCalendars">当前产线的工作日历</param>
- /// <returns></returns>
- public DateTime GetPreWorkDay(DateTime startTime, List<ShopCalendarWorkCtr> curCalendars)
- {
- DateTime rtnData = startTime;
- //前一天
- DateTime preDate = startTime.Date.AddDays(-1);
- //前一天是周几
- int preWeekDay = (int)preDate.DayOfWeek;
- var calendar = curCalendars.FirstOrDefault(p => p.WeekDay == preWeekDay);
- //判断前一天是否是工作日
- if (preWeekDay == 0 || preWeekDay == 6)//前一天是周六或者周日,需要判断是否调休,需要加班
- {
- if (!holidays.Exists(p => p.Dated.GetValueOrDefault().Date == preDate && p.Ufld1 == "调休"))//前一天是非工作日
- {
- //递归继续找下一个工作日
- GetPreWorkDay(preDate, curCalendars);
- }
- rtnData = preDate.AddHours((double)calendar.ShiftsStart1);
- return rtnData;
- }
- //前一天不是周六周日,需要判断是不是节假日
- if (holidays.Exists(p => p.Dated.GetValueOrDefault().Date == preDate && p.Ufld1 == "休假"))//是节假日
- {
- //递归继续找前一个工作日
- GetPreWorkDay(preDate, curCalendars);
- }
- rtnData = preDate.AddHours((double)calendar.ShiftsStart1);
- return rtnData;
- }
- /// <summary>
- /// 处理当前日期的工作时间段
- /// </summary>
- /// <param name="startTime"></param>
- /// <param name="shopCal">当前产线的工作日历-周几</param>
- /// <param name="curQtyDtls">每天休息记录</param>
- /// <returns></returns>
- public List<LineWorkPointDto> DealWorkDayToLevels(DateTime startTime, ShopCalendarWorkCtr shopCal, List<QualityLineWorkDetail> curQtyDtls)
- {
- //年-月-日
- string date = startTime.Date.ToString("yyyy-MM-dd");
- //排产记录结束日期是周几
- int weekDay = (int)startTime.DayOfWeek;
- //计算当天的开工时间点,停工时间点
- DateTime dayStartPoint = startTime.Date.AddHours(Convert.ToDouble(shopCal.ShiftsStart1));
- DateTime dayEndPoint = dayStartPoint.AddHours(Convert.ToDouble(shopCal.ShiftsHours1));
- //工作时间段
- List<LineWorkPointDto> workPoints = new List<LineWorkPointDto>();
- LineWorkPointDto dto = new LineWorkPointDto();
- dto.Level = 1;
- dto.Line = shopCal.ProdLine;
- dto.WeekDay = weekDay;
- dto.StartPoint = dayStartPoint;
- int level = 1;
- TimeSpan span = TimeSpan.Zero;
- foreach (var item in curQtyDtls)
- {
- DateTime endPoint = Convert.ToDateTime(date + " " + item.RestTimePoint);
- dto.EndPoint= endPoint;
- span = dto.EndPoint - dto.StartPoint;
- dto.WorkMinutes = (decimal)span.TotalMinutes;
- workPoints.Add(dto);
- level++;
- dto = new LineWorkPointDto();
- dto.Level = level;
- dto.Line = shopCal.ProdLine;
- dto.WeekDay = weekDay;
- dto.StartPoint = endPoint.AddMinutes(item.RestTime);
- }
- dto.EndPoint = dayEndPoint;
- span = dto.EndPoint - dto.StartPoint;
- dto.WorkMinutes = (decimal)span.TotalMinutes;
- workPoints.Add(dto);
- return workPoints.OrderBy(p => p.Level).ToList();
- }
- /// <summary>
- /// 工单工艺路线预处理
- /// </summary>
- /// <param name="workOrd">工单</param>
- /// <param name="woRuntings">当前工单对应的工序</param>
- /// <param name="prodLines">产线</param>
- /// <param name="schedules">当前工单对应产品的排产记录</param>
- /// <returns></returns>
- public List<WorkOrdRoutingDto> ProcPretreatment(WorkOrdMaster workOrd,List<WorkOrdRouting> woRuntings, List<ProdLineDetail> prodLines, List<ScheduleResultOpMaster> schedules)
- {
- List<WorkOrdRoutingDto> routingDtos = new List<WorkOrdRoutingDto>();
- //当前工单计划开始时间
- DateTime planStart = workOrd.OrdDate.GetValueOrDefault();
- //取主工序(第一层级工序)
- 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;
- //主工序对应的产线
- 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);
- 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)
- {
- return;
- }
- //获取父级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;
- //当前层级工序对应的产线,子产线有多条,则取产能最大的那一条
- 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);
- }
- }
- }
|