using Business.Core.MongoDBHelper;
using Business.ResourceExamineManagement.Dto;
using Bussiness.Model.Production;
using Bussiness.Model.Tech;
using Bussiness.MongoModel.Production;
using Bussiness.MongoModel.Tech;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
namespace Business.ResourceExamineManagement
{
///
/// 产能检查
///
public class ProductExamineAppService: ApplicationService
{
#region 服务
///
/// 工艺路径
///
private readonly IMongoDB _mes_technique;
///
/// 工序
///
private readonly IMongoDB _mes_process;
///
/// 工艺关联工序
///
private readonly IMongoDB _mes_tech_process;
///
/// 工作日历
///
private readonly IMongoDB _mes_work_calendar;
///
/// 工作日历明细
///
private readonly IMongoDB _mes_work_calendar_list;
///
/// 工艺工序关联工位
///
private readonly IMongoDB _mes_tech_proc_workshop;
///
/// 排程占用记录
///
private readonly IMongoDB _mes_schedule_occupy;
#endregion
///
/// 构造函数
///
public ProductExamineAppService(
IMongoDB mes_technique,
IMongoDB mes_process,
IMongoDB mes_tech_process,
IMongoDB mes_tech_proc_workshop
)
{
_mes_technique = mes_technique;
_mes_process = mes_process;
_mes_tech_process = mes_tech_process;
_mes_tech_proc_workshop = mes_tech_proc_workshop;
}
///
/// 产能计算
///
/// 产能检查入参
/// 生产时长
public async Task ProductiveExamine(ProdExamineParamDto param)
{
if (param.packages <= 0)
{
throw new NotImplementedException("产能计算参数有误!");
}
#region 1、数据准备
//1.1、获取工艺路径数据
mo_mes_technique tech = _mes_technique.Find(p => p.bom == param.bom_number && p.bomver == param.version && p.tenant_id == param.tenantId && p.factory_id == param.factoryId && !p.IsDeleted).Result.FirstOrDefault();
if (tech == null)
{
return param.packages * 100;
//throw new NotImplementedException("请先配置工艺路径!");
}
//1.2、获取工艺路径关联工序数据
List tech_Processes = await _mes_tech_process.GetManyByCondition(p => p.tech_id == tech.mysql_id && p.tenant_id == param.tenantId && p.factory_id == param.factoryId && !p.IsDeleted);
if (tech_Processes.Count == 0)
{
return param.packages * 100;
//throw new NotImplementedException("请先配置工序!");
}
//1.3、获取当前工艺路径下的工序数据
//FilterDefinition filter = Builders.Filter.In(s => s.Id, tech_Processes.Select(m => m.proc_id).ToList());
//List process = await _mes_process.GetManyByIds(filter);
List procIds = tech_Processes.Select(m => m.proc_id.GetValueOrDefault()).ToList();
List process = await _mes_process.GetManyByCondition(p => procIds.Contains(p.mysql_id) && p.tenant_id == param.tenantId && p.factory_id == param.factoryId && !p.IsDeleted);
//1.3、获取工艺工序关联工位信息
List techProcIds = tech_Processes.Select(m => m.mysql_id).ToList();
List tech_Proc_Workshops = await _mes_tech_proc_workshop.GetManyByCondition(p => techProcIds.Contains(p.tech_proc_id.Value) && p.tenant_id == param.tenantId && p.factory_id == param.factoryId && !p.IsDeleted);
#endregion
//1、获取工艺路径下的第一层级工序:目前只考虑第一层级
List fistLevels = tech_Processes.Where(p => p.parentprocid == tech.mysql_id).ToList();
if (fistLevels.Count == 0)
{
throw new NotImplementedException("当前工艺路径没有配置工序,请调整!");
}
//工艺预处理
List teches = TechProcPretreatment(fistLevels,param.packages);
decimal sumTimes = teches.OrderByDescending(p => p.sumTimes).First().sumTimes;
return sumTimes;
}
///
/// 工序预处理,按照多分支生成多条单独的工艺路径
///
/// 当前工序
/// 工艺关联工序list
/// 生产件数
///
private List TechProcPretreatment(List processes,int packages)
{
//工艺路径预处理dto
List techProcDtos = new List();
TechProcDto dto;
//获取下一步工序id
List nextProcIds = processes.Where(p=>p.nextprocid != null).Select(p => p.nextprocid.GetValueOrDefault()).ToList();
//获取起点工序
var startProcs = processes.Where(p => !nextProcIds.Contains(p.proc_id.GetValueOrDefault())).ToList();
//递归处理工序返回值
List rtnList;
for (int i = 0; i < startProcs.Count; i++)
{
dto = new TechProcDto();
rtnList = new List();
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;
}
///
/// 递归:根据起始工序,获取后续工序
///
///
///
///
private void GetNextProc(mo_mes_tech_process proc, List processes, List rtnList)
{
rtnList.Add(proc);
//下一工序id为null,终止
if (proc?.nextprocid == null)
{
return;
}
//获取下一个工序
var nextProc = processes.FirstOrDefault(p=>p.proc_id == proc.nextprocid);
if (nextProc == null)
{
return;
}
GetNextProc(nextProc, processes, rtnList);
}
///
/// 通过Lq计算工艺耗时
///
///
///
///
private List CalcTakeTimeByLq(List Processes, int packages)
{
//工序需要等待时间记录
List starts = new List();
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;
}
///
/// 通过Lq计算当前工序前置准备时间
///
///
/// LeadQuantity to Start Next
/// 件数
///
private StartTimeDto CalcProcTakeTimeByLq(mo_mes_tech_process proc, decimal quantity, int packages)
{
//记录当前工序耗时
StartTimeDto dto = new StartTimeDto();
//添加耗时记录
dto.tech_id = proc.tech_id.GetValueOrDefault();
dto.proc_id = proc.proc_id.GetValueOrDefault();
dto.nextproc_id = proc.nextprocid;
if (proc.wctype == 1)//人工型:数量/uph(一小时生产数量)*60(小时转换为分钟)/wsinuse(工位数)
{
if ( proc.uph.GetValueOrDefault() == 0 || proc.wsinuse.GetValueOrDefault() == 0)
{
throw new NotImplementedException("当前工序uph或wsinuse参数配置错误,请调整!");
}
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)
{
throw new NotImplementedException("当前工序ct或wsinuse参数配置错误,请调整!");
}
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)
{
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;
}
///
/// 通过Lqt计算工艺耗时
///
///
///
///
private List CalcTakeTimeByLqt(List Processes, int packages)
{
//工序需要等待时间记录
List starts = new List();
StartTimeDto dto;
foreach (var chd in Processes)
{
dto = new StartTimeDto();
//添加耗时记录
dto.tech_id = chd.tech_id.GetValueOrDefault();
dto.proc_id = chd.proc_id.GetValueOrDefault();
dto.nextproc_id = chd.nextprocid;
//计算当前工序生产耗时
dto.take_time = CalcProcTakeTime(chd, packages);
if (chd.nextprocid == null)//最后一个工序
{
dto.wait_time = dto.take_time;
}
else
{
dto.wait_time = chd.lqt.Value;
}
//添加记录
starts.Add(dto);
}
return starts;
}
///
/// 计算当前工序生产时间
///
///
/// 件数
///
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)
{
throw new NotImplementedException("当前工序uph或wsinuse参数配置错误,请调整!");
}
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)
{
throw new NotImplementedException("当前工序upe或ct或wsinuse参数配置错误,请调整!");
}
takeTiem = Math.Ceiling(packages / proc.upe.GetValueOrDefault() / proc.wsinuse.GetValueOrDefault()) * proc.ct.GetValueOrDefault();
}
return takeTiem;
}
}
}