using Business.Business.Dto;
using Business.Core.Utilities;
using Business.Dto;
using Business.EntityFrameworkCore;
using Business.EntityFrameworkCore.SqlRepositories;
using Business.Model.MES.IC;
using Business.Model.Production;
using EFCore.BulkExtensions;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using System.Transactions;
using Volo.Abp.Application.Services;
using Volo.Abp.MultiTenancy;
namespace Business.ResourceExamineManagement
{
///
/// 生产排产服务
///
public class ProductionScheduleAppService : ApplicationService
{
#region 服务
///
/// 物料
///
private ISqlRepository _itemMaster;
///
/// 工单
///
private ISqlRepository _workOrdMaster;
///
/// 工单物料明细
///
private ISqlRepository _workOrdDetail;
///
/// 工单工艺路线明细
///
private ISqlRepository _workOrdRouting;
///
/// 库存主数据
///
private ISqlRepository _invMaster;
///
/// 生产线明细
///
private ISqlRepository _prodLineDetail;
///
/// 生产周期明细
///
private ISqlRepository _periodSequenceDet;
///
/// 排产结果明细
///
private ISqlRepository _scheduleResultOpMaster;
///
/// 工作日历数据
///
private ISqlRepository _shopCalendarWorkCtr;
///
/// 产线休息时间记录表
///
private ISqlRepository _qualityLineWorkDetail;
///
/// 节假日记录表
///
private ISqlRepository _holidayMaster;
///
/// 雪花算法
///
SnowFlake help = new SnowFlake();
private readonly ICurrentTenant _currentTenant;
///
/// 工作日历数据
///
private List calendars;
///
/// 产线休息记录数据
///
private List qualityLines;
///
/// 节假日记录数据
///
private List holidays;
#endregion
#region 构造函数
///
/// 构造函数
///
public ProductionScheduleAppService(
ISqlRepository itemMaster,
ISqlRepository workOrdMaster,
ISqlRepository workOrdDetail,
ISqlRepository workOrdRouting,
ISqlRepository prodLineDetail,
ISqlRepository periodSequenceDet,
ISqlRepository scheduleResultOpMaster,
ISqlRepository invMaster,
ISqlRepository shopCalendarWorkCtr,
ISqlRepository qualityLineWorkDetail,
ISqlRepository holidayMaster,
ICurrentTenant currentTenant
)
{
_itemMaster = itemMaster;
_workOrdMaster = workOrdMaster;
_workOrdDetail = workOrdDetail;
_workOrdRouting = workOrdRouting;
_prodLineDetail = prodLineDetail;
_periodSequenceDet = periodSequenceDet;
_scheduleResultOpMaster = scheduleResultOpMaster;
_invMaster = invMaster;
_shopCalendarWorkCtr = shopCalendarWorkCtr;
_qualityLineWorkDetail = qualityLineWorkDetail;
_holidayMaster = holidayMaster;
_currentTenant = currentTenant;
}
#endregion
///
/// 执行生产排产
///
public async void DoExt()
{
List workOrds = new List();
await DoProductShcedule(workOrds);
}
///
/// 生产排产
///
/// 工单:定时任务执行时count=0;资源检查调用count>0
///
public async Task DoProductShcedule(List workOrds)
{
if (workOrds.Count == 0)//定时任务调用时,需要获取工单数据
{
//1、获取需要排产的工单:Status为空且IsActive==1
workOrds = _workOrdMaster.Select(p => string.IsNullOrEmpty(p.Status) && p.IsActive);
}
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;
}
//2、获取数据
//获取工单工艺路径数据
List workOrdRoutings = _workOrdRouting.Select(p => workOrds.Select(m => m.WorkOrd).Contains(p.WorkOrd) && p.Domain == "1001" && p.Status != "C" && p.IsActive);
//获取物料对应的生产线信息:物料、工序对应的生产线
List prodLines = _prodLineDetail.Select(p => workOrds.Select(m => m.ItemNum).Contains(p.Part) && p.Domain == "1001" && p.IsActive);
List lines = prodLines.Select(p => p.Line).ToList();
//获取生产周期数据
List dbPeriodSequences = _periodSequenceDet.Select(p => lines.Contains(p.Line) && p.PlanDate >= earlist && p.Domain == "1001" && p.IsActive);
//获取当前日期往后的排产记录数据
List dbSchedules = _scheduleResultOpMaster.Select(p => lines.Contains(p.Line) && p.WorkDate >= earlist && p.Domain == "1001");
//获取工作日历数据
calendars = _shopCalendarWorkCtr.Select(p => p.Domain == "1001" && p.IsActive);
//获取产线休息记录数据
qualityLines = _qualityLineWorkDetail.Select(p => p.Domain == "1001" && p.IsActive);
//获取节假日记录数据
holidays = _holidayMaster.Select(p => p.Domain == "1001" && p.IsActive && p.Dated >= earlist);
//3、排产
//生产周期
List periodSequenceDtls = new List();
//排产记录表
List scheduleMasters = new List();
//排产结果
List allResults = new List();
allResults.AddRange(dbSchedules);
foreach (var item in workOrds)
{
////当前工单的排产计划开始时间:年-月-日
//DateTime planStart = item.OrdDate.GetValueOrDefault().Date;
////当前工单对应的产线数据
//var curLines = prodLines.Where(p => p.Part == item.ItemNum).Select(m=>m.Line).Distinct().ToList();
////当前工单的对应的产线排产记录
//var curSchedules = dbSchedules.Where(p => curLines.Contains(p.Line)).ToList();
allResults.AddRange(scheduleMasters);
//工序预处理:确定每层级工序对应的产线
List routingDtos = ProcPretreatment(item, workOrdRoutings.Where(p => p.WorkOrd == item.WorkOrd).ToList(), prodLines, allResults);
//排产前的数据校验
if (routingDtos.Count == 0)//没有维护主工序
{
//记录排产异常原因
new NLogHelper("ProductionScheduleAppService").WriteLog("DoProductShcedule", "工单<" + item.WorkOrd + ">的工序数据维护错误", _currentTenant.Id.ToString());
continue;
}
//校验每层级工序是否都维护了产线
if (routingDtos.Exists(p => string.IsNullOrEmpty(p.Line)))
{
//记录排产异常原因
new NLogHelper("ProductionScheduleAppService").WriteLog("DoProductShcedule", "工单<" + item.WorkOrd + ">的产线数据维护错误", _currentTenant.Id.ToString());
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)
{
//记录排产异常原因
new NLogHelper("ProductionScheduleAppService").WriteLog("DoProductShcedule", "工单<" + item.WorkOrd + ">的产线工作日历数据维护错误", _currentTenant.Id.ToString());
continue;
}
//产线排产
LineSchedule(item, routingDtos.OrderBy(p => p.level).ToList(), periodSequenceDtls, scheduleMasters);
List scheduleList = scheduleMasters.Where(s => s.WorkOrd == item.WorkOrd).ToList();
if (scheduleList.Any())
{
item.OrdDate = scheduleList.Min(s => s.WorkStartTime.Date);
item.DueDate = scheduleList.Max(s => s.WorkEndTime.Date);
}
/*//更新工单表
item.Status = "r";*/
}
using (TransactionScope scope = new TransactionScope())
{
try
{
//记录排产数据
_workOrdMaster.Update(workOrds);
_periodSequenceDet.Insert(periodSequenceDtls);
_scheduleResultOpMaster.Insert(scheduleMasters);
scope.Complete();
}
catch (Exception ex)
{
new NLogHelper("ProductionScheduleAppService").WriteLog("DoProductShcedule", "记录排产数据失败:" + ex.Message, _currentTenant.Id.ToString());
scope.Dispose();
}
}
}
///
/// 排产
///
/// 工单
/// 每层级工序对应的产线信息,从小到大排序
/// 生产周期
/// 排产结果
public void LineSchedule(WorkOrdMaster workOrd, List routingDtos, List periodsDet, List scheduleResults)
{
//生产周期
List curSequences = new List();
//排产明细
List curScheduleRsts = new List();
//产线排产开始时间
List lineStarts = new List();
//循环产线,排产
foreach (var item in routingDtos)
{
//当前产线的工作日历
var mLCalendars = calendars.Where(p => p.ProdLine == item.Line).ToList();
//当前产线的每天休息时间记录
var mlqtyWorkDtls = qualityLines.Where(p => p.ProdLine == item.Line).OrderBy(p => p.Line).ToList();
//产线已排产数量
decimal sumQty = 0m;
//产线实际排产开始时间
DateTime workStartTime;
if (item.level == 1)//主产线
{
workStartTime = DealStartTime(item.StartTime, mLCalendars, mlqtyWorkDtls);
}
else
{
//子产线获取实际排产开始日期
//获取父级排产开始时间
DateTime parentStartTime = lineStarts.First(p => p.ChdParentOps.Contains(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,
ChdParentOps = item.ChdParentOps
});
//排产
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,
Op = item.Op,
IsActive = true,
Status = "",
CreateTime = DateTime.Now
});
//记录排产记录
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,
CreateTime = DateTime.Now
});
//累计已排产数量
sumQty += dto.ProductQty;
//继续排下一个工作日
workStartTime = GetNextWorkDay((int)workStartTime.DayOfWeek, workStartTime, mLCalendars);
}
else// 最后一天的产能只能占用一部分
{
//剩余需要排产的数量
decimal residueQty = workOrd.QtyOrded - sumQty;
//剩余数量生产需要时长(分钟)
decimal workTime = Math.Ceiling(residueQty / item.Rate * 60);
//获取当天的工作时间段
List workPoints = DealWorkDayToLevels(workStartTime, mLCalendars.First(p => p.WeekDay == (int)workStartTime.DayOfWeek), mlqtyWorkDtls);
var curPoint = workPoints.Find(p => p.StartPoint <= workStartTime && workStartTime <= p.EndPoint);
if (curPoint == null)
{
continue;
}
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;
}
}
sumQty = workOrd.QtyOrded;
//记录生产周期
curSequences.Add(new PeriodSequenceDet
{
Domain = "1001",
Line = item.Line,
ItemNum = workOrd.ItemNum,
PlanDate = workStartTime.Date,
Period = 1,//目前只考虑一班制
OrdQty = residueQty,
WorkOrds = workOrd.WorkOrd,
Op = item.Op,
IsActive = true,
Status = "",
CreateTime = DateTime.Now
});
//记录排产记录
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,
CreateTime = DateTime.Now
});
}
}
}
//记录排产结果
periodsDet.AddRange(curSequences);
scheduleResults.AddRange(curScheduleRsts);
}
///
/// 获取产线当天的开工时间,结束时间,有效工作时长,生产数量
///
/// 产线
///
///
///
///
public LineScheduledDto GetScheduledPoint(WorkOrdRoutingDto routingDto, DateTime startTime, List curCalendars, List curQtyDtls)
{
LineScheduledDto scheduledDto = new LineScheduledDto();
//当天排产开始时间
scheduledDto.StartTime = startTime;
//开始时间是周几
int weekDay = (int)startTime.DayOfWeek;
//当天的工作日历
var shopCal = curCalendars.Where(p => p.WeekDay == weekDay).First();
//当前日期的工作时间段
List 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 = Math.Floor(scheduledDto.EffTime * routingDto.Rate);
return scheduledDto;
}
///
/// 计算主产线实际排产开始时间
///
/// 工单排产开始时间
/// 当前产线工作日历
/// 当前产线休息记录
///
public DateTime DealStartTime(DateTime startTime, List curCalendars, List curQtyDtls)
{
//实际排产开始时间
DateTime actStart = startTime;
//开始时间是周几
int weekDay = (int)startTime.DayOfWeek;
//判断当天是否是工作日
bool isWorkDay = CheckIsWorkDay(startTime);
if (!isWorkDay)//不是工作日
{
//获取下一个工作日开始时间
actStart = GetNextWorkDay(weekDay, startTime, curCalendars);
return actStart;
}
//开始时间往后推到半小时/整点节点
actStart = CalcStartTimeAfter(actStart);
//当天的工作日历
var shopCal = curCalendars.Where(p => p.WeekDay == weekDay).First();
//当前日期的工作时间段
List workPoints = DealWorkDayToLevels(startTime, shopCal, curQtyDtls);
//计算starttime处于那个工作时间段
var curPoint = workPoints.Where(p => startTime >= p.StartPoint && startTime <= p.EndPoint).FirstOrDefault();
if (curPoint == null)//不处于工作时间段
{
//开始时间小于当天工作开始时间
if (startTime < workPoints.First().StartPoint)
{
actStart = workPoints.First().StartPoint;
}
//开始时间大于当前工作结束时间
else if (startTime > workPoints.Last().EndPoint)
{
//获取下一个工作日开始时间
actStart = GetNextWorkDay(weekDay, startTime, curCalendars);
}
else
{//开始时间位于当天的休息时间段
foreach (var item in workPoints)
{
//获取下一个时间段
var next = workPoints.First(p => p.Level == item.Level + 1);
if (item.EndPoint < startTime && startTime < next.StartPoint)
{
actStart = next.StartPoint;
break;
}
}
}
return actStart;
}
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;
}
///
/// 排产开始时间处理为半小时/整点开始--向后取整
///
///
///
public DateTime CalcStartTimeAfter(DateTime startTime)
{
DateTime rtnTime = startTime;
DateTime curDate = startTime.Date;
//时间转换为分钟
TimeSpan span = rtnTime - curDate;
decimal sumMinutes = (decimal)span.TotalMinutes;
int times = (int)Math.Ceiling(sumMinutes / 30);
rtnTime = curDate.AddMinutes(times * 30);
return rtnTime;
}
///
/// 排产开始时间处理为半小时/整点开始--向前取整
///
///
///
public DateTime CalcStartTimeBefore(DateTime startTime)
{
DateTime rtnTime = startTime;
DateTime curDate = startTime.Date;
//时间转换为分钟
TimeSpan span = rtnTime - curDate;
decimal sumMinutes = (decimal)span.TotalMinutes;
int times = (int)Math.Floor(sumMinutes / 30);
rtnTime = curDate.AddMinutes(times * 30);
return rtnTime;
}
///
/// 判断当天是否是工作日
///
///
///
public bool CheckIsWorkDay(DateTime dateTime)
{
bool isWorkDay = true;
//周几
int weekDay = (int)dateTime.DayOfWeek;
//判断当天是否是工作日
if (weekDay == 0 || weekDay == 6)//周六或者周日,需要判断是否调班,需要加班
{
if (!holidays.Exists(p => p.Dated.GetValueOrDefault().Date == dateTime.Date && p.Ufld1 == "调班"))//不是调班
{
isWorkDay = false;
}
return isWorkDay;
}
//不是周六周日,需要判断是不是节假日
if (holidays.Exists(p => p.Dated.GetValueOrDefault().Date == dateTime.Date && p.Ufld1 == "休假"))//是节假日
{
isWorkDay = false;
}
return isWorkDay;
}
///
/// 计算子产线实际排产开始时间
///
/// 父级工单排产开始时间
/// 当前产线提前期(小时)-需要提前生产时长,不包括休息时间
/// 当前产线工作日历
/// 当前产线休息记录
///
public DateTime DealChildStartTime(DateTime startTime, decimal setupTime, List curCalendars, List curQtyDtls)
{
//提前期转换成分钟
decimal needMinute = setupTime * 60;
//实际排产开始时间
DateTime actStart = startTime;
//开始时间是周几
int weekDay = (int)startTime.DayOfWeek;
//当天的工作日历
var shopCal = curCalendars.Where(p => p.WeekDay == weekDay).First();
//当前日期的工作时间段
List 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);
//开始时间往前推到半小时/整点节点
actStart = CalcStartTimeBefore(actStart);
return actStart;
}
//当前时间段的可用提前期不满足
//剩余提前期
needMinute -= curMins;
//获取前层级时间段
var prePoints = workPoints.Where(p => p.Level < curPoint.Level).OrderByDescending(p => p.Level).ToList();
bool flag = true;//标志位
foreach (var item in prePoints)
{
if (item.WorkMinutes >= needMinute)//当前时间段的可用提前期满足
{
actStart = item.EndPoint.AddMinutes((double)-needMinute);
flag = false;
break;
}
needMinute -= item.WorkMinutes;
}
if (!flag)
{
//开始时间往前推到半小时/整点节点
actStart = CalcStartTimeBefore(actStart);
return actStart;
}
//今天可用提前期不够,往前工作日找
DateTime perStartTime = startTime;
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;
}
else
{
//当天可用提前期不满足
needMinute -= sumWorkMins;
}
}
//开始时间往前推到半小时/整点节点
actStart = CalcStartTimeBefore(actStart);
return actStart;
}
///
/// 获取下一个工作日开始时间
///
/// 当前周几
/// 开始时间
/// 当前产线的工作日历
///
public DateTime GetNextWorkDay(int weekDay, DateTime startTime, List curCalendars)
{
DateTime rtnData = startTime;
//下一天
DateTime nextDate = startTime.Date.AddDays(1);
//下一天是周几
int nextWeekDay = (weekDay + 1) % 7;
var calendar = curCalendars.FirstOrDefault(p => p.WeekDay == nextWeekDay);
string strStart = calendar.ShiftsStart1.ToString("0.00").Replace(".", ":");
//判断下一天是否是工作日
if (nextWeekDay == 0 || nextWeekDay == 6)//下一天是周六或者周日,需要判断是否调班,需要加班
{
if (!holidays.Exists(p => p.Dated.GetValueOrDefault().Date == nextDate && p.Ufld1 == "调班"))//下一天是周末
{
//递归继续找下一个工作日
rtnData = GetNextWorkDay(nextWeekDay, nextDate, curCalendars);
return rtnData;
}
rtnData = Convert.ToDateTime(nextDate.ToString("yyyy-MM-dd") + " " + strStart);
return rtnData;
}
//下一天不是周六周日,需要判断是不是节假日
if (holidays.Exists(p => p.Dated.GetValueOrDefault().Date == nextDate && p.Ufld1 == "休假"))//是节假日
{
//递归继续找下一个工作日
rtnData = GetNextWorkDay(nextWeekDay, nextDate, curCalendars);
return rtnData;
}
rtnData = Convert.ToDateTime(nextDate.ToString("yyyy-MM-dd") + " " + strStart);
return rtnData;
}
///
/// 获取上一个工作日开始时间
///
/// 开始时间
/// 当前产线的工作日历
///
public DateTime GetPreWorkDay(DateTime startTime, List curCalendars)
{
DateTime rtnData = startTime;
//前一天
DateTime preDate = startTime.Date.AddDays(-1);
//前一天是周几
int preWeekDay = (int)preDate.DayOfWeek;
var calendar = curCalendars.FirstOrDefault(p => p.WeekDay == preWeekDay);
string strStart = calendar.ShiftsStart1.ToString("0.00").Replace(".", ":");
//判断前一天是否是工作日
if (preWeekDay == 0 || preWeekDay == 6)//前一天是周六或者周日,需要判断是否调班,需要加班
{
if (!holidays.Exists(p => p.Dated.GetValueOrDefault().Date == preDate && p.Ufld1 == "调班"))//前一天是非工作日
{
//递归继续找上一个工作日
rtnData = GetPreWorkDay(preDate, curCalendars);
return rtnData;
}
rtnData = Convert.ToDateTime(preDate.ToString("yyyy-MM-dd") + " " + strStart);
return rtnData;
}
//前一天不是周六周日,需要判断是不是节假日
if (holidays.Exists(p => p.Dated.GetValueOrDefault().Date == preDate && p.Ufld1 == "休假"))//是节假日
{
//递归继续找前一个工作日
rtnData = GetPreWorkDay(preDate, curCalendars);
return rtnData;
}
rtnData = Convert.ToDateTime(preDate.ToString("yyyy-MM-dd") + " " + strStart);
return rtnData;
}
///
/// 处理当前日期的工作时间段
///
///
/// 当前产线的工作日历-周几
/// 每天休息记录
///
public List DealWorkDayToLevels(DateTime startTime, ShopCalendarWorkCtr shopCal, List curQtyDtls)
{
//年-月-日
string date = startTime.Date.ToString("yyyy-MM-dd");
//排产记录结束日期是周几
int weekDay = (int)startTime.DayOfWeek;
//计算当天的开工时间点,停工时间点
string strStart = shopCal.ShiftsStart1.ToString("0.00").Replace(".", ":");
DateTime dayStartPoint = Convert.ToDateTime(date + " " + strStart);
DateTime dayEndPoint = dayStartPoint.AddHours(Convert.ToDouble(shopCal.ShiftsHours1));
//工作时间段
List workPoints = new List();
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();
}
///
/// 工单工艺路线预处理
///
/// 工单
/// 当前工单对应的工序
/// 产线
/// 当前工单对应产品的排产记录
///
public List ProcPretreatment(WorkOrdMaster workOrd, List woRuntings, List prodLines, List schedules)
{
List routingDtos = new List();
//当前工单计划开始时间(默认加两天)
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();
//获取当前层级工序中有子级的工序集合
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);
return routingDtos;
}
///
/// 递归处理工序
///
/// 工单工序
/// 上-层级工序
/// 层级
/// 返回结果
/// 产线
public void RecursionProc(List woRuntings, List preLevels, int level, List routingDtos, List 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;
dto.ChdParentOps = new List();
//获取当前层级工序中有子级的工序集合
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);
}
}
}