| | |
| | | using System.Threading.Tasks; |
| | | using Tiger.Model; |
| | | using Tiger.Model.Entitys.MES.Position; |
| | | using Tiger.Business.MES.WorkAction; |
| | | |
| | | namespace Tiger.Business.MES.Transaction |
| | | { |
| | |
| | | } |
| | | |
| | | #region Propertys & Variables |
| | | |
| | | /// <summary> |
| | | /// 工序当前执行的指令代码,包括:<br/> |
| | | /// {null or ""}:工序空闲,指令为空则认为工序没有执行任何指令<br/> |
| | | /// NodeSubmit:产品过站,正常产品过站逻辑<br/> |
| | | /// PkgSubmit:手动结束包装,手动保存暂存的包装数据<br/> |
| | | /// </summary> |
| | | public string NodeCmd { get; set; } |
| | | public PackingAction TempPkgAction { get; set; } |
| | | #endregion Propertys & Variables |
| | | |
| | | #region Functions |
| | |
| | | /// <returns></returns> |
| | | public async Task<ApiAction<SubmitOutput>> Submit(SubmitInput input) |
| | | { |
| | | var action = new ApiAction<SubmitOutput>(); |
| | | var action = new ApiAction<SubmitOutput>(new SubmitOutput()); |
| | | try |
| | | { |
| | | //工步列表为空或者工序节点工步有未完成时,优先完成工序节点工步 |
| | | if (Steps.IsNullOrEmpty() || !IsFinishNodeSteps) |
| | | //PkgSubmit:手动结束包装,手动保存暂存的包装数据 |
| | | if (NodeCmd == "PkgSubmit") |
| | | { |
| | | //先判断当前工单不为空且当前岗位在当前工单是不是首站,如果是则不允许变更当前工单,尝试把条码绑定到当前工单 |
| | | if (!CurBatch.IsNullOrEmpty() && CurBatch.IsFirstNode(PostCode)) |
| | | { |
| | | action = NodeSubmit(action, input); |
| | | return action; |
| | | } |
| | | //当前岗位在当前工单不是首站,则查找条码已绑定的工单当作当前工单 |
| | | else |
| | | { |
| | | var wosn = Biz.Db.Queryable<BIZ_MES_WO_BATCH, BIZ_MES_WO_SN>((b, s) => new JoinQueryInfos(JoinType.Inner, b.ORDER_NO == s.WORK_ORDER)).ByAuth(input.AuthOption) |
| | | .Where((b, s) => s.SN == input.SN && b.ACT_LINE == CurLine.LINE_CODE && (b.STATUS == BIZ_MES_WO_BATCH.STATUSs.Release.GetValue() || b.STATUS == BIZ_MES_WO_BATCH.STATUSs.Working.GetValue())) |
| | | .Select((b, s) => new { Batch = b, SN = s }).First(); |
| | | //查找到条码已绑定的工单 |
| | | if (!wosn.IsNullOrEmpty()) |
| | | { |
| | | if (CurBatch?.Batch?.ORDER_NO != wosn.Batch.ORDER_NO) |
| | | { |
| | | //条码已绑定的工单不等于当前工单则重新选择工单 |
| | | var result = await SelectOrder(new() { AuthOption = input.AuthOption, OrderNo = wosn.Batch.ORDER_NO }); |
| | | if (!result.IsSuccessed) |
| | | { |
| | | action.IsSuccessed = result.IsSuccessed; |
| | | action.LocaleMsg = result.LocaleMsg; |
| | | return action; |
| | | } |
| | | } |
| | | //条码过站 |
| | | action = NodeSubmit(action, input); |
| | | return action; |
| | | } |
| | | //查找不到条码已绑定的工单 |
| | | else |
| | | { |
| | | //没有当前工单,则先选择工单后再扫描条码 |
| | | if (CurBatch.IsNullOrEmpty()) |
| | | { |
| | | action.IsSuccessed = false; |
| | | //action.LocaleMsg = new($"未选择工单,请先选择要生产的工单"); |
| | | action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.NotSelectOrderException"); |
| | | return action; |
| | | } |
| | | //有当前工单且不是投入,则提示条码未投入生产,请先去首站扫描 |
| | | else |
| | | { |
| | | action.IsSuccessed = false; |
| | | //action.LocaleMsg = new($"条码[{input.SN}]尚未投入生产,请先去首站扫描", input.SN); |
| | | action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.NotInputException", input.SN); |
| | | return action; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | //完成工序节点工步后,后开始执行当前工序的行为工步 |
| | | else if(IsFinishNodeSteps && !IsFinishAllSteps) |
| | | { |
| | | var submitStep = Steps.Where(q => q.ID == input.CurStepID && !q.IsFinished).FirstOrDefault(); |
| | | if (submitStep.IsNullOrEmpty()) |
| | | action = TempPkgAction.Submit(input); |
| | | //如果行为被标记成已完成,则重置工序 |
| | | if (TempPkgAction.IsFinished) |
| | | { |
| | | action = BeginNextActionStep(input); |
| | | return action; |
| | | action.Data.OperInfo = CurOperInfo(input.Locale); |
| | | ResetNode(); |
| | | } |
| | | else |
| | | } |
| | | //NodeSubmit:产品过站,正常产品过站逻辑 |
| | | else |
| | | { |
| | | NodeCmd = "NodeSubmit"; |
| | | //工步列表为空或者工序节点工步有未完成时,优先完成工序节点工步 |
| | | if (Steps.IsNullOrEmpty() || !IsFinishNodeSteps) |
| | | { |
| | | var result = submitStep.Submit(input); |
| | | //如果当前工步已完成,开始执行下一工步 |
| | | if (result.IsSuccessed && submitStep.IsFinished) |
| | | //先判断当前工单不为空且当前岗位在当前工单是不是首站,如果是则不允许变更当前工单,尝试把条码绑定到当前工单 |
| | | if (!CurBatch.IsNullOrEmpty() && CurBatch.IsFirstNode(PostCode)) |
| | | { |
| | | result = BeginNextActionStep(input); |
| | | //更新CurBatch |
| | | CurBatch?.Update(); |
| | | //提交数据 |
| | | action = NodeSubmit(action, input); |
| | | //更新工序信息 |
| | | if (!action.IsSuccessed) |
| | | { |
| | | var info = WoContext.GetSnOperInfo(input.SN).Data; |
| | | info.InputQty = CurOperInfo(input.Locale).InputQty; |
| | | action.Data.OperInfo = info; |
| | | } |
| | | else if (!IsFinishAllSteps) |
| | | { |
| | | action.Data.OperInfo = SetOperNodeInfo(CurOperInfo(input.Locale)); |
| | | } |
| | | } |
| | | //如果当前工步未完成 |
| | | //当前岗位在当前工单不是首站,则查找条码已绑定的工单当作当前工单 |
| | | else |
| | | { |
| | | //行为执行出错,工步被重置 |
| | | if (Steps.IsNullOrEmpty()) |
| | | var wosns = Biz.Db.Queryable<BIZ_MES_WO_BATCH, BIZ_MES_WO_SN>((q, s) => new JoinQueryInfos(JoinType.Inner, q.ORDER_NO == s.WORK_ORDER)) |
| | | .ByAuth(input.AuthOption).Where((q, s) => s.SN == input.SN || s.FLOW_SN == input.SN || s.TRAY_SN == input.SN).Select((q, s) => new { Batch = q, SN = s }).ToList(); |
| | | |
| | | //查找到条码已绑定的工单 |
| | | if (!wosns.IsNullOrEmpty()) |
| | | { |
| | | result.LocaleMsg = new(Biz.T(result.LocaleMsg, input.Locale) + Biz.T(Biz.L("MES.Transaction.Position.RescanSN"), input.Locale)); |
| | | if (wosns.Count > 1 && wosns.Any(q => q.SN.TRAY_SN != q.SN.OUTER_SN)) |
| | | { |
| | | action.Data.ShortMsg = new("请扫描产品", ShortMessage.Types.Error); |
| | | action.Data.OperInfo = new(); |
| | | action.IsSuccessed = false; |
| | | //action.LocaleMsg = new($"条码[{0}]是载具条码,请扫描载具中的产品条码继续包装操作"); |
| | | action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.OnlyMinPackage", input.SN); |
| | | return action; |
| | | } |
| | | if (Context.ContainsKey("CurPackage") && !Context["CurPackage"].IsNullOrEmpty() && (Context["CurPackage"] as WipPkg).WorkBatch != wosns.First().Batch.BATCH_NO) |
| | | { |
| | | action.Data.ShortMsg = new("工单批次错误", ShortMessage.Types.Error); |
| | | action.Data.OperInfo = new(); |
| | | action.IsSuccessed = false; |
| | | //action.LocaleMsg = new($"正在包装工单批次[{0}]的产品,请先完成当前包装后再扫描其他批次[{1}]的产品[{2}]"); |
| | | action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.BatchError", (Context["CurPackage"] as WipPkg).WorkBatch, wosns.First().Batch.BATCH_NO, CurSN); |
| | | return action; |
| | | } |
| | | if (wosns.First().Batch.ACT_LINE != CurLine.LINE_CODE) |
| | | { |
| | | action.Data.ShortMsg = new("产线投产错误", ShortMessage.Types.Error); |
| | | action.Data.OperInfo = new(); |
| | | action.IsSuccessed = false; |
| | | //action.LocaleMsg = new($"条码[{0}]已在产线[{1}]投入生产,请在正确岗位扫描"); |
| | | action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.NotCorrectLine", input.SN, wosns.First().Batch.ACT_LINE); |
| | | } |
| | | else |
| | | { |
| | | if (CurBatch?.Batch?.ORDER_NO != wosns.First().Batch.ORDER_NO) |
| | | { |
| | | //条码已绑定的工单不等于当前工单则重新选择工单 |
| | | var result = await SelectOrder(new() { AuthOption = input.AuthOption, OrderNo = wosns.First().Batch.ORDER_NO }); |
| | | if (!result.IsSuccessed) |
| | | { |
| | | action.Data.ShortMsg = new("工单异常", ShortMessage.Types.Error); |
| | | action.Data.OperInfo = new(); |
| | | action.IsSuccessed = result.IsSuccessed; |
| | | action.LocaleMsg = result.LocaleMsg; |
| | | return action; |
| | | } |
| | | } |
| | | //更新CurBatch |
| | | CurBatch?.Update(); |
| | | //条码过站 |
| | | action = NodeSubmit(action, input); |
| | | if (!action.IsSuccessed && !CurWipSNs.Any()) |
| | | { |
| | | var info = WoContext.GetSnOperInfo(input.SN).Data; |
| | | info.InputQty = CurOperInfo(input.Locale).InputQty; |
| | | action.Data.OperInfo = info; |
| | | } |
| | | else if (!IsFinishAllSteps) |
| | | { |
| | | action.Data.OperInfo = SetOperNodeInfo(CurOperInfo(input.Locale)); |
| | | } |
| | | } |
| | | } |
| | | //查找不到条码已绑定的工单 |
| | | else |
| | | { |
| | | //没有当前工单,则先选择工单后再扫描条码 |
| | | if (CurBatch.IsNullOrEmpty()) |
| | | { |
| | | action.Data.ShortMsg = new("未选择工单", ShortMessage.Types.Error); |
| | | action.Data.OperInfo = new(); |
| | | action.IsSuccessed = false; |
| | | //action.LocaleMsg = new($"未选择工单,请先选择要生产的工单"); |
| | | action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.NotSelectOrderException"); |
| | | } |
| | | //有当前工单且不是投入,则提示条码未投入生产,请先去首站扫描 |
| | | else |
| | | { |
| | | action.Data.ShortMsg = new("未投入生产", ShortMessage.Types.Error); |
| | | action.Data.OperInfo = new(); |
| | | action.IsSuccessed = false; |
| | | //action.LocaleMsg = new($"条码[{input.SN}]尚未投入生产,请先去首站扫描", input.SN); |
| | | action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.NotInputException", input.SN); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | //完成工序节点工步后,后开始执行当前工序的行为工步 |
| | | else if (IsFinishNodeSteps && !IsFinishAllSteps) |
| | | { |
| | | //更新CurBatch |
| | | CurBatch?.Update(); |
| | | |
| | | var submitStep = Steps.Where(q => q.ID == input.CurStepID && !q.IsFinished).FirstOrDefault(); |
| | | if (submitStep.IsNullOrEmpty()) |
| | | { |
| | | action = BeginNextActionStep(input); |
| | | action.Data.OperInfo = CurOperInfo(input.Locale); |
| | | } |
| | | else |
| | | { |
| | | var result = submitStep.Submit(input); |
| | | //如果当前工步已完成,开始执行下一工步 |
| | | if (result.IsSuccessed && submitStep.IsFinished) |
| | | { |
| | | result = BeginNextActionStep(input); |
| | | } |
| | | //如果当前工步未完成 |
| | | else |
| | | { |
| | | //行为执行出错,工步被重置 |
| | | if (Steps.IsNullOrEmpty()) |
| | | { |
| | | result.LocaleMsg = new(Biz.T(result.LocaleMsg, input.Locale) + Biz.T(Biz.L("MES.Transaction.Position.RescanSN"), input.Locale)); |
| | | } |
| | | else |
| | | { |
| | | result.Data.SetValue(CurBatch, CurStep, CurStep?.ID, IsFinishAllSteps); |
| | | } |
| | | } |
| | | //如果所有工步都完成 |
| | | if (IsFinishAllSteps) |
| | | { |
| | | result = DoIfFinishAllSteps(result, input.Locale); |
| | | } |
| | | else |
| | | { |
| | | result.Data.IsFinished = IsFinishAllSteps; |
| | | result.Data.CurWO = CurBatch.WO.ORDER_NO; |
| | | result.Data.CurBatch = CurBatch.Batch.BATCH_NO; |
| | | result.Data.CurNode = CurStep.NodeAct.ACT_NAME; |
| | | result.Data.StepActCode = CurStep.NodeAct.ACT_CODE; |
| | | result.Data.NextStepID = CurStep.ID; |
| | | result.Data.OperInfo = CurOperInfo(input.Locale); |
| | | } |
| | | return result; |
| | | } |
| | | //如果所有工步都完成 |
| | | if (IsFinishAllSteps) |
| | | { |
| | | return DoIfFinishAllSteps(result); |
| | | } |
| | | return result; |
| | | } |
| | | //没有可执行的工步 |
| | | else |
| | | { |
| | | action.Data.ShortMsg = new("重置扫描", ShortMessage.Types.Error); |
| | | action.Data.OperInfo = CurOperInfo(input.Locale); |
| | | ResetNode(); |
| | | action.IsSuccessed = false; |
| | | //action.LocaleMsg = new($"岗位[{CurPosition.POST_CODE}]工步执行异常,请重新扫描产品条码", CurPosition.POST_CODE); |
| | | action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.WorkStepException", CurPosition.POST_CODE); |
| | | } |
| | | } |
| | | //没有可执行的工步 |
| | | ResetSteps(); |
| | | action.IsSuccessed = false; |
| | | //action.LocaleMsg = new($"岗位[{CurPosition.POST_CODE}]工步执行异常,请重新扫描产品条码", CurPosition.POST_CODE); |
| | | action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.WorkStepException", CurPosition.POST_CODE); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | action.CatchExceptionWithLog(ex, $"包装工序:扫描产品条码异常"); |
| | | action.Data.ShortMsg = new("工序异常", ShortMessage.Types.Error); |
| | | action.Data.OperInfo = CurOperInfo(input.Locale); |
| | | //action.CatchExceptionWithLog(ex, $"包装工序:提交操作数据异常"); |
| | | action.CatchExceptionWithLog(ex, Biz.L("MES.Transaction.PackingNode.SubmitException")); |
| | | } |
| | | return action; |
| | | } |
| | |
| | | /// <returns></returns> |
| | | public ApiAction<SubmitOutput> NodeSubmit(ApiAction<SubmitOutput> action, SubmitInput input) |
| | | { |
| | | var curNode = CurBatch.GetNode(PostCode); |
| | | try |
| | | { |
| | | //当工步列表为空,则执行当前工序的必要逻辑当作第一个工序节点,完成后按需求创建后续的工步列表 |
| | | if (Steps.IsNullOrEmpty()) |
| | | //判断工单实时状态判断是否可以生产 |
| | | var woStatus = CurBatch.CheckCanProduce(curNode); |
| | | if (!woStatus.IsSuccessed) |
| | | { |
| | | var curNode = CurBatch.GetNode(PostCode); |
| | | woStatus.Data.ShortMsg = new("工单异常", ShortMessage.Types.Error); |
| | | return woStatus; |
| | | } |
| | | |
| | | //绑定条码到工单 |
| | | if (!CurBatch.WoSNs.Any(q => q.SN == input.SN)) |
| | | { |
| | | CurBatch.WoSNs.Add(new() |
| | | { |
| | | WORK_ORDER = CurBatch.Batch.ORDER_NO, |
| | | SN = input.SN, |
| | | STATUS = BIZ_MES_WO_SN.STATUSs.NotInput.GetValue(), |
| | | }); |
| | | } |
| | | var woSN = CurBatch.WoSNs.FirstOrDefault(q => q.SN == input.SN); |
| | | woSN.AUTH_ORG = CurBatch.WO.AUTH_ORG; |
| | | woSN.AUTH_PROD = CurLine.LINE_CODE; |
| | | woSN.BATCH_NO = CurBatch.Batch.BATCH_NO; |
| | | |
| | | //条码过站 |
| | | var wipSN = Biz.Db.Queryable<MES_WIP_DATA>().First(q => q.SN == input.SN && q.BATCH_NO == CurBatch.Batch.BATCH_NO) ?? new MES_WIP_DATA() |
| | | var wipSNs = Biz.Db.Queryable<MES_WIP_DATA>().IncludesAllFirstLayer().Where(q => q.SN == input.SN || q.FLOW_SN == input.SN || q.TRAY_SN == input.SN).ToList(); |
| | | if (wipSNs.IsNullOrEmpty()) |
| | | { |
| | | var wipSN = new MES_WIP_DATA() |
| | | { |
| | | SN = input.SN, |
| | | STATUS = MES_WIP_DATA.STATUSs.Init.GetValue(), |
| | | FLOW_SN = input.SN, |
| | | STATUS = MES_WIP_DATA.STATUSs.Init.GetValue(), |
| | | ITEM_CODE = CurBatch.WO.ITEM_CODE, |
| | | WORK_ORDER = CurBatch.Batch.ORDER_NO, |
| | | BATCH_NO = CurBatch.Batch.BATCH_NO, |
| | | HOLD_FLAG = "N", |
| | | FTY_CODE = CurFactory.FTY_CODE, |
| | | WS_CODE = CurWorkshop.WS_CODE, |
| | | LINE_CODE = CurLine.LINE_CODE, |
| | | POST_CODE = CurPosition.POST_CODE, |
| | | HOLD_FLAG = "N", |
| | | REWORK_FLAG = CurBatch.WO.ORDER_TYPE == BIZ_MES_WO.ORDER_TYPEs.Rework.GetValue() ? "Y" : "N", |
| | | FINISHED_FLAG = "N", |
| | | INV_FLAG = "N", |
| | | DFT_FLAG = "N", |
| | | DFT_COUNT = 0, |
| | | }; |
| | | wipSN.AUTH_ORG = CurBatch.WO.AUTH_ORG; |
| | | wipSN.AUTH_PROD = CurLine.LINE_CODE; |
| | | wipSN.STATUS = wipSN.STATUS > 0 ? MES_WIP_DATA.STATUSs.OK.GetValue() : wipSN.STATUS; |
| | | wipSN.ROT_CODE = CurBatch.WO.ROUTE_CODE; |
| | | wipSN.NODE_ID = curNode.ID; |
| | | wipSN.NODE_NAME = curNode.NODE_NAME; |
| | | wipSN.FTY_CODE = CurFactory.FTY_CODE; |
| | | wipSN.WS_CODE = CurWorkshop.WS_CODE; |
| | | wipSN.LINE_CODE = CurLine.LINE_CODE; |
| | | wipSN.POST_CODE = CurPosition.POST_CODE; |
| | | wipSN.OPER_CODE = curNode.OPER_CODE; |
| | | wipSN.SEGMENT = curNode.SEGMENT; |
| | | wipSN.OPERATION_TIME = DateTime.Now; |
| | | var curShiftPeriod = GetShiftPeriodForNow(); |
| | | if (!curShiftPeriod.IsNullOrEmpty()) |
| | | wipSNs.Add(wipSN); |
| | | } |
| | | |
| | | //如果条码不是当前工单或者产线的则报错 |
| | | if (wipSNs.Any(q => q.WORK_ORDER != CurBatch.WO.ORDER_NO || q.LINE_CODE != CurLine.LINE_CODE)) |
| | | { |
| | | if (wipSNs.First().LINE_CODE == CurLine.LINE_CODE) |
| | | { |
| | | wipSN.SFTS_CODE = curShiftPeriod.ShiftSys.SFTS_CODE; |
| | | wipSN.SFT_CODE = curShiftPeriod.Shift.SFT_CODE; |
| | | wipSN.PRD_CODE = curShiftPeriod.Period.PRD_CODE; |
| | | action.Data.ShortMsg = new("工单错误", ShortMessage.Types.Error); |
| | | action.IsSuccessed = false; |
| | | //action.LocaleMsg = new($"产品[{CurSN}]不属于当前工单,请切换到工单[{wipSNs.First().WORK_ORDER}]后再扫描"); |
| | | action.LocaleMsg = new("MES.Transaction.PackingNode.NodeSubmit.WoError", input.SN, wipSNs.First().WORK_ORDER); |
| | | return action; |
| | | } |
| | | CurBatch.StartWorking(UserCode); |
| | | |
| | | //如果是投入站 |
| | | if (curNode.IS_INPUT == "Y") |
| | | { |
| | | woSN.STATUS = BIZ_MES_WO_SN.STATUSs.Inputed.GetValue(); |
| | | wipSN.STATUS = MES_WIP_DATA.STATUSs.Input.GetValue(); |
| | | wipSN.INLINE_DATE = DateTime.Now; |
| | | } |
| | | //如果是产出站 |
| | | if (curNode.IS_OUTPUT == "Y") |
| | | { |
| | | woSN.STATUS = BIZ_MES_WO_SN.STATUSs.Finished.GetValue(); |
| | | wipSN.STATUS = MES_WIP_DATA.STATUSs.Finished.GetValue(); |
| | | wipSN.OUTLINE_DATE = DateTime.Now; |
| | | } |
| | | //如果有提交不良 |
| | | if (!input.DFT_CODE.IsNullOrEmpty()) |
| | | { |
| | | wipSN.STATUS = MES_WIP_DATA.STATUSs.NG.GetValue(); |
| | | wipSN.DFT_FLAG = "Y"; |
| | | wipSN.DFT_COUNT++; |
| | | wipSN.DFT_CODE = input.DFT_CODE; |
| | | } |
| | | |
| | | CurWipSNHis = new MES_WIP_HIS(CurWipSN = wipSN, $"工单[{wipSN.WORK_ORDER}]的条码[{wipSN.SN}]在岗位[{wipSN.POST_CODE}]工序[{wipSN.NODE_NAME}]过站成功,状态[{wipSN.STATUS.GetEnumDesc<MES_WIP_DATA.STATUSs>()}]"); |
| | | |
| | | //初始化工步列表 |
| | | Steps.Clear(); |
| | | var curStep = new Biz.WorkStep(IWorkStep.Types.Node, this) |
| | | else |
| | | { |
| | | NodeID = curNode.ID, |
| | | Sequence = Steps.Count + 1, |
| | | action.Data.ShortMsg = new("产线错误", ShortMessage.Types.Error); |
| | | action.IsSuccessed = false; |
| | | //action.LocaleMsg = new($"产品[{CurSN}]已在产线[{wipSNs.First().LINE_CODE}]投产,请到产线[{wipSNs.First().LINE_CODE}]扫描"); |
| | | action.LocaleMsg = new("MES.Transaction.PackingNode.NodeSubmit.LineError", input.SN, wipSNs.First().LINE_CODE); |
| | | return action; |
| | | } |
| | | } |
| | | |
| | | //非法过站防呆:进入工序时要增加判断条码是否按流程过站 |
| | | var canGotoNext = CurBatch.CanGotoNext(input, wipSNs.First(), curNode); |
| | | if (!canGotoNext.IsSuccessed) |
| | | { |
| | | woStatus.Data.ShortMsg = new("进站错误", ShortMessage.Types.Error); |
| | | return canGotoNext; |
| | | } |
| | | |
| | | //当工步列表为空,则执行当前工序的必要逻辑当作第一个工序节点,完成后按需求创建后续的工步列表 |
| | | if (Steps.IsNullOrEmpty()) |
| | | { |
| | | //绑定条码到工单 |
| | | foreach (var wipSN in wipSNs) |
| | | { |
| | | if (!CurBatch.WoSNs.Any(q => q.SN == wipSN.SN)) |
| | | { |
| | | CurBatch.WoSNs.Add(new() |
| | | { |
| | | WORK_ORDER = CurBatch.Batch.ORDER_NO, |
| | | WIP_ID = wipSN.ID, |
| | | SN = wipSN.SN, |
| | | FLOW_SN = wipSN.SN, |
| | | TRAY_SN = wipSN.TRAY_SN, |
| | | STATUS = BIZ_MES_WO_SN.STATUSs.NotInput.GetValue(), |
| | | }); |
| | | } |
| | | } |
| | | |
| | | //更新工单条码明细信息 |
| | | var woSNs = CurBatch.WoSNs.Where(q => wipSNs.Any(w => q.SN == w.SN)).ToList(); |
| | | foreach (var woSN in woSNs) |
| | | { |
| | | woSN.AUTH_ORG = CurBatch.WO.AUTH_ORG; |
| | | woSN.AUTH_PROD = CurLine.LINE_CODE; |
| | | woSN.BATCH_NO = CurBatch.Batch.BATCH_NO; |
| | | } |
| | | |
| | | //条码过站 |
| | | foreach (var wipSN in wipSNs) |
| | | { |
| | | wipSN.AUTH_ORG = CurBatch.WO.AUTH_ORG; |
| | | wipSN.AUTH_PROD = CurLine.LINE_CODE; |
| | | wipSN.STATUS = MES_WIP_DATA.STATUSs.OK.GetValue();//wipSN.STATUS > 0 ? MES_WIP_DATA.STATUSs.OK.GetValue() : wipSN.STATUS; |
| | | wipSN.ROT_CODE = CurBatch.WO.ROUTE_CODE; |
| | | wipSN.NODE_ID = curNode.ID; |
| | | wipSN.NODE_NAME = curNode.NODE_NAME; |
| | | wipSN.FTY_CODE = CurFactory.FTY_CODE; |
| | | wipSN.WS_CODE = CurWorkshop.WS_CODE; |
| | | wipSN.LINE_CODE = CurLine.LINE_CODE; |
| | | wipSN.POST_CODE = CurPosition.POST_CODE; |
| | | wipSN.OPER_CODE = curNode.OPER_CODE; |
| | | wipSN.SEGMENT = curNode.SEGMENT; |
| | | wipSN.OPERATION_TIME = DateTime.Now; |
| | | var curShiftPeriod = GetShiftPeriodForNow(); |
| | | if (!curShiftPeriod.IsNullOrEmpty()) |
| | | { |
| | | wipSN.SFTS_CODE = curShiftPeriod.ShiftSys.SFTS_CODE; |
| | | wipSN.SFT_CODE = curShiftPeriod.Shift.SFT_CODE; |
| | | wipSN.PRD_CODE = curShiftPeriod.Period.PRD_CODE; |
| | | } |
| | | //如果是投入站 |
| | | if (curNode.IS_INPUT == "Y") |
| | | { |
| | | var woSN = woSNs.First(q => q.SN == wipSN.SN); |
| | | woSN.STATUS = BIZ_MES_WO_SN.STATUSs.Inputed.GetValue(); |
| | | wipSN.STATUS = MES_WIP_DATA.STATUSs.Input.GetValue(); |
| | | wipSN.INLINE_DATE = DateTime.Now; |
| | | } |
| | | //如果是产出站 |
| | | if (curNode.IS_OUTPUT == "Y") |
| | | { |
| | | var woSN = woSNs.First(q => q.SN == wipSN.SN); |
| | | woSN.STATUS = BIZ_MES_WO_SN.STATUSs.Finished.GetValue(); |
| | | wipSN.STATUS = MES_WIP_DATA.STATUSs.Finished.GetValue(); |
| | | wipSN.OUTLINE_DATE = DateTime.Now; |
| | | } |
| | | //如果有提交不良 |
| | | if (!input.DFT_CODE.IsNullOrEmpty()) |
| | | { |
| | | wipSN.STATUS = MES_WIP_DATA.STATUSs.NG.GetValue(); |
| | | wipSN.DFT_FLAG = "Y"; |
| | | wipSN.DFT_COUNT++; |
| | | wipSN.DFT_CODE = input.DFT_CODE; |
| | | var dft = CurBatch.Defects.FirstOrDefault(q => q.DFT_CODE == wipSN.DFT_CODE); |
| | | var defect = new MES_WIP_DFT() |
| | | { |
| | | AUTH_ORG = wipSN.AUTH_ORG, |
| | | AUTH_PROD = wipSN.LINE_CODE, |
| | | WIP_ID = wipSN.ID, |
| | | SN = wipSN.SN, |
| | | STATUS = MES_WIP_DFT.STATUSs.WaitHandle.GetValue(), |
| | | ITEM_CODE = wipSN.ITEM_CODE, |
| | | WORK_ORDER = wipSN.WORK_ORDER, |
| | | BATCH_NO = wipSN.BATCH_NO, |
| | | ROT_CODE = wipSN.ROT_CODE, |
| | | NODE_ID = wipSN.NODE_ID, |
| | | NODE_NAME = wipSN.NODE_NAME, |
| | | FTY_CODE = wipSN.FTY_CODE, |
| | | WS_CODE = wipSN.WS_CODE, |
| | | LINE_CODE = wipSN.LINE_CODE, |
| | | POST_CODE = wipSN.POST_CODE, |
| | | OPER_CODE = wipSN.OPER_CODE, |
| | | SEGMENT = wipSN.SEGMENT, |
| | | DFT_CODE = dft.DFT_CODE, |
| | | DFT_NAME = dft.DFT_NAME, |
| | | DFT_LEVEL = dft.DFT_LEVEL, |
| | | FLOW_SN = wipSN.FLOW_SN, |
| | | TRAY_SN = wipSN.TRAY_SN, |
| | | INNER_SN = wipSN.INNER_SN, |
| | | CARTON_SN = wipSN.CARTON_SN, |
| | | PALLET_SN = wipSN.PALLET_SN, |
| | | INV_FLAG = wipSN.INV_FLAG, |
| | | OPERATION_TIME = DateTime.Now, |
| | | SFTS_CODE = wipSN.SFTS_CODE, |
| | | SFT_CODE = wipSN.SFT_CODE, |
| | | PRD_CODE = wipSN.PRD_CODE, |
| | | OBA_BATCH = wipSN.OBA_BATCH, |
| | | LOCK_BATCH = wipSN.LOCK_BATCH, |
| | | }; |
| | | CurDefects.Add(defect); |
| | | } |
| | | } |
| | | |
| | | //工单开工 |
| | | CurBatch.StartWorking(UserCode); |
| | | |
| | | //把当前条码增加到当前条码列表 |
| | | CurWipSNs = wipSNs; |
| | | CurWipSNHiss.Clear(); |
| | | foreach (var wipSN in wipSNs) |
| | | { |
| | | var his = new MES_WIP_HIS(wipSN, $"工单[{wipSN.WORK_ORDER}]条码[{wipSN.SN}]在岗位[{wipSN.POST_CODE}]过站工序[{wipSN.NODE_NAME}]成功"); |
| | | wipSN.History.Add(his); |
| | | CurWipSNHiss.Add(his); |
| | | } |
| | | |
| | | //创建变量克隆对象用于传入DBSubmitAction中保存当前需要暂存的数据值 |
| | | var _woSns = woSNs.Clone(); |
| | | var _wipSns = wipSNs.Clone(); |
| | | var _wipHiss = CurWipSNHiss.Clone(); |
| | | var _defect = CurDefects.Clone(); |
| | | var _curNode = curNode.Clone(); |
| | | var _Batch = CurBatch.Batch.Clone(); |
| | | |
| | | //初始化工步列表 |
| | | Steps.Clear(); |
| | | var curStep = new Biz.WorkStep(IWorkStep.NodeTypes.Node, this) |
| | | { |
| | | Name = "扫描产品", |
| | | Sequence = Steps.Count + 1, |
| | | Node = curNode, |
| | | DBSubmitAction = () => |
| | | OperSetting = CurBatch.NodeSets.FirstOrDefault(q => q.NODE_ID == curNode.ID), |
| | | DBSubmitAction = () => |
| | | { |
| | | var db = GetCommitDB(); |
| | | db.Storageable(woSN, UserCode).ExecuteCommand(); |
| | | db.Storageable(wipSN, UserCode).ExecuteCommand(); |
| | | db.Storageable(CurWipSNHis, UserCode).ExecuteCommand(); |
| | | } |
| | | //使用统一的事务DB对象 |
| | | var db = GetCommitDB(); |
| | | //数据保存逻辑 |
| | | db.Storageable(_woSns, UserCode).ExecuteCommand(); |
| | | db.Storageable(_wipSns, UserCode).ExecuteCommand(); |
| | | db.Storageable(_wipHiss, UserCode).ExecuteCommand(); |
| | | //如果有不良则保存 |
| | | if (_defect.Any()) |
| | | { |
| | | db.Storageable(_defect, UserCode).ExecuteCommand(); |
| | | } |
| | | //如果是投入站 |
| | | if (_curNode.IS_INPUT == "Y") |
| | | { |
| | | db.Updateable<BIZ_MES_WO>().SetColumns(q => q.INPUT_QTY == q.INPUT_QTY + _woSns.Count).Where(q => q.ORDER_NO == _Batch.ORDER_NO).ExecuteCommand(); |
| | | db.Updateable<BIZ_MES_WO_BATCH>().SetColumns(q => q.INPUT_QTY == q.INPUT_QTY + _woSns.Count).Where(q => q.BATCH_NO == _Batch.BATCH_NO).ExecuteCommand(); |
| | | } |
| | | //如果是产出站 |
| | | if (_curNode.IS_OUTPUT == "Y") |
| | | { |
| | | db.Updateable<BIZ_MES_WO>().SetColumns(q => q.OUTPUT_QTY == q.OUTPUT_QTY + _woSns.Count).Where(q => q.ORDER_NO == _Batch.ORDER_NO).ExecuteCommand(); |
| | | db.Updateable<BIZ_MES_WO_BATCH>().SetColumns(q => q.OUTPUT_QTY == q.OUTPUT_QTY + _woSns.Count).Where(q => q.BATCH_NO == _Batch.BATCH_NO).ExecuteCommand(); |
| | | } |
| | | } |
| | | }; |
| | | Steps.Add(curStep); |
| | | |
| | | //有需要则添加工序节点的其他工步 |
| | | |
| | | //最后添加当前工序的行为工步 |
| | | GenerateSteps(curStep); |
| | | |
| | | //完成第一个工序节点工步 |
| | | curStep.IsFinished = true; |
| | | CurStep = curStep; |
| | | |
| | | //有需要用户提交信息则添加工序节点的其他工步 |
| | | |
| | | //最后添加当前工序的行为工步 |
| | | try |
| | | { |
| | | GenerateSteps(curStep); |
| | | } |
| | | catch (System.Exception ex) |
| | | { |
| | | ResetNode(); |
| | | //action.CatchExceptionWithLog(ex, $"{curNode.NODE_NAME}:工序行为工步生成异常,请检查工序行为设置"); |
| | | action.CatchExceptionWithLog(ex, Biz.L("MES.Transaction.PackingNode.NodeSubmit.GenerateStepsException", curNode.NODE_NAME)); |
| | | } |
| | | |
| | | //完成第一个工序节点工步 |
| | | curStep.Status = StepStatus.Finished; |
| | | CurStep = curStep; |
| | | } |
| | | else if (!IsFinishNodeSteps) |
| | | { |
| | | var curStep = Steps.Where(q => q.Type == IWorkStep.Types.Node && !q.IsFinished).OrderBy(q => q.Sequence).First(); |
| | | var curStep = Steps.Where(q => q.NodeType == IWorkStep.NodeTypes.Node && !q.IsFinished).OrderBy(q => q.Sequence).First(); |
| | | |
| | | //完成当前工序节点工步 |
| | | curStep.IsFinished = true; |
| | | curStep.Status = StepStatus.Finished; |
| | | CurStep = curStep; |
| | | } |
| | | |
| | | //未完成所有工步 |
| | | if (!IsFinishAllSteps) |
| | | { |
| | | var result = new ApiAction<SubmitOutput>(); |
| | | //未完成所有工序节点工步 |
| | | if (!IsFinishNodeSteps) |
| | | { |
| | | var next = Steps.Where(q => q.Type == IWorkStep.Types.Node && !q.IsFinished).OrderBy(q => q.Sequence).First(); |
| | | var next = Steps.Where(q => q.NodeType == IWorkStep.NodeTypes.Node && !q.IsFinished).OrderBy(q => q.Sequence).First(); |
| | | //设置后续可执行的工步列表 |
| | | NextSteps.Clear(); |
| | | NextSteps.Add(next); |
| | | //根据后续工步返回ApiAction |
| | | result.Data.IsFinished = IsFinishAllSteps; |
| | | result.Data.CurWO = CurBatch.WO.ORDER_NO; |
| | | result.Data.CurBatch = CurBatch.Batch.BATCH_NO; |
| | | result.Data.CurNode = CurStep.Node.NODE_NAME; |
| | | result.Data.NextStepID = next.ID; |
| | | |
| | | action.Data.SetValue(CurBatch, CurStep, next.ID, IsFinishAllSteps); |
| | | |
| | | //根据工序节点工步的序号返回相应的操作提示 |
| | | switch (next.Sequence) |
| | | { |
| | | case 2: |
| | | //action.LocaleMsg = new($"请执行第二步"); |
| | | result.LocaleMsg = new("MES.Transaction.PackingNode.第二步操作提示"); |
| | | action.LocaleMsg = new("MES.Transaction.CollectNode.第二步操作提示"); |
| | | break; |
| | | default: |
| | | break; |
| | |
| | | //已完成所有工序节点工步,开始执行行为工步 |
| | | else |
| | | { |
| | | result = BeginNextActionStep(input); |
| | | action = BeginNextActionStep(input); |
| | | } |
| | | return result; |
| | | } |
| | | //已完成所有工步 |
| | | else |
| | | if (IsFinishAllSteps) |
| | | { |
| | | action.Data.IsFinished = IsFinishAllSteps; |
| | | action.Data.CurWO = CurBatch.WO.ORDER_NO; |
| | | action.Data.CurBatch = CurBatch.Batch.BATCH_NO; |
| | | action.Data.CurNode = CurStep.Node.NODE_NAME; |
| | | action.Data.NextStepID = ""; |
| | | action = DoIfFinishAllSteps(action); |
| | | action.Data.SetValue(CurBatch, CurStep, "", IsFinishAllSteps); |
| | | action = DoIfFinishAllSteps(action, input.Locale); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | ResetSteps(); |
| | | action.CatchExceptionWithLog(ex, $"包装工序:扫描产品条码异常"); |
| | | action.Data.ShortMsg = new("工步异常", ShortMessage.Types.Error); |
| | | ResetNode(); |
| | | //action.CatchExceptionWithLog(ex, $"{curNode.NODE_NAME}:工序节点工步提交数据异常,请检查工序节点设置"); |
| | | action.CatchExceptionWithLog(ex, Biz.L("MES.Transaction.PackingNode.NodeSubmitException", curNode.NODE_NAME)); |
| | | } |
| | | return action; |
| | | } |
| | |
| | | /// </summary> |
| | | /// <param name="action"></param> |
| | | /// <returns></returns> |
| | | public ApiAction<SubmitOutput> DoIfFinishAllSteps(ApiAction<SubmitOutput> action) |
| | | public ApiAction<SubmitOutput> DoIfFinishAllSteps(ApiAction<SubmitOutput> action, string locale) |
| | | { |
| | | //保存数据库 |
| | | var dbTran = GetCommitDB().UseTran(() => |
| | | { |
| | | //在同一个事务中保存所有工步的数据 |
| | | foreach (var step in Steps.OrderBy(q => q.Sequence)) |
| | | { |
| | | step.DBSubmitAction.Invoke(); |
| | | } |
| | | }); |
| | | if (!dbTran.IsSuccess) |
| | | { |
| | | throw dbTran.ErrorException; |
| | | } |
| | | SaveStepsCommitActionToDB(); |
| | | |
| | | //保存成功,返回过站消息 |
| | | //action.LocaleMsg = new($"工单[{CurWipSN.WORK_ORDER}]的条码[{CurWipSN.SN}]在岗位[{CurWipSN.POST_CODE}]工序[{CurWipSN.NODE_NAME}]过站成功,状态[{CurWipSN.STATUS.GetEnumDesc<MES_WIP_DATA.STATUSs>()}]", CurWipSN.WORK_ORDER, CurWipSN.SN, CurWipSN.POST_CODE, CurWipSN.NODE_NAME, CurWipSN.STATUS.GetEnumDesc<MES_WIP_DATA.STATUSs>()); |
| | | action.LocaleMsg = new("MES.Transaction.PackingNode.ScanSn.PassSuccess", CurWipSN.WORK_ORDER, CurWipSN.SN, CurWipSN.POST_CODE, CurWipSN.NODE_NAME, CurWipSN.STATUS.GetEnumDesc<MES_WIP_DATA.STATUSs>()); |
| | | |
| | | CurOperInfo(locale).InputQty += CurWipSNs.Count; |
| | | action.Data.OperInfo = SetOperNodeInfo(CurOperInfo(locale)); |
| | | action.Data.ShortMsg = new("过站成功", ShortMessage.Types.Success); |
| | | //action.LocaleMsg = new($"工单[{CurWipSN.WORK_ORDER}]的条码[{CurWipSN.SN}]在岗位[{CurWipSN.POST_CODE}]工序[{CurWipSN.NODE_NAME}]过站成功,状态[{CurWipSN.STATUS.GetEnumDesc<MES_WIP_DATA.STATUSs>()}]"); |
| | | action.LocaleMsg = new("MES.Transaction.CollectNode.ScanSn.PassSuccess", CurWipSNs.First().WORK_ORDER, CurSN, CurWipSNs.First().POST_CODE, CurWipSNs.First().NODE_NAME,CurWipSNs.First().STATUS.GetEnumDesc<MES_WIP_DATA.STATUSs>()); |
| | | |
| | | //重置工序 |
| | | ResetNode(); |
| | | |
| | | return action; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 包装工序:手动结束包装 |
| | | /// </summary> |
| | | /// <returns></returns> |
| | | public async Task<ApiAction<SubmitOutput>> CompletePkg(ApiAction input) |
| | | { |
| | | var action = new ApiAction<SubmitOutput>(new SubmitOutput()); |
| | | try |
| | | { |
| | | //PkgSubmit:手动结束包装,保存暂存的包装数据 |
| | | if (NodeCmd.IsNullOrEmpty("PkgSubmit") == "PkgSubmit") |
| | | { |
| | | if (Context.ContainsKey("CurPackage")) |
| | | { |
| | | var CurPkg = Context["CurPackage"] as WipPkg; |
| | | int shipQty = input.Data.ToInt32(); |
| | | |
| | | NodeCmd = "PkgSubmit"; |
| | | |
| | | TempPkgAction = new PackingAction(); |
| | | TempPkgAction.Init(this); |
| | | |
| | | int curQty = CurPkg.Item.TotalQty; |
| | | int ruleQty = TempPkgAction.PkgRule.GetTotalQty(); |
| | | //有填出货数量则验证当前包装数量必须等于(出货数量/包装规则数量)的余数 |
| | | if (shipQty > 0 && curQty != shipQty % ruleQty) |
| | | { |
| | | action.IsSuccessed = false; |
| | | //action.LocaleMsg = new($"出货数量设定为[{0}],目前包装了[{1}],包装尾数的数量必需是[{2}],请装够数量后再结束包装"); |
| | | action.LocaleMsg = new("MES.Transaction.PackingNode.NotMatchShipQty", shipQty, curQty, shipQty % ruleQty); |
| | | } |
| | | |
| | | action = TempPkgAction.Submit(null); |
| | | } |
| | | else |
| | | { |
| | | action.IsSuccessed = false; |
| | | //action.LocaleMsg = new($"找不到暂存的包装数据,无需手动结束包装"); |
| | | action.LocaleMsg = new("MES.Transaction.PackingNode.NoCurPackage"); |
| | | } |
| | | } |
| | | //工序处于其他命令模式,不允许手动结束包装 |
| | | else |
| | | { |
| | | switch (NodeCmd) |
| | | { |
| | | case "NodeSubmit": |
| | | action.IsSuccessed = false; |
| | | //action.LocaleMsg = new($"正在进行条码[{CurWipSN.SN}]的过站业务,不允许手动结束包装"); |
| | | action.LocaleMsg = new("MES.Transaction.PackingNode.WorkingOnNodeSubmit", CurSN); |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | action.CatchExceptionWithLog(ex, $"包装工序:手动结束包装异常"); |
| | | } |
| | | return action; |
| | | } |
| | | |
| | | #endregion Functions |
| | | |
| | | /// <summary> |
| | | /// 重置当前工步,有需要则重写此方法 |
| | | /// </summary> |
| | | public override void ResetNode() |
| | | { |
| | | base.ResetNode(); |
| | | NodeCmd = null; |
| | | } |
| | | |
| | | public override bool Close(bool needSaveHistoryLog = false) |
| | | { |
| | | //needSaveHistoryLog = true; |