ProductionScheduleAppService.cs 77 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490
  1. using Business.Business.Dto;
  2. using Business.Core.Utilities;
  3. using Business.Domain;
  4. using Business.Dto;
  5. using Business.EntityFrameworkCore.SqlRepositories;
  6. using Business.ResourceExamineManagement.Dto;
  7. using Business.StructuredDB.Production;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Collections.Immutable;
  11. using System.Configuration;
  12. using System.Linq;
  13. using System.Threading.Tasks;
  14. using System.Transactions;
  15. using Volo.Abp.Application.Services;
  16. using Volo.Abp.MultiTenancy;
  17. using Microsoft.Extensions.Configuration;
  18. using MongoDB.Driver.Linq;
  19. using Amazon.Runtime.Internal.Util;
  20. using IdentityModel.Client;
  21. namespace Business.ResourceExamineManagement
  22. {
  23. /// <summary>
  24. /// 生产排产服务
  25. /// </summary>
  26. public class ProductionScheduleAppService : ApplicationService
  27. {
  28. #region 服务
  29. /// <summary>
  30. /// 物料
  31. /// </summary>
  32. private ISqlRepository<ItemMaster> _itemMaster;
  33. /// <summary>
  34. /// 工单
  35. /// </summary>
  36. private ISqlRepository<WorkOrdMaster> _workOrdMaster;
  37. /// <summary>
  38. /// 工单物料明细
  39. /// </summary>
  40. private ISqlRepository<WorkOrdDetail> _workOrdDetail;
  41. /// <summary>
  42. /// 工单工艺路线明细
  43. /// </summary>
  44. private ISqlRepository<WorkOrdRouting> _workOrdRouting;
  45. /// <summary>
  46. /// 库存主数据
  47. /// </summary>
  48. private ISqlRepository<InvMaster> _invMaster;
  49. /// <summary>
  50. /// 生产线明细
  51. /// </summary>
  52. private ISqlRepository<ProdLineDetail> _prodLineDetail;
  53. /// <summary>
  54. /// 生产周期明细
  55. /// </summary>
  56. private ISqlRepository<PeriodSequenceDet> _periodSequenceDet;
  57. /// <summary>
  58. /// 排产结果明细
  59. /// </summary>
  60. private ISqlRepository<ScheduleResultOpMaster> _scheduleResultOpMaster;
  61. /// <summary>
  62. /// 工作日历数据
  63. /// </summary>
  64. private ISqlRepository<ShopCalendarWorkCtr> _shopCalendarWorkCtr;
  65. /// <summary>
  66. /// 产线休息时间记录表
  67. /// </summary>
  68. private ISqlRepository<QualityLineWorkDetail> _qualityLineWorkDetail;
  69. /// <summary>
  70. /// 产线人员配置表
  71. /// </summary>
  72. private ISqlRepository<ProdLineDetailRunCrew> _prodLineDetailRunCrew;
  73. /// <summary>
  74. /// 加班设置表
  75. /// </summary>
  76. private ISqlRepository<ResourceOccupancyTime> _resourceOccupancyTime;
  77. /// <summary>
  78. /// 加班设置表
  79. /// </summary>
  80. private ISqlRepository<GeneralizedCodeMaster> _generalizedCodeMaster;
  81. /// <summary>
  82. /// 节假日记录表
  83. /// </summary>
  84. private ISqlRepository<HolidayMaster> _holidayMaster;
  85. /// <summary>
  86. /// 排产异常记录
  87. /// </summary>
  88. private ISqlRepository<ScheduleExceptionMaster> _scheduleExceptionMaster;
  89. /// <summary>
  90. /// 雪花算法
  91. /// </summary>
  92. SnowFlake help = new SnowFlake();
  93. private readonly ICurrentTenant _currentTenant;
  94. /// <summary>
  95. /// 工作日历数据
  96. /// </summary>
  97. private List<ShopCalendarWorkCtr> calendars;
  98. /// <summary>
  99. /// 产线休息记录数据
  100. /// </summary>
  101. private List<QualityLineWorkDetail> qualityLines;
  102. /// <summary>
  103. /// 节假日记录数据
  104. /// </summary>
  105. private List<HolidayMaster> holidays;
  106. /// <summary>
  107. /// 生产线UPH设置
  108. /// </summary>
  109. private List<ProdLineDetailRunCrew> prodLineDetailRunCrews;
  110. /// <summary>
  111. /// 加班设置
  112. /// </summary>
  113. private List<ResourceOccupancyTime> resourceOccupancyTimes;
  114. /// <summary>
  115. /// 工厂id
  116. /// </summary>
  117. private string domain = "";
  118. #endregion
  119. #region 构造函数
  120. /// <summary>
  121. /// 构造函数
  122. /// </summary>
  123. public ProductionScheduleAppService(
  124. ISqlRepository<ItemMaster> itemMaster,
  125. ISqlRepository<WorkOrdMaster> workOrdMaster,
  126. ISqlRepository<WorkOrdDetail> workOrdDetail,
  127. ISqlRepository<WorkOrdRouting> workOrdRouting,
  128. ISqlRepository<ProdLineDetail> prodLineDetail,
  129. ISqlRepository<ProdLineDetailRunCrew> prodLineDetailRunCrew,
  130. ISqlRepository<ResourceOccupancyTime> resourceOccupancyTime,
  131. ISqlRepository<PeriodSequenceDet> periodSequenceDet,
  132. ISqlRepository<ScheduleResultOpMaster> scheduleResultOpMaster,
  133. ISqlRepository<InvMaster> invMaster,
  134. ISqlRepository<ShopCalendarWorkCtr> shopCalendarWorkCtr,
  135. ISqlRepository<QualityLineWorkDetail> qualityLineWorkDetail,
  136. ISqlRepository<HolidayMaster> holidayMaster,
  137. ISqlRepository<GeneralizedCodeMaster> generalizedCodeMaster,
  138. ICurrentTenant currentTenant,
  139. ISqlRepository<ScheduleExceptionMaster> scheduleExceptionMaster
  140. )
  141. {
  142. _itemMaster = itemMaster;
  143. _workOrdMaster = workOrdMaster;
  144. _workOrdDetail = workOrdDetail;
  145. _workOrdRouting = workOrdRouting;
  146. _prodLineDetail = prodLineDetail;
  147. _prodLineDetailRunCrew = prodLineDetailRunCrew;
  148. _resourceOccupancyTime = resourceOccupancyTime;
  149. _periodSequenceDet = periodSequenceDet;
  150. _scheduleResultOpMaster = scheduleResultOpMaster;
  151. _invMaster = invMaster;
  152. _shopCalendarWorkCtr = shopCalendarWorkCtr;
  153. _qualityLineWorkDetail = qualityLineWorkDetail;
  154. _holidayMaster = holidayMaster;
  155. _generalizedCodeMaster = generalizedCodeMaster;
  156. _currentTenant = currentTenant;
  157. _scheduleExceptionMaster = scheduleExceptionMaster;
  158. }
  159. #endregion
  160. /// <summary>
  161. /// 生产排产-定时任务
  162. /// </summary>
  163. public async void DoExt()
  164. {
  165. //定时任务排产:获取工厂id
  166. IConfiguration configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).Build();
  167. domain = configuration.GetConnectionString("Factory_id");
  168. //获取排产锁定期
  169. var generalizedCodeMaster = _generalizedCodeMaster.Select(p => p.FldName == "SystemConfig" && p.Val == "WorkOrderLockPeriod" && p.Domain == domain && p.IsActive).FirstOrDefault();
  170. decimal lockDays = generalizedCodeMaster != null ? generalizedCodeMaster.UDeci1 : 0;
  171. //获取需要排产的工单(获取四周的工单:正常工单+已审批通过的特殊工单)
  172. DateTime endDate = DateTime.Now.Date.AddWeeks(4).AddDays(1);
  173. DateTime startDate = DateTime.Now.Date;
  174. var workOrds = _workOrdMaster.Select(p => p.IsActive && p.Domain == domain && p.OrdDate < endDate && p.OrdDate >= startDate && string.IsNullOrEmpty(p.Status) && (string.IsNullOrEmpty(p.Typed) || (!string.IsNullOrEmpty(p.Typed) && p.BusinessID >0 ))).ToList();
  175. await DoProductSchedule(workOrds, domain, 1);
  176. }
  177. /// <summary>
  178. /// 生产排产
  179. /// </summary>
  180. /// <param name="workOrds">工单</param>
  181. /// <param name="factoryid">工单的工厂id</param>
  182. /// <param name="type">排产类型:1-自动排产;2-手动排产</param>
  183. /// <returns></returns>
  184. public async Task DoProductSchedule(List<WorkOrdMaster> workOrds, string factoryid,int type)
  185. {
  186. //记录工厂id
  187. domain = factoryid;
  188. if (workOrds.Count == 0)//没有工单需要排产
  189. {
  190. return;
  191. }
  192. //获取排产工单的最早计划开工日期
  193. DateTime earlist = DateTime.Now.Date.AddDays(1);
  194. //特殊工单指定时间开工,不需要计算产能,只需要计算时长即可
  195. var tsWorkOrds = workOrds.Where(p => !string.IsNullOrEmpty(p.Typed)).ToList();
  196. //正常工单
  197. var zcWorkOrds = workOrds.Where(p => string.IsNullOrEmpty(p.Typed)).ToList();
  198. //2、获取数据
  199. //获取工单工艺路径数据
  200. 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);
  201. //特殊工单的工艺路线
  202. List<WorkOrdRouting> tsWoRoutings = workOrdRoutings.Where(p => tsWorkOrds.Select(m => m.WorkOrd).Contains(p.WorkOrd)).ToList();
  203. //获取物料对应的生产线信息:物料、工序对应的生产线
  204. List<ProdLineDetail> prodLines = _prodLineDetail.Select(p => workOrds.Select(m => m.ItemNum).Contains(p.Part) && p.Domain == domain && p.IsActive);
  205. List<string> lines = prodLines.Select(p => p.Line).ToList();
  206. //获取非标准产线人员配置
  207. prodLineDetailRunCrews = _prodLineDetailRunCrew.Select(x => prodLines.Select(p => p.RecID).Contains(x.ProdLineDetailRecID) && x.IsActive && x.Domain == domain).ToList();
  208. //获取加班设置
  209. resourceOccupancyTimes = _resourceOccupancyTime.Select(x => prodLines.Select(p => p.Line).Contains(x.Resource) && x.IsActive && x.Domain == domain && x.StartTime.Value > DateTime.Now).ToList();
  210. //获取生产周期数据,TODO:需要过滤锁定期之外的工单
  211. List<PeriodSequenceDet> dbPeriodSequences = _periodSequenceDet.Select(p => lines.Contains(p.Line) && p.PlanDate >= earlist && p.Domain == domain && p.IsActive);
  212. //获取当前日期往后的排产记录数据,TODO:需要过滤锁定期之外的工单
  213. List<ScheduleResultOpMaster> dbSchedules = _scheduleResultOpMaster.Select(p => lines.Contains(p.Line) && p.WorkDate >= earlist && p.Domain == domain);
  214. //获取工作日历数据:产线的工作日历+默认的工作日历
  215. calendars = _shopCalendarWorkCtr.Select(p => (lines.Contains(p.ProdLine) || string.IsNullOrEmpty(p.ProdLine)) && p.Domain == domain && p.IsActive);
  216. //获取产线休息记录数据
  217. qualityLines = _qualityLineWorkDetail.Select(p => lines.Contains(p.ProdLine) && p.Domain == domain && p.IsActive);
  218. //获取节假日记录数据
  219. holidays = _holidayMaster.Select(p => p.Domain == domain && p.IsActive && p.Dated >= earlist);
  220. //排产前校验
  221. List<ScheduleExceptionMaster> exceptions = BeforeScheduleCheck(workOrds, workOrdRoutings, prodLines,type);
  222. if (exceptions.Any())
  223. {
  224. using (TransactionScope scope = new TransactionScope())
  225. {
  226. try
  227. {
  228. //记录排产异常数据
  229. _scheduleExceptionMaster.Insert(exceptions);
  230. scope.Complete();
  231. }
  232. catch (Exception ex)
  233. {
  234. new NLogHelper("ProductionScheduleAppService").WriteLog("DoProductSchedule", "保存排产异常数据失败:" + ex.Message, _currentTenant.Id.ToString());
  235. scope.Dispose();
  236. }
  237. }
  238. return;
  239. }
  240. //3、排产
  241. //生产周期
  242. List<PeriodSequenceDet> periodSequenceDtls = new List<PeriodSequenceDet>();
  243. //排产记录表
  244. List<ScheduleResultOpMaster> scheduleMasters = new List<ScheduleResultOpMaster>();
  245. //排产结果
  246. List<ScheduleResultOpMaster> allResults = new List<ScheduleResultOpMaster>();
  247. allResults.AddRange(dbSchedules);
  248. //记录特殊工单排产结果
  249. List<string> ypcWorkOrds = new List<string>();
  250. foreach (var item in zcWorkOrds)
  251. {
  252. //记录产线占用情况
  253. allResults.AddRange(scheduleMasters);
  254. //当前工单工艺路线主产线的关键工序
  255. var curRoutings = workOrdRoutings.Where(p => p.ItemNum == item.ItemNum && p.ParentOp == 0 && p.MilestoneOp).OrderBy(p=>p.OP).ToList();
  256. //当前工单的产线明细
  257. var curProdLines = prodLines.Where(p => p.Part == item.ItemNum).ToList();
  258. //产线排产
  259. LineSchedule(item, curRoutings, curProdLines, periodSequenceDtls, scheduleMasters, allResults, tsWorkOrds, ypcWorkOrds, tsWoRoutings);
  260. List<ScheduleResultOpMaster> scheduleList = scheduleMasters.Where(s => s.WorkOrd == item.WorkOrd).ToList();
  261. if (scheduleList.Any())
  262. {
  263. item.OrdDate = scheduleList.Min(s => s.WorkStartTime.Date);
  264. item.DueDate = scheduleList.Max(s => s.WorkEndTime.Date);
  265. }
  266. }
  267. using (TransactionScope scope = new TransactionScope())
  268. {
  269. try
  270. {
  271. //记录排产数据
  272. _workOrdMaster.Update(workOrds);
  273. _periodSequenceDet.Insert(periodSequenceDtls);
  274. _scheduleResultOpMaster.Insert(scheduleMasters);
  275. scope.Complete();
  276. }
  277. catch (Exception ex)
  278. {
  279. new NLogHelper("ProductionScheduleAppService").WriteLog("DoProductSchedule", "保存排产数据失败:" + ex.Message, _currentTenant.Id.ToString());
  280. scope.Dispose();
  281. }
  282. }
  283. }
  284. /// <summary>
  285. /// 排产前校验
  286. /// </summary>
  287. /// <param name="workOrds">待排产工单</param>
  288. /// <param name="workOrdRoutings">工单工艺路线</param>
  289. /// <param name="prodLines">产线明细</param>
  290. /// <param name="type">排产类型:1-自动排产,2-手动排产</param>
  291. /// <returns></returns>
  292. public List<ScheduleExceptionMaster> BeforeScheduleCheck(List<WorkOrdMaster> workOrds,List<WorkOrdRouting> workOrdRoutings, List<ProdLineDetail> prodLines,int type)
  293. {
  294. List<ScheduleExceptionMaster> exceptions = new List<ScheduleExceptionMaster>();
  295. ScheduleExceptionMaster entity;
  296. foreach (var item in workOrds)
  297. {
  298. if (!string.IsNullOrEmpty(item.Typed))//特殊工单
  299. {
  300. //只需要校验有没有维护工艺路线即可
  301. var routings = workOrdRoutings.Where(p=>p.ItemNum == item.ItemNum).ToList();
  302. if (!routings.Any())
  303. {
  304. entity = new ScheduleExceptionMaster();
  305. entity.Domain = item.Domain;
  306. entity.WorkOrd = item.WorkOrd;
  307. entity.ItemNum = item.ItemNum;
  308. entity.CreateTime = DateTime.Now;
  309. entity.Remark = "排产异常:工单没有维护工艺路线,请维护后再操作!";
  310. entity.Type = type == 1 ? "自动排产" : "手动排产";
  311. exceptions.Add(entity);
  312. }
  313. continue;
  314. }
  315. var curRoutings = workOrdRoutings.Where(p => p.ItemNum == item.ItemNum && p.MilestoneOp).OrderBy(p=>p.OP).Select(p=>p.OP).ToList();
  316. //判断当前工单主产线是否设置了关键工序
  317. if (!curRoutings.Any())//当前工单没有维护关键工序
  318. {
  319. entity = new ScheduleExceptionMaster();
  320. entity.Domain = item.Domain;
  321. entity.WorkOrd = item.WorkOrd;
  322. entity.ItemNum = item.ItemNum;
  323. entity.CreateTime = DateTime.Now;
  324. entity.Remark = "排产异常:工单的工艺路径没有维护关键工序,请维护后再操作!";
  325. entity.Type = type == 1 ? "自动排产" : "手动排产";
  326. exceptions.Add(entity);
  327. continue;
  328. }
  329. //获取当前工单物料对应的产线信息
  330. var lineDetails = prodLines.Where(x => x.Part == item.ItemNum && curRoutings.Contains(x.Op)).ToList();
  331. if (!lineDetails.Any())
  332. {
  333. entity = new ScheduleExceptionMaster();
  334. entity.Domain = item.Domain;
  335. entity.WorkOrd = item.WorkOrd;
  336. entity.ItemNum = item.ItemNum;
  337. entity.CreateTime = DateTime.Now;
  338. entity.Remark = "排产异常:工单的关键工序没有维护产线数据,请维护后再操作!";
  339. entity.Type = type == 1 ? "自动排产" : "手动排产";
  340. exceptions.Add(entity);
  341. continue;
  342. }
  343. //校验关键工序是否维护了产线
  344. List<LineStartDto> lines = new List<LineStartDto>();
  345. foreach (var op in curRoutings)
  346. {
  347. var curLines = lineDetails.Where(p => p.Op == op).ToList();
  348. if (!curLines.Any())//当前Op没有维护产线
  349. {
  350. entity = new ScheduleExceptionMaster();
  351. entity.Domain = item.Domain;
  352. entity.WorkOrd = item.WorkOrd;
  353. entity.ItemNum = item.ItemNum;
  354. entity.CreateTime = DateTime.Now;
  355. entity.Remark = "排产异常:工单的关键工序["+ op + "]没有维护产线数据,请维护后再操作!";
  356. entity.Type = type == 1 ? "自动排产" : "手动排产";
  357. exceptions.Add(entity);
  358. }
  359. foreach (var line in curLines)
  360. {
  361. lines.Add(new LineStartDto {
  362. Line = line.Line,
  363. Op = op
  364. });
  365. }
  366. }
  367. //校验一条产线是否维护了多个关键工序
  368. List<string> pdLines = lines.Select(p => p.Line).Distinct().ToList();
  369. foreach (var line in pdLines)
  370. {
  371. var curLines = lines.Where(p => p.Line == line).ToList();
  372. if (curLines.Count() > 1)//当前物料的多个关键工序对应一条产线
  373. {
  374. entity = new ScheduleExceptionMaster();
  375. entity.Domain = item.Domain;
  376. entity.WorkOrd = item.WorkOrd;
  377. entity.ItemNum = item.ItemNum;
  378. entity.CreateTime = DateTime.Now;
  379. entity.Remark = "排产异常:工单的多个关键工序[" + string.Join("、",curLines.Select(p=>p.Op).ToList()) + "]对应同一条产线,请调整后再操作!";
  380. entity.Type = type == 1 ? "自动排产" : "手动排产";
  381. exceptions.Add(entity);
  382. }
  383. }
  384. //校验主产线关键工序对应的产线是否维护了工作日历
  385. var curCalendars = calendars.Where(x => lineDetails.Select(p => p.Line).Contains(x.ProdLine) || string.IsNullOrEmpty(x.ProdLine)).ToList();
  386. foreach (var rut in lineDetails)
  387. {
  388. //获取当前产线维护的工作日历
  389. var lineCals = curCalendars.Where(p => p.ProdLine == rut.Line).ToList();
  390. if (!lineCals.Any())//当前产线未维护工作日历
  391. {
  392. lineCals = calendars.Where(p => string.IsNullOrEmpty(p.ProdLine)).ToList();
  393. if (!lineCals.Any() || lineCals.Select(p => p.WeekDay).Distinct().Count() != 7)
  394. {
  395. entity = new ScheduleExceptionMaster();
  396. entity.Domain = item.Domain;
  397. entity.WorkOrd = item.WorkOrd;
  398. entity.ItemNum = item.ItemNum;
  399. entity.CreateTime = DateTime.Now;
  400. entity.Remark = "排产异常:产线["+rut.Line+ "]没有维护工作日历且标准工作日历未维护完全,请维护后再操作!";
  401. entity.Type = type == 1 ? "自动排产" : "手动排产";
  402. exceptions.Add(entity);
  403. }
  404. }
  405. else if (lineCals.Select(p => p.WeekDay).Distinct().Count() != 7)//当前产线维护了工作日历,但是没有维护完全
  406. {
  407. entity = new ScheduleExceptionMaster();
  408. entity.Domain = item.Domain;
  409. entity.WorkOrd = item.WorkOrd;
  410. entity.ItemNum = item.ItemNum;
  411. entity.CreateTime = DateTime.Now;
  412. entity.Remark = "排产异常:产线[" + rut.Line + "]工作日历没有维护完整,请维护后再操作!";
  413. entity.Type = type == 1 ? "自动排产" : "手动排产";
  414. exceptions.Add(entity);
  415. }
  416. }
  417. }
  418. return exceptions;
  419. }
  420. /// <summary>
  421. /// 排产
  422. /// </summary>
  423. /// <param name="workOrd">工单</param>
  424. /// <param name="workOrdRoutings">当前工单的工艺路线的关键工序(有几个关键工序就有几条产线)</param>
  425. /// <param name="prodLines">当前工单的产线明细</param>
  426. /// <param name="periodsDet">生产周期</param>
  427. /// <param name="scheduleResults">排产结果</param>
  428. /// <param name="allResults">产线占用记录</param>
  429. /// <param name="tsWorkOrds">特殊工单</param>
  430. /// <param name="ypcWorkOrds">已排产特殊工单的工单编号</param>
  431. /// <param name="tsWoRoutings">特殊工单的工艺路线</param>
  432. public void LineSchedule(WorkOrdMaster workOrd, List<WorkOrdRouting> workOrdRoutings, List<ProdLineDetail> prodLines,List<PeriodSequenceDet> periodsDet, List<ScheduleResultOpMaster> scheduleResults, List<ScheduleResultOpMaster> allResults,List<WorkOrdMaster> tsWorkOrds,List<string> ypcWorkOrds, List<WorkOrdRouting> tsWoRoutings)
  433. {
  434. //生产周期
  435. List<PeriodSequenceDet> curSequences = new List<PeriodSequenceDet>();
  436. //排产明细
  437. List<ScheduleResultOpMaster> curScheduleRsts = new List<ScheduleResultOpMaster>();
  438. //记录上一产线排产开始时间
  439. LineStartDto lineStart = new LineStartDto();
  440. TimeSpan span = TimeSpan.Zero;
  441. //第一层级工序有几个关键工序,就有几条产线
  442. for (int i = 0; i < workOrdRoutings.Count; i++)
  443. {
  444. //产线实际排产开始时间
  445. if (i == 0)//第一条产线
  446. {
  447. lineStart = DealStartTime(workOrd, workOrdRoutings[i].OP, prodLines, allResults);
  448. }
  449. else
  450. {
  451. //获取前一产线排产开始时间,通过提前期计算当前产线排产开始时间
  452. lineStart = DealNextStartTime(workOrd, lineStart, workOrdRoutings[i].OP, prodLines, allResults);
  453. }
  454. //当前产线的工作日历
  455. var mLCalendars = calendars.Where(p => p.ProdLine == lineStart.Line || string.IsNullOrEmpty(p.ProdLine)).ToList();
  456. //当前产线的休息时间设置
  457. var mlqtyWorkDtls = qualityLines.Where(p => p.ProdLine == lineStart.Line).ToList();
  458. //产线已排产数量
  459. decimal sumQty = 0m;
  460. //产线准备时间
  461. decimal sumTimes = 0m;
  462. //产线排产开始时间
  463. DateTime workStartTime = lineStart.StartTime;
  464. //记录特殊工单:获取第一天是否有特殊工单
  465. var fstWOMasters = tsWorkOrds.Where(p => p.ProdLine == lineStart.Line && p.OrdDate.Value.Date == workStartTime.Date).OrderBy(p => p.OrdDate).ToList();
  466. List<WorkOrdMstDto> workDtos = new List<WorkOrdMstDto>();
  467. foreach (var item in fstWOMasters)
  468. {
  469. int op = tsWoRoutings.Where(p => p.WorkOrd == item.WorkOrd).OrderByDescending(p => p.OP).First().OP;
  470. workDtos.Add(new WorkOrdMstDto{
  471. WorkOrd = item.WorkOrd,
  472. ItemNum = item.ItemNum,
  473. QtyOrded = item.QtyOrded,
  474. LbrVar = item.LbrVar * 60,
  475. Worked = 0,
  476. QtyWorked = 0,
  477. Op = op
  478. });
  479. }
  480. //特殊工单工作时长
  481. decimal sumTsTimes = fstWOMasters.Sum(p=>p.LbrVar);
  482. //特殊工单排产结束当天占用的产能时长
  483. decimal dayOccupyTimes = 0m;
  484. while (sumTsTimes > 0) //产线排产开始当天安排了特殊工单,则先安排特殊工单
  485. {
  486. //获取当天的产能
  487. LineScheduledDto dto = GetScheduledPoint(lineStart, workStartTime, mLCalendars, mlqtyWorkDtls);
  488. //获取当天的工作时间段
  489. var curCalendar = mLCalendars.FirstOrDefault(p => !string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == (int)workStartTime.DayOfWeek);
  490. if (curCalendar == null)
  491. {
  492. curCalendar = mLCalendars.FirstOrDefault(p => string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == (int)workStartTime.DayOfWeek);
  493. }
  494. List<LineWorkPointDto> workPoints = DealWorkDayToLevels(lineStart.Line, workStartTime, curCalendar, mlqtyWorkDtls);
  495. if (dto.EffTime >= sumTsTimes)//当天的可用产能满足特殊工单生产时长
  496. {
  497. DateTime beginTime = workStartTime;
  498. DateTime endTime = DateTime.Now;
  499. foreach (var item in workDtos)
  500. {
  501. if (item.LbrVar == item.Worked)//当前工单已排产
  502. {
  503. continue;
  504. }
  505. //工单排产完成,排产时长=工单工作时长
  506. item.Worked = item.LbrVar;
  507. //当前工单还需工作时长
  508. decimal needTime = item.LbrVar - item.Worked;
  509. //计算工单排产结束时间
  510. var curPoint = workPoints.Find(p => p.StartPoint <= beginTime && beginTime <= p.EndPoint);
  511. span = curPoint.EndPoint - beginTime;
  512. //当天工作时间段的有效生产时间(分钟)
  513. decimal effMins = (decimal)span.TotalMinutes;
  514. if (effMins >= needTime)//当前工作时间段即可满足产能
  515. {
  516. endTime = beginTime.AddMinutes((double)needTime);
  517. }
  518. else
  519. {
  520. //获取后续生产时间段
  521. var nextPoints = workPoints.Where(p => p.Level > curPoint.Level).ToList();
  522. //剩余需要工作时长
  523. decimal nextMins = needTime - effMins;
  524. foreach (var p in nextPoints)
  525. {
  526. if (p.WorkMinutes >= nextMins)
  527. {
  528. endTime = p.StartPoint.AddMinutes((double)nextMins);
  529. break;
  530. }
  531. nextMins -= p.WorkMinutes;
  532. }
  533. }
  534. //记录生产周期
  535. curSequences.Add(new PeriodSequenceDet
  536. {
  537. Domain = domain,
  538. Line = lineStart.Line,
  539. ItemNum = item.ItemNum,
  540. PlanDate = beginTime.Date,
  541. Period = dto.Period,
  542. OrdQty = item.QtyOrded - item.QtyWorked,
  543. WorkOrds = item.WorkOrd,
  544. Op = item.Op,
  545. IsActive = true,
  546. Status = "",
  547. CreateTime = DateTime.Now
  548. });
  549. //记录排产记录
  550. curScheduleRsts.Add(new ScheduleResultOpMaster
  551. {
  552. Domain = domain,
  553. WorkOrd = item.WorkOrd,
  554. Line = lineStart.Line,
  555. ItemNum = item.ItemNum,
  556. Op = item.Op,
  557. WorkDate = beginTime.Date,
  558. WorkQty = item.QtyOrded - item.QtyWorked,
  559. WorkStartTime = beginTime,
  560. WorkEndTime = endTime,
  561. CreateTime = DateTime.Now
  562. });
  563. //下一工单开始时间=当前工单结束时间,如果位于工作区间结尾,则为下一工作区间开始时间
  564. curPoint = workPoints.Find(p => p.StartPoint <= endTime && endTime <= p.EndPoint);
  565. if (endTime == curPoint.EndPoint)//结束时间位于工作区间结尾
  566. {
  567. //获取后续生产时间段
  568. var nextPoint = workPoints.Where(p => p.Level == curPoint.Level + 1).FirstOrDefault();
  569. if (nextPoint != null)//存在后续工作区间
  570. {
  571. endTime = nextPoint.StartPoint;
  572. }
  573. }
  574. beginTime = endTime;
  575. }
  576. dayOccupyTimes = sumTsTimes;
  577. if (dto.EffTime == sumTsTimes)//当天产能完全占用
  578. {
  579. //获取下一个工作日
  580. workStartTime = GetNextWorkDay((int)workStartTime.DayOfWeek, workStartTime, mLCalendars);
  581. dayOccupyTimes = 0m;
  582. }
  583. sumTsTimes = 0;//排产完毕,特殊工单时长置0
  584. }
  585. else//当天的可用产能不满足特殊工单生产时长
  586. {
  587. decimal residueTime = dto.EffTime * 60;//当天产能剩余产能(分钟)
  588. DateTime beginTime = workStartTime;//排产开始时间
  589. DateTime endTime = dto.EndTime;//排产结束时间
  590. foreach (var item in workDtos)
  591. {
  592. if (item.LbrVar == item.Worked)//当前工单已排产
  593. {
  594. continue;
  595. }
  596. //当前工单剩余待排产时长(分钟)
  597. decimal needTime = item.LbrVar - item.Worked;
  598. if (residueTime >= needTime)//当天剩余产能满足当前工单的剩余待排产时长
  599. {
  600. //计算工单排产结束时间
  601. var curPoint = workPoints.Find(p => p.StartPoint <= beginTime && beginTime <= p.EndPoint);
  602. span = curPoint.EndPoint - beginTime;
  603. //当前工作时间段的有效生产时间(分钟)
  604. decimal effMins = (decimal)span.TotalMinutes;
  605. if (effMins >= needTime)//当前工作时间段即可满足产能
  606. {
  607. endTime = beginTime.AddMinutes((double)needTime);
  608. }
  609. else
  610. {
  611. //获取后续生产时间段
  612. var nextPoints = workPoints.Where(p => p.Level > curPoint.Level).ToList();
  613. //剩余需要工作时长
  614. decimal nextMins = needTime - effMins;
  615. foreach (var p in nextPoints)
  616. {
  617. if (p.WorkMinutes >= nextMins)
  618. {
  619. endTime = p.StartPoint.AddMinutes((double)nextMins);
  620. break;
  621. }
  622. nextMins -= p.WorkMinutes;
  623. }
  624. }
  625. //记录生产周期
  626. curSequences.Add(new PeriodSequenceDet
  627. {
  628. Domain = domain,
  629. Line = lineStart.Line,
  630. ItemNum = item.ItemNum,
  631. PlanDate = beginTime.Date,
  632. Period = dto.Period,
  633. OrdQty = item.QtyOrded - item.QtyWorked,
  634. WorkOrds = item.WorkOrd,
  635. Op = item.Op,
  636. IsActive = true,
  637. Status = "",
  638. CreateTime = DateTime.Now
  639. });
  640. //记录排产记录
  641. curScheduleRsts.Add(new ScheduleResultOpMaster
  642. {
  643. Domain = domain,
  644. WorkOrd = item.WorkOrd,
  645. Line = lineStart.Line,
  646. ItemNum = item.ItemNum,
  647. Op = item.Op,
  648. WorkDate = beginTime.Date,
  649. WorkQty = item.QtyOrded - item.QtyWorked,
  650. WorkStartTime = beginTime,
  651. WorkEndTime = endTime,
  652. CreateTime = DateTime.Now
  653. });
  654. beginTime = endTime;
  655. //当前工单已排产完成,已排产时间=工单生产时长,已排产数量=工单数量
  656. item.Worked = item.LbrVar;
  657. item.QtyWorked = item.QtyWorked;
  658. //当天剩余产能
  659. residueTime -= needTime;
  660. if (residueTime == 0)
  661. {
  662. break;
  663. }
  664. }
  665. else//当天剩余产能不满足当前工单的剩余待排产时长
  666. {
  667. //计算生产数量
  668. decimal qty = Math.Ceiling(residueTime / item.LbrVar * item.QtyOrded);
  669. //记录生产周期
  670. curSequences.Add(new PeriodSequenceDet
  671. {
  672. Domain = domain,
  673. Line = lineStart.Line,
  674. ItemNum = item.ItemNum,
  675. PlanDate = beginTime.Date,
  676. Period = dto.Period,
  677. OrdQty = qty,
  678. WorkOrds = item.WorkOrd,
  679. Op = item.Op,
  680. IsActive = true,
  681. Status = "",
  682. CreateTime = DateTime.Now
  683. });
  684. //记录排产记录
  685. curScheduleRsts.Add(new ScheduleResultOpMaster
  686. {
  687. Domain = domain,
  688. WorkOrd = item.WorkOrd,
  689. Line = lineStart.Line,
  690. ItemNum = item.ItemNum,
  691. Op = item.Op,
  692. WorkDate = beginTime.Date,
  693. WorkQty = qty,
  694. WorkStartTime = beginTime,
  695. WorkEndTime = endTime,
  696. CreateTime = DateTime.Now
  697. });
  698. item.Worked += residueTime;
  699. item.QtyWorked += qty;
  700. residueTime = 0;
  701. break;
  702. }
  703. }
  704. //特殊工单剩余待排产时长
  705. sumTsTimes -= dto.EffTime;
  706. //获取下一个工作日
  707. workStartTime = GetNextWorkDay((int)workStartTime.DayOfWeek, workStartTime, mLCalendars);
  708. }
  709. }
  710. //记录排产开始第二天及以后安排的特殊工单
  711. var secWOMasters = new List<WorkOrdMaster>();
  712. //排产
  713. while (sumQty < workOrd.QtyOrded)
  714. {
  715. //获取当天的产能
  716. LineScheduledDto dto = GetScheduledPoint(lineStart, workStartTime, mLCalendars, mlqtyWorkDtls);
  717. //排产开始时,需要先减去产线准备时间
  718. if (sumTimes < lineStart.setupTime)
  719. {
  720. //判断当天的可用生产时长能满足提前期
  721. if (dto.EffTime >= lineStart.setupTime - sumTimes)
  722. {
  723. //当天剩余产能
  724. decimal sumAmount = dto.ProductQty - Math.Floor(dto.Rate * (lineStart.setupTime - sumTimes));
  725. //判断已排产数量+当天的产能是否超过工单数量
  726. if (sumQty + sumAmount <= workOrd.QtyOrded)//当天的产能需要全部排产
  727. {
  728. //记录生产周期
  729. curSequences.Add(new PeriodSequenceDet
  730. {
  731. Domain = domain,
  732. Line = lineStart.Line,
  733. ItemNum = workOrd.ItemNum,
  734. PlanDate = workStartTime.Date,
  735. Period = dto.Period,
  736. OrdQty = sumAmount,
  737. WorkOrds = workOrd.WorkOrd,
  738. Op = lineStart.Op,
  739. IsActive = true,
  740. Status = "",
  741. CreateTime = DateTime.Now
  742. });
  743. //记录排产记录
  744. curScheduleRsts.Add(new ScheduleResultOpMaster
  745. {
  746. Domain = domain,
  747. WorkOrd = workOrd.WorkOrd,
  748. Line = lineStart.Line,
  749. ItemNum = workOrd.ItemNum,
  750. Op = lineStart.Op,
  751. WorkDate = workStartTime.Date,
  752. WorkQty = sumAmount,
  753. WorkStartTime = dto.StartTime,
  754. WorkEndTime = dto.EndTime,
  755. CreateTime = DateTime.Now
  756. });
  757. //累计已排产数量
  758. sumQty += sumAmount;
  759. //继续排下一个工作日
  760. workStartTime = GetNextWorkDay((int)workStartTime.DayOfWeek, workStartTime, mLCalendars);
  761. }
  762. else// 最后一天的产能只能占用一部分
  763. {
  764. //剩余需要排产的数量
  765. decimal residueQty = workOrd.QtyOrded - sumQty;
  766. //剩余数量生产需要时长(分钟)
  767. decimal workTime = Math.Ceiling(residueQty / dto.Rate * 60);
  768. //获取当天的工作时间段
  769. var curCalendar = mLCalendars.FirstOrDefault(p => !string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == (int)workStartTime.DayOfWeek);
  770. if (curCalendar == null)
  771. {
  772. curCalendar = mLCalendars.FirstOrDefault(p => string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == (int)workStartTime.DayOfWeek);
  773. }
  774. List<LineWorkPointDto> workPoints = DealWorkDayToLevels(lineStart.Line, workStartTime, curCalendar, mlqtyWorkDtls);
  775. var curPoint = workPoints.Find(p => p.StartPoint <= workStartTime && workStartTime <= p.EndPoint);
  776. if (curPoint != null)
  777. {
  778. span = curPoint.EndPoint - workStartTime;
  779. //当天工作时间段的有效生产时间
  780. decimal effMins = (decimal)span.TotalMinutes;
  781. DateTime workEndTime = workStartTime;
  782. if (effMins >= workTime)//当前工作时间段即可满足产能
  783. {
  784. workEndTime = workStartTime.AddMinutes((double)workTime);
  785. }
  786. else
  787. {
  788. //获取后续生产时间段
  789. var nextPoints = workPoints.Where(p => p.Level > curPoint.Level).ToList();
  790. //剩余需要工作时长
  791. decimal nextMins = workTime - effMins;
  792. foreach (var p in nextPoints)
  793. {
  794. if (p.WorkMinutes >= nextMins)
  795. {
  796. workEndTime = p.StartPoint.AddMinutes((double)nextMins);
  797. break;
  798. }
  799. nextMins -= p.WorkMinutes;
  800. }
  801. }
  802. sumQty = workOrd.QtyOrded;
  803. //记录生产周期
  804. curSequences.Add(new PeriodSequenceDet
  805. {
  806. Domain = domain,
  807. Line = lineStart.Line,
  808. ItemNum = workOrd.ItemNum,
  809. PlanDate = workStartTime.Date,
  810. Period = dto.Period,
  811. OrdQty = residueQty,
  812. WorkOrds = workOrd.WorkOrd,
  813. Op = lineStart.Op,
  814. IsActive = true,
  815. Status = "",
  816. CreateTime = DateTime.Now
  817. });
  818. //记录排产记录
  819. curScheduleRsts.Add(new ScheduleResultOpMaster
  820. {
  821. Domain = domain,
  822. WorkOrd = workOrd.WorkOrd,
  823. Line = lineStart.Line,
  824. ItemNum = workOrd.ItemNum,
  825. Op = lineStart.Op,
  826. WorkDate = workStartTime.Date,
  827. WorkQty = residueQty,
  828. WorkStartTime = workStartTime,
  829. WorkEndTime = workEndTime,
  830. CreateTime = DateTime.Now
  831. });
  832. }
  833. }
  834. sumTimes = lineStart.setupTime;
  835. }
  836. else
  837. {
  838. //当天的可用生产时长不能满足提前期
  839. sumTimes += dto.EffTime;
  840. //获取下一个工作日
  841. workStartTime = GetNextWorkDay((int)workStartTime.DayOfWeek, workStartTime, mLCalendars);
  842. }
  843. }
  844. else
  845. {
  846. //判断已排产数量+当天的产能是否超过工单数量
  847. if (sumQty + dto.ProductQty <= workOrd.QtyOrded)//当天的产能需要全部排产
  848. {
  849. //记录生产周期
  850. curSequences.Add(new PeriodSequenceDet
  851. {
  852. Domain = domain,
  853. Line = lineStart.Line,
  854. ItemNum = workOrd.ItemNum,
  855. PlanDate = workStartTime.Date,
  856. Period = dto.Period,
  857. OrdQty = dto.ProductQty,
  858. WorkOrds = workOrd.WorkOrd,
  859. Op = lineStart.Op,
  860. IsActive = true,
  861. Status = "",
  862. CreateTime = DateTime.Now
  863. });
  864. //记录排产记录
  865. curScheduleRsts.Add(new ScheduleResultOpMaster
  866. {
  867. Domain = domain,
  868. WorkOrd = workOrd.WorkOrd,
  869. Line = lineStart.Line,
  870. ItemNum = workOrd.ItemNum,
  871. Op = lineStart.Op,
  872. WorkDate = workStartTime.Date,
  873. WorkQty = dto.ProductQty,
  874. WorkStartTime = dto.StartTime,
  875. WorkEndTime = dto.EndTime,
  876. CreateTime = DateTime.Now
  877. });
  878. //累计已排产数量
  879. sumQty += dto.ProductQty;
  880. //继续排下一个工作日
  881. workStartTime = GetNextWorkDay((int)workStartTime.DayOfWeek, workStartTime, mLCalendars);
  882. }
  883. else// 最后一天的产能只能占用一部分
  884. {
  885. //剩余需要排产的数量
  886. decimal residueQty = workOrd.QtyOrded - sumQty;
  887. //剩余数量生产需要时长(分钟)
  888. decimal workTime = Math.Ceiling(residueQty / dto.Rate * 60);
  889. //获取当天的工作时间段
  890. var curCalendar = mLCalendars.FirstOrDefault(p => !string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == (int)workStartTime.DayOfWeek);
  891. if (curCalendar == null)
  892. {
  893. curCalendar = mLCalendars.FirstOrDefault(p => string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == (int)workStartTime.DayOfWeek);
  894. }
  895. List<LineWorkPointDto> workPoints = DealWorkDayToLevels(lineStart.Line, workStartTime, curCalendar, mlqtyWorkDtls);
  896. var curPoint = workPoints.Find(p => p.StartPoint <= workStartTime && workStartTime <= p.EndPoint);
  897. span = curPoint.EndPoint - workStartTime;
  898. //当天工作时间段的有效生产时间
  899. decimal effMins = (decimal)span.TotalMinutes;
  900. DateTime workEndTime = workStartTime;
  901. if (effMins >= workTime)//当前工作时间段即可满足产能
  902. {
  903. workEndTime = workStartTime.AddMinutes((double)workTime);
  904. }
  905. else
  906. {
  907. //获取后续生产时间段
  908. var nextPoints = workPoints.Where(p => p.Level > curPoint.Level).ToList();
  909. //剩余需要工作时长
  910. decimal nextMins = workTime - effMins;
  911. foreach (var p in nextPoints)
  912. {
  913. if (p.WorkMinutes >= nextMins)
  914. {
  915. workEndTime = p.StartPoint.AddMinutes((double)nextMins);
  916. break;
  917. }
  918. nextMins -= p.WorkMinutes;
  919. }
  920. }
  921. sumQty = workOrd.QtyOrded;
  922. //记录生产周期
  923. curSequences.Add(new PeriodSequenceDet
  924. {
  925. Domain = domain,
  926. Line = lineStart.Line,
  927. ItemNum = workOrd.ItemNum,
  928. PlanDate = workStartTime.Date,
  929. Period = dto.Period,
  930. OrdQty = residueQty,
  931. WorkOrds = workOrd.WorkOrd,
  932. Op = lineStart.Op,
  933. IsActive = true,
  934. Status = "",
  935. CreateTime = DateTime.Now
  936. });
  937. //记录排产记录
  938. curScheduleRsts.Add(new ScheduleResultOpMaster
  939. {
  940. Domain = domain,
  941. WorkOrd = workOrd.WorkOrd,
  942. Line = lineStart.Line,
  943. ItemNum = workOrd.ItemNum,
  944. Op = lineStart.Op,
  945. WorkDate = workStartTime.Date,
  946. WorkQty = residueQty,
  947. WorkStartTime = workStartTime,
  948. WorkEndTime = workEndTime,
  949. CreateTime = DateTime.Now
  950. });
  951. }
  952. }
  953. }
  954. }
  955. //记录排产结果
  956. periodsDet.AddRange(curSequences);
  957. scheduleResults.AddRange(curScheduleRsts);
  958. }
  959. /// <summary>
  960. /// 获取产线当天的开工时间,结束时间,有效工作时长,生产数量
  961. /// </summary>
  962. /// <param name="lineStart">排产产线</param>
  963. /// <param name="startTime">产线排产开始时间</param>
  964. /// <param name="curCalendars"></param>
  965. /// <param name="curQtyDtls"></param>
  966. /// <returns></returns>
  967. public LineScheduledDto GetScheduledPoint(LineStartDto lineStart, DateTime startTime, List<ShopCalendarWorkCtr> curCalendars, List<QualityLineWorkDetail> curQtyDtls)
  968. {
  969. LineScheduledDto scheduledDto = new LineScheduledDto();
  970. //当天排产开始时间
  971. scheduledDto.StartTime = startTime;
  972. //开始时间是周几
  973. int weekDay = (int)startTime.DayOfWeek;
  974. //当天的工作日历
  975. var shopCal = curCalendars.Where(p => !string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == weekDay).FirstOrDefault();
  976. if (shopCal == null)//当前产线当天没有设置工作日历
  977. {
  978. //取默认工作日历
  979. shopCal = curCalendars.Where(p =>string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == weekDay).FirstOrDefault();
  980. }
  981. //当前日期的工作时间段
  982. List<LineWorkPointDto> workPoints = DealWorkDayToLevels(lineStart.Line, startTime, shopCal, curQtyDtls);
  983. //当天排产结束时间
  984. scheduledDto.EndTime = workPoints.Last().EndPoint;
  985. //计算starttime处于那个工作时间段
  986. var curPoint = workPoints.Where(p => startTime >= p.StartPoint && startTime <= p.EndPoint).First();
  987. TimeSpan span = curPoint.EndPoint - startTime;
  988. scheduledDto.EffTime = (decimal)span.TotalHours;
  989. //获取后续工作时间段的有效工作时间
  990. var nextPoints = workPoints.Where(p => p.Level > curPoint.Level).ToList();
  991. foreach (var item in nextPoints)
  992. {
  993. span = item.EndPoint - item.StartPoint;
  994. scheduledDto.EffTime += (decimal)span.TotalHours;
  995. }
  996. ////判断产线当天有没有加班
  997. //var curOccupyTimes = resourceOccupancyTimes.Where(p=> p.Resource == lineStart.Line && p.StartTime.GetValueOrDefault().Date == startTime.Date).ToList();
  998. //scheduledDto.EffTime += curOccupyTimes.Sum(p=> Convert.ToDecimal(p.Ufld1));
  999. //判断当前产线的UPH
  1000. var curRunCrews = prodLineDetailRunCrews.Where(p=>p.ProdLineDetailRecID == lineStart.RecID).ToList();
  1001. //判断当前日期是否配置了UPH
  1002. var curLevel = curRunCrews.FirstOrDefault(p => p.StartDate.GetValueOrDefault().Date <= startTime.Date && p.EndDate.GetValueOrDefault().Date >= startTime.Date);
  1003. decimal rate = curLevel == null ? lineStart.Rate : curLevel.Rate;
  1004. scheduledDto.Rate = rate;
  1005. //计算当天的产能(向下取整)
  1006. scheduledDto.ProductQty = Math.Floor(scheduledDto.EffTime * rate);
  1007. //计算班次
  1008. scheduledDto.Period = 1;//默认一般制
  1009. if (shopCal.ShiftsStart2 != 0 && shopCal.ShiftsHours2 != 0)
  1010. {
  1011. scheduledDto.Period = 2;
  1012. }
  1013. return scheduledDto;
  1014. }
  1015. /// <summary>
  1016. /// 计算主产线实际排产开始时间
  1017. /// </summary>
  1018. /// <param name="workOrd">工单</param>
  1019. /// <param name="op">工序</param>
  1020. /// <param name="prodLines">生产线明细</param>
  1021. /// <param name="allResults">产线占用记录</param>
  1022. /// <returns></returns>
  1023. public LineStartDto DealStartTime(WorkOrdMaster workOrd,int op,List<ProdLineDetail> prodLines, List<ScheduleResultOpMaster> allResults)
  1024. {
  1025. DateTime actStart = DateTime.Now.Date.AddDays(1);
  1026. LineStartDto lineStart = new LineStartDto();
  1027. //获取工序对应的产线,根据优先级排序
  1028. var lines = prodLines.Where(p => p.Part == workOrd.ItemNum && p.Op == op).OrderBy(p => p.Sequence).ToList();
  1029. //获取第一条产线排产结束时间
  1030. var schedule = allResults.Where(p => p.Line == lines[0].Line).OrderByDescending(p => p.WorkEndTime).FirstOrDefault();
  1031. actStart = schedule == null ? actStart : (schedule.WorkEndTime >= actStart ? schedule.WorkEndTime : actStart);
  1032. //计算实际开工时间
  1033. //产线工作日历:当前产线的工作日历+默认工作日历
  1034. var curCalendars = calendars.Where(p => p.ProdLine == lines[0].Line || string.IsNullOrEmpty(p.ProdLine)).ToList();
  1035. var curQtyDtls = qualityLines.Where(p=>p.ProdLine == lines[0].Line).ToList();
  1036. actStart = CalcActStartTime(lines[0].Line, actStart, curCalendars, curQtyDtls);
  1037. lineStart.RecID = lines[0].RecID;
  1038. lineStart.Line = lines[0].Line;
  1039. lineStart.StartTime = actStart;
  1040. lineStart.setupTime = lines[0].SetupTime;
  1041. lineStart.Rate = lines[0].Rate;
  1042. lineStart.Op = op;
  1043. //循环其他产线
  1044. for (int i = 1; i < lines.Count; i++)
  1045. {
  1046. schedule = allResults.Where(p => p.Line == lines[i].Line).OrderByDescending(p => p.WorkEndTime).FirstOrDefault();
  1047. DateTime StartTime = schedule == null ? actStart : (schedule.WorkEndTime >= actStart ? schedule.WorkEndTime : actStart);
  1048. //计算实际开工时间
  1049. //产线工作日历:当前产线的工作日历+默认工作日历
  1050. curCalendars = calendars.Where(p => p.ProdLine == lines[i].Line || string.IsNullOrEmpty(p.ProdLine)).ToList();
  1051. curQtyDtls = qualityLines.Where(p => p.ProdLine == lines[i].Line).ToList();
  1052. StartTime = CalcActStartTime(lines[0].Line, StartTime, curCalendars, curQtyDtls);
  1053. if (StartTime < lineStart.StartTime)
  1054. {
  1055. lineStart.RecID = lines[i].RecID;
  1056. lineStart.Line = lines[i].Line;
  1057. lineStart.StartTime = StartTime;
  1058. lineStart.setupTime = lines[i].SetupTime;
  1059. lineStart.Rate = lines[i].Rate;
  1060. lineStart.Op = op;
  1061. }
  1062. }
  1063. return lineStart;
  1064. }
  1065. /// <summary>
  1066. /// 计算主产线实际排产开始时间
  1067. /// </summary>
  1068. /// <param name="prodLine">生产线</param>
  1069. /// <param name="startTime">开始时间</param>
  1070. /// <param name="curCalendars">当前产线工作日历+默认工作日历</param>
  1071. /// <param name="curQtyDtls">当前产线休息记录</param>
  1072. /// <returns></returns>
  1073. public DateTime CalcActStartTime(string prodLine,DateTime startTime, List<ShopCalendarWorkCtr> curCalendars, List<QualityLineWorkDetail> curQtyDtls)
  1074. {
  1075. //实际排产开始时间
  1076. DateTime actStart = startTime;
  1077. //开始时间是周几
  1078. int weekDay = (int)startTime.DayOfWeek;
  1079. //判断当天是否是工作日
  1080. bool isWorkDay = CheckIsWorkDay(startTime);
  1081. if (!isWorkDay)//不是工作日
  1082. {
  1083. //获取下一个工作日开始时间
  1084. actStart = GetNextWorkDay(weekDay, startTime, curCalendars);
  1085. return actStart;
  1086. }
  1087. //当天的工作日历
  1088. var shopCal = curCalendars.Where(p => !string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == weekDay).FirstOrDefault();
  1089. if (shopCal == null)
  1090. {
  1091. //产线没有维护当天的工作日历,则取默认工作日历
  1092. shopCal = curCalendars.Where(p => string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == weekDay).FirstOrDefault();
  1093. }
  1094. //当前日期的工作时间段
  1095. List<LineWorkPointDto> workPoints = DealWorkDayToLevels(prodLine, startTime, shopCal, curQtyDtls);
  1096. //计算starttime处于那个工作时间段
  1097. var curPoint = workPoints.Where(p => startTime >= p.StartPoint && startTime <= p.EndPoint).FirstOrDefault();
  1098. if (curPoint == null)//不处于工作时间段
  1099. {
  1100. //开始时间小于当天工作开始时间
  1101. if (startTime < workPoints.First().StartPoint)
  1102. {
  1103. actStart = workPoints.First().StartPoint;
  1104. }
  1105. //开始时间大于当前工作结束时间
  1106. else if (startTime > workPoints.Last().EndPoint)
  1107. {
  1108. //获取下一个工作日开始时间
  1109. actStart = GetNextWorkDay(weekDay, startTime, curCalendars);
  1110. }
  1111. else
  1112. {//开始时间位于当天的休息时间段
  1113. foreach (var item in workPoints)
  1114. {
  1115. //获取下一个时间段
  1116. var next = workPoints.First(p => p.Level == item.Level + 1);
  1117. if (item.EndPoint < startTime && startTime < next.StartPoint)
  1118. {
  1119. actStart = next.StartPoint;
  1120. break;
  1121. }
  1122. }
  1123. }
  1124. return actStart;
  1125. }
  1126. if (startTime != curPoint.EndPoint)
  1127. {
  1128. return actStart;
  1129. }
  1130. //查询下一时间段的开始时间点
  1131. var nextPoint = workPoints.Where(p => p.Level == curPoint.Level + 1).FirstOrDefault();
  1132. if (nextPoint != null)
  1133. {
  1134. return nextPoint.StartPoint;
  1135. }
  1136. //开始时间为今天下班时间,实际排产开始时间为下一个工作日的开始时间
  1137. actStart = GetNextWorkDay(weekDay, startTime, curCalendars);
  1138. return actStart;
  1139. }
  1140. /// <summary>
  1141. /// 排产开始时间处理为半小时/整点开始--向后取整
  1142. /// </summary>
  1143. /// <param name="startTime"></param>
  1144. /// <returns></returns>
  1145. public DateTime CalcStartTimeAfter(DateTime startTime)
  1146. {
  1147. DateTime rtnTime = startTime;
  1148. DateTime curDate = startTime.Date;
  1149. //时间转换为分钟
  1150. TimeSpan span = rtnTime - curDate;
  1151. decimal sumMinutes = (decimal)span.TotalMinutes;
  1152. int times = (int)Math.Ceiling(sumMinutes / 30);
  1153. rtnTime = curDate.AddMinutes(times * 30);
  1154. return rtnTime;
  1155. }
  1156. /// <summary>
  1157. /// 排产开始时间处理为半小时/整点开始--向前取整
  1158. /// </summary>
  1159. /// <param name="startTime"></param>
  1160. /// <returns></returns>
  1161. public DateTime CalcStartTimeBefore(DateTime startTime)
  1162. {
  1163. DateTime rtnTime = startTime;
  1164. DateTime curDate = startTime.Date;
  1165. //时间转换为分钟
  1166. TimeSpan span = rtnTime - curDate;
  1167. decimal sumMinutes = (decimal)span.TotalMinutes;
  1168. int times = (int)Math.Floor(sumMinutes / 30);
  1169. rtnTime = curDate.AddMinutes(times * 30);
  1170. return rtnTime;
  1171. }
  1172. /// <summary>
  1173. /// 判断当天是否是工作日
  1174. /// </summary>
  1175. /// <param name="dateTime"></param>
  1176. /// <returns></returns>
  1177. public bool CheckIsWorkDay(DateTime dateTime)
  1178. {
  1179. bool isWorkDay = true;
  1180. //周几
  1181. int weekDay = (int)dateTime.DayOfWeek;
  1182. //判断当天是否是工作日
  1183. if (weekDay == 0 || weekDay == 6)//周六或者周日,需要判断是否调班,需要加班
  1184. {
  1185. if (!holidays.Exists(p => p.Dated.GetValueOrDefault().Date == dateTime.Date && p.Ufld1 == "调班"))//不是调班
  1186. {
  1187. isWorkDay = false;
  1188. }
  1189. return isWorkDay;
  1190. }
  1191. //不是周六周日,需要判断是不是节假日
  1192. if (holidays.Exists(p => p.Dated.GetValueOrDefault().Date == dateTime.Date && p.Ufld1 == "休假"))//是节假日
  1193. {
  1194. isWorkDay = false;
  1195. }
  1196. return isWorkDay;
  1197. }
  1198. /// <summary>
  1199. /// 计算下一产线实际排产开始时间
  1200. /// </summary>
  1201. /// <param name="workOrd">工单</param>
  1202. /// <param name="lineStart">上一产线开始时间</param>
  1203. /// <param name="op">工序</param>
  1204. /// <param name="prodLines">产线明细</param>
  1205. /// <param name="allResults">产线占用记录</param>
  1206. /// <returns></returns>
  1207. public LineStartDto DealNextStartTime(WorkOrdMaster workOrd,LineStartDto lineStart,int op, List<ProdLineDetail> prodLines, List<ScheduleResultOpMaster> allResults)
  1208. {
  1209. LineStartDto startDto = new LineStartDto();
  1210. //获取产线
  1211. var lines = prodLines.Where(p => p.Part == workOrd.ItemNum && p.Op == op).OrderBy(p=>p.Sequence).ToList();
  1212. //获取第一条产线排产结束时间
  1213. var schedule = allResults.Where(p => p.Line == lines[0].Line).OrderByDescending(p => p.WorkEndTime).FirstOrDefault();
  1214. DateTime startTime = CalcStartTimeWithSetUpTime(lines[0], lineStart.StartTime, lineStart.setupTime + lines[0].OverlapTime);
  1215. startDto.RecID = lines[0].RecID;
  1216. startDto.Line = lines[0].Line;
  1217. startDto.setupTime = lines[0].SetupTime;
  1218. startDto.StartTime = schedule == null ? startTime : (startTime < schedule.WorkEndTime ? schedule.WorkEndTime : startTime);
  1219. startDto.Rate = lines[0].Rate;
  1220. startDto.Op = op;
  1221. //循环剩余产线,找到最早可开工产线
  1222. for (int i = 1; i < lines.Count(); i++)
  1223. {
  1224. schedule = allResults.Where(p => p.Line == lines[i].Line).OrderByDescending(p => p.WorkEndTime).FirstOrDefault();
  1225. startTime = CalcStartTimeWithSetUpTime(lines[i], lineStart.StartTime, lineStart.setupTime + lines[i].OverlapTime);
  1226. startTime = schedule == null ? startTime : (startTime < schedule.WorkEndTime ? schedule.WorkEndTime : startTime);
  1227. if (startTime < startDto.StartTime)
  1228. {
  1229. startDto.RecID = lines[i].RecID;
  1230. startDto.Line = lines[i].Line;
  1231. startDto.setupTime = lines[i].SetupTime;
  1232. startDto.StartTime = startTime;
  1233. startDto.Rate = lines[0].Rate;
  1234. startDto.Op = op;
  1235. }
  1236. }
  1237. return startDto;
  1238. }
  1239. /// <summary>
  1240. /// 通过提前期计算产线排产开始时间
  1241. /// </summary>
  1242. /// <param name="line">产线</param>
  1243. /// <param name="startTime">上一产线开始时间</param>
  1244. /// <param name="setupTime">上一产线准备时间+当前产线提前期</param>
  1245. /// <returns></returns>
  1246. public DateTime CalcStartTimeWithSetUpTime(ProdLineDetail line, DateTime startTime, decimal setupTime)
  1247. {
  1248. //提前期转换成分钟
  1249. decimal needMinute = setupTime * 60;
  1250. //实际排产开始时间
  1251. DateTime actStart = startTime;
  1252. //开始时间是周几
  1253. int weekDay = (int)startTime.DayOfWeek;
  1254. //当天的工作日历
  1255. var shopCal = calendars.Where(p => p.ProdLine == line.Line && p.WeekDay == weekDay).FirstOrDefault();
  1256. if (shopCal == null)
  1257. {
  1258. //产线没有维护当天的工作日历,则取默认工作日历
  1259. shopCal = calendars.Where(p => string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == weekDay).FirstOrDefault();
  1260. }
  1261. //当前产线的休息时间设置
  1262. var curQtyDtls = qualityLines.Where(p => p.ProdLine == line.Line).ToList();
  1263. //当前日期的工作时间段
  1264. List<LineWorkPointDto> workPoints = DealWorkDayToLevels(line.Line, startTime, shopCal, curQtyDtls);
  1265. //计算starttime处于那个工作时间段
  1266. var curPoint = workPoints.Where(p => startTime >= p.StartPoint && startTime <= p.EndPoint).FirstOrDefault();
  1267. //当前时间段可用时长
  1268. TimeSpan span = curPoint.EndPoint - startTime;
  1269. decimal curMins = (decimal)span.TotalMinutes;
  1270. if (curMins >= needMinute)//当前时间段的可用时长满足提前期
  1271. {
  1272. actStart = startTime.AddMinutes((double)needMinute);
  1273. return actStart;
  1274. }
  1275. //当前时间段的可用时长不满足
  1276. //剩余提前期
  1277. needMinute -= curMins;
  1278. //获取后层级时间段
  1279. var prePoints = workPoints.Where(p => p.Level > curPoint.Level).OrderBy(p => p.Level).ToList();
  1280. bool flag = true;//标志位
  1281. foreach (var item in prePoints)
  1282. {
  1283. if (item.WorkMinutes >= needMinute)//当前时间段的可用时长满足
  1284. {
  1285. actStart = item.StartPoint.AddMinutes((double)needMinute);
  1286. flag = false;
  1287. break;
  1288. }
  1289. needMinute -= item.WorkMinutes;
  1290. }
  1291. if (!flag)
  1292. {
  1293. return actStart;
  1294. }
  1295. //今天可用时长不够,往后工作日找
  1296. DateTime nextStartTime = startTime;
  1297. while (flag)
  1298. {
  1299. //获取下一个工作日
  1300. nextStartTime = GetNextWorkDay(weekDay, startTime, calendars.Where(p => p.ProdLine == line.Line || string.IsNullOrEmpty(p.ProdLine)).ToList());
  1301. weekDay = (int)nextStartTime.DayOfWeek;
  1302. //获取前一个工作日的工作时间段数据,level顺排
  1303. var curCalendar = calendars.Where(p => p.ProdLine == line.Line && p.WeekDay == weekDay).FirstOrDefault();
  1304. if (curCalendar == null )
  1305. {
  1306. curCalendar = calendars.Where(p => string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == weekDay).FirstOrDefault();
  1307. }
  1308. workPoints = DealWorkDayToLevels(line.Line, nextStartTime, curCalendar, curQtyDtls).OrderBy(p => p.Level).ToList();
  1309. //当天的工作时长(分钟)
  1310. decimal sumWorkMins = workPoints.Sum(p => p.WorkMinutes);
  1311. if (sumWorkMins >= needMinute)//当天可用提前期满足
  1312. {
  1313. //获取开始时间
  1314. foreach (var item in workPoints)
  1315. {
  1316. if (item.WorkMinutes >= needMinute)//当前时间段满足
  1317. {
  1318. actStart = item.StartPoint.AddMinutes((double)needMinute);
  1319. break;
  1320. }
  1321. needMinute -= item.WorkMinutes;
  1322. }
  1323. flag = false;
  1324. }
  1325. else
  1326. {
  1327. //当天可用提前期不满足
  1328. needMinute -= sumWorkMins;
  1329. }
  1330. }
  1331. return actStart;
  1332. }
  1333. /// <summary>
  1334. /// 获取下一个工作日开始时间
  1335. /// </summary>
  1336. /// <param name="weekDay">当前周几</param>
  1337. /// <param name="startTime">开始时间</param>
  1338. /// <param name="curCalendars">当前产线的工作日历+默认工作日历</param>
  1339. /// <returns></returns>
  1340. public DateTime GetNextWorkDay(int weekDay, DateTime startTime, List<ShopCalendarWorkCtr> curCalendars)
  1341. {
  1342. DateTime rtnData = startTime;
  1343. //下一天
  1344. DateTime nextDate = startTime.Date.AddDays(1);
  1345. //下一天是周几
  1346. int nextWeekDay = (weekDay + 1) % 7;
  1347. //获取工作日历:先获取当前产线的工作日历,若没有维护,则获取默认工作日历
  1348. var calendar = curCalendars.FirstOrDefault(p =>!string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == nextWeekDay);
  1349. if (calendar == null)
  1350. {
  1351. calendar = curCalendars.FirstOrDefault(p => string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == nextWeekDay);
  1352. }
  1353. string strStart = calendar.ShiftsStart1.ToString("0.00").Replace(".", ":");
  1354. //判断下一天是否是工作日
  1355. if (nextWeekDay == 0 || nextWeekDay == 6)//下一天是周六或者周日,需要判断是否调班,需要加班
  1356. {
  1357. if (!holidays.Exists(p => p.Dated.GetValueOrDefault().Date == nextDate && p.Ufld1 == "调班"))//下一天是周末
  1358. {
  1359. //递归继续找下一个工作日
  1360. rtnData = GetNextWorkDay(nextWeekDay, nextDate, curCalendars);
  1361. return rtnData;
  1362. }
  1363. rtnData = Convert.ToDateTime(nextDate.ToString("yyyy-MM-dd") + " " + strStart);
  1364. return rtnData;
  1365. }
  1366. //下一天不是周六周日,需要判断是不是节假日
  1367. if (holidays.Exists(p => p.Dated.GetValueOrDefault().Date == nextDate && p.Ufld1 == "休假"))//是节假日
  1368. {
  1369. //递归继续找下一个工作日
  1370. rtnData = GetNextWorkDay(nextWeekDay, nextDate, curCalendars);
  1371. return rtnData;
  1372. }
  1373. rtnData = Convert.ToDateTime(nextDate.ToString("yyyy-MM-dd") + " " + strStart);
  1374. return rtnData;
  1375. }
  1376. /// <summary>
  1377. /// 处理当前日期的工作时间段
  1378. /// </summary>
  1379. /// <param name="prodLine"></param>
  1380. /// <param name="startTime"></param>
  1381. /// <param name="shopCal">当前产线的工作日历-周几</param>
  1382. /// <param name="curQtyDtls">每天休息记录</param>
  1383. /// <returns></returns>
  1384. public List<LineWorkPointDto> DealWorkDayToLevels(string prodLine, DateTime startTime, ShopCalendarWorkCtr shopCal, List<QualityLineWorkDetail> curQtyDtls)
  1385. {
  1386. //年-月-日
  1387. string date = startTime.Date.ToString("yyyy-MM-dd");
  1388. //排产记录结束日期是周几
  1389. int weekDay = (int)startTime.DayOfWeek;
  1390. //工作时间段
  1391. List<LineWorkPointDto> workPoints = new List<LineWorkPointDto>();
  1392. LineWorkPointDto dto = new LineWorkPointDto();
  1393. int level = 1;
  1394. TimeSpan span = TimeSpan.Zero;
  1395. //开始时间
  1396. string strStart = shopCal.ShiftsStart1.ToString("0.00").Replace(".", ":");
  1397. DateTime dayStartPoint = Convert.ToDateTime(date + " " + strStart);
  1398. //结束时间
  1399. DateTime dayEndPoint = dayStartPoint.AddHours((double)(shopCal.ShiftsHours1 + shopCal.ShiftsHours2));
  1400. dto.Level = level;
  1401. dto.Line = prodLine;
  1402. dto.WeekDay = weekDay;
  1403. dto.StartPoint = dayStartPoint;
  1404. //按照产线休息时间切分时间段
  1405. curQtyDtls = curQtyDtls.OrderBy(p => p.RestTimePoint).ToList();
  1406. foreach (var item in curQtyDtls)
  1407. {
  1408. DateTime endPoint = Convert.ToDateTime(date + " " + item.RestTimePoint);
  1409. dto.EndPoint = endPoint;
  1410. span = dto.EndPoint - dto.StartPoint;
  1411. dto.WorkMinutes = (decimal)span.TotalMinutes;
  1412. workPoints.Add(dto);
  1413. level++;
  1414. dto = new LineWorkPointDto();
  1415. dto.Level = level;
  1416. dto.Line = prodLine;
  1417. dto.WeekDay = weekDay;
  1418. dto.StartPoint = endPoint.AddMinutes(item.RestTime);
  1419. }
  1420. dto.EndPoint = dayEndPoint;
  1421. span = dto.EndPoint - dto.StartPoint;
  1422. dto.WorkMinutes = (decimal)span.TotalMinutes;
  1423. workPoints.Add(dto);
  1424. //产线当天加班设置
  1425. var curOccupyTimes = resourceOccupancyTimes.Where(p => p.Resource == prodLine && p.StartTime.GetValueOrDefault().Date == startTime.Date).OrderBy(p=>p.StartTime).ToList();
  1426. foreach (var item in curOccupyTimes)
  1427. {
  1428. level++;
  1429. dto = new LineWorkPointDto();
  1430. dto.Level = level;
  1431. dto.Line = prodLine;
  1432. dto.WeekDay = weekDay;
  1433. dto.StartPoint = item.StartTime.GetValueOrDefault();
  1434. dto.EndPoint = dto.StartPoint.AddHours(Convert.ToDouble(item.Ufld1));
  1435. dto.WorkMinutes = Convert.ToDecimal(item.Ufld1) * 60;
  1436. workPoints.Add(dto);
  1437. }
  1438. return workPoints.OrderBy(p => p.Level).ToList();
  1439. }
  1440. }
  1441. }