using Rhea.Common; using Tiger.IBusiness; using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading; using System.Threading.Tasks; using Tiger.Model; using Tiger.Model.Entitys.MES.Position; using Tiger.Business.MES.WorkAction; using Tiger.Model.MES.Yada; using System.Data; using System.Diagnostics; namespace Tiger.Business.MES.Transaction { /// /// 包装工序事务 /// public class YadaPacking : Position, IYadaPacking { public new IYadaPacking Init(string id, string apiHost, string userCode, string postCode) { base.Init(id, apiHost, userCode, postCode); U9CDB = Biz.DataSource["YadaU9C"].Client; Logger.Console.Info($"User[{userCode}] start a {this.GetType().Name}[{postCode}] Transaction[ID: {TransID}]"); return this; } #region Propertys & Variables /// /// 工序当前执行的指令代码,包括:
/// {null or ""}:工序空闲,指令为空则认为工序没有执行任何指令
/// NodeSubmit:产品过站,正常产品过站逻辑
/// PkgSubmit:手动结束包装,手动保存暂存的包装数据
///
public string NodeCmd { get; set; } public PackingAction TempPkgAction { get; set; } public bool IsPrintCustomerLabel { get; set; } public string ShippingOrder { get; set; } public DbClient U9CDB { get; set; } public Model.TraceDebug debug { get; set; } public DateTime curtime = DateTime.Now; public DateTime prevtime = DateTime.Now; #endregion Propertys & Variables #region Functions /// /// 包装工序:提交操作数据 /// /// /// public async Task> Submit(SubmitInput input) { debug = new(new StackFrame(true)); debug.Log(new StackFrame(true), "***** 1", info => { ConsoleExt.WriteLine(info.ToString()); }); var action = new ApiAction(new SubmitOutput()); try { //if (input.Qty.ToInt32() <= 0) //{ // action.Data.ShortMsg = new("请输入包装数量", ShortMessage.Types.Error); // action.Data.OperInfo = new(); // action.Data.Data = "QtyError"; // action.IsSuccessed = false; // action.LocaleMsg = new($"包装数量[{input.Qty}]不正确,请重新输入数量后继续包装操作"); // //action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.QtyError", input.Qty); // return SetOutPutMqttMsg(action, input.Locale); //} //if (input.Options.ContainsKey("IsPrintCustomerLabel")) //{ // IsPrintCustomerLabel = input.Options["IsPrintCustomerLabel"].ToBoolean(); //} //if (input.Options.ContainsKey("ShippingOrder")) //{ // ShippingOrder = IsPrintCustomerLabel ? input.Options["ShippingOrder"].ToString() : ""; //} //PkgSubmit:手动结束包装,手动保存暂存的包装数据 if (NodeCmd == "PkgSubmit") { action = TempPkgAction.Submit(input); //如果行为被标记成已完成,则重置工序 if (TempPkgAction.IsFinished) { action.Data.OperInfo = CurOperInfo(input.Locale); ResetNode(); } } //NodeSubmit:产品过站,正常产品过站逻辑 else { //工步列表为空或者工序节点工步有未完成时,优先完成工序节点工步 if (Steps.IsNullOrEmpty() || !IsFinishNodeSteps) { var wosns = MainDB.Queryable((q, s) => new JoinQueryInfos(JoinType.Inner, q.ORDER_NO == s.WORK_ORDER && q.BATCH_NO == s.BATCH_NO)) .ByAuth(input.AuthOption)//.Where((q, s) => s.STATUS < BIZ_MES_WO_SN.STATUSs.Finished.GetValue()) .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(); debug.Log(new StackFrame(true), "***** 2", info => { ConsoleExt.WriteLine(info.ToString()); }); //先判断当前工单不为空且当前岗位在当前工单是不是首站,如果是则不允许变更当前工单,尝试把条码绑定到当前工单 if (!CurBatch.IsNullOrEmpty() && CurBatch.IsFirstNode(PostCode)) { //更新CurBatch CurBatch?.Update(); debug.Log(new StackFrame(true), "***** 3", info => { ConsoleExt.WriteLine(info.ToString()); }); var wosn = wosns.FirstOrDefault(q => q.Batch.ORDER_NO == CurBatch.WO.ORDER_NO && (q.SN.STATUS == BIZ_MES_WO_SN.STATUSs.Finished.GetValue() || q.SN.STATUS == BIZ_MES_WO_SN.STATUSs.Scrap.GetValue())); if (!wosn.IsNullOrEmpty()) { action.Data.ShortMsg = new($"产品{wosn.SN.STATUS.GetEnumDesc()}", ShortMessage.Types.Error); action.Data.OperInfo = new(); action.IsSuccessed = false; //action.LocaleMsg = new($"进站扫描错误,条码[{0}]{1}", input.SN); action.LocaleMsg = new("MES.Transaction.CollectNode.Submit.SnStatusError", input.SN, wosn.SN.STATUS.GetEnumDesc()); } else { //提交数据 action = NodeSubmit(action, input); debug.Log(new StackFrame(true), "***** 4", info => { ConsoleExt.WriteLine(info.ToString()); }); //更新工序信息 if (!action.IsSuccessed) { var info = WoContext.GetSnOperInfo(input.SN).Data; info.InputQty = CurOperInfo(input.Locale).InputQty; action.Data.OperInfo = info; } else if (Steps.Any() && !IsFinishAllSteps) { action.Data.OperInfo = SetOperNodeInfo(CurOperInfo(input.Locale)); } } } //当前岗位在当前工单不是首站,则查找条码已绑定的工单当作当前工单 else { //查找到条码已绑定的工单 if (wosns.Any(q => q.SN.STATUS < BIZ_MES_WO_SN.STATUSs.Finished.GetValue())) { var curSNs = wosns.Where(q => q.SN.STATUS < BIZ_MES_WO_SN.STATUSs.Finished.GetValue()); if (curSNs.Count() > 1 && curSNs.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 SetOutPutMqttMsg(action, input.Locale); } var curPkg = Context.ContainsKey("CurPackage") ? Context["CurPackage"] as WipPkg : null; if (!curPkg.IsNullOrEmpty() && !curPkg.IsFinished && curPkg.WorkBatch != curSNs.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", curPkg.WorkBatch, curSNs.First().Batch.BATCH_NO, input.SN); return SetOutPutMqttMsg(action, input.Locale); } if (curSNs.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, curSNs.First().Batch.ACT_LINE); } else { if (CurBatch?.Batch?.ORDER_NO != curSNs.First().Batch.ORDER_NO) { //条码已绑定的工单不等于当前工单则重新选择工单 var result = await SelectOrder(new() { AuthOption = input.AuthOption, OrderNo = curSNs.First().Batch.ORDER_NO }, curSNs.First().Batch.BATCH_NO); if (!result.IsSuccessed) { action.Data.ShortMsg = new("工单异常", ShortMessage.Types.Error); action.Data.OperInfo = new(); action.IsSuccessed = result.IsSuccessed; action.LocaleMsg = result.LocaleMsg; return SetOutPutMqttMsg(action, input.Locale); } } //更新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 (Steps.Any() && !IsFinishAllSteps) { action.Data.OperInfo = SetOperNodeInfo(CurOperInfo(input.Locale)); } } } //查找不到条码已绑定的工单 else { var lastSn = wosns.OrderByDescending(q => q.SN.UPDATE_TIME).FirstOrDefault(); if (!lastSn.IsNullOrEmpty()) { action.Data.ShortMsg = new($"产品{lastSn.SN.STATUS.GetEnumDesc()}", ShortMessage.Types.Error); action.Data.OperInfo = new(); action.IsSuccessed = false; //action.LocaleMsg = new($"进站扫描错误,条码[{0}]{1}", input.SN); action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.SnStatusError", input.SN, lastSn.SN.STATUS.GetEnumDesc()); } 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(); debug.Log(new StackFrame(true), "***** 5", info => { ConsoleExt.WriteLine(info.ToString()); }); var submitStep = Steps.Where(q => q.ID == input.CurStepID && !q.IsFinished).FirstOrDefault(); //如果客户端返回的当前工步ID找不到未完成的行为工步,则直接开始下一个行为工步 if (submitStep.IsNullOrEmpty()) { action = BeginNextActionStep(input); debug.Log(new StackFrame(true), "***** 6", info => { ConsoleExt.WriteLine(info.ToString()); }); } else { action = submitStep.Submit(input); debug.Log(new StackFrame(true), "***** 7", info => { ConsoleExt.WriteLine(info.ToString()); }); //如果当前工步已完成,开始执行下一工步 if (action.IsSuccessed && submitStep.IsFinished) { action = BeginNextActionStep(input); debug.Log(new StackFrame(true), "***** 8", info => { ConsoleExt.WriteLine(info.ToString()); }); } //如果当前工步未完成 else { //行为执行出错,工步被重置 if (Steps.IsNullOrEmpty()) { action.LocaleMsg = new(Biz.T(action.LocaleMsg, input.Locale) + Biz.T(Biz.L("MES.Transaction.Position.RescanSN"), input.Locale)); } else { action.Data.SetValue(CurBatch, CurStep, CurStep?.ID, IsFinishAllSteps); } } } //如果所有工步都完成 if (IsFinishAllSteps) { action = DoIfFinishAllSteps(action, input.Locale); } else { action.Data.OperInfo = CurOperInfo(input.Locale); } } //没有可执行的工步 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); } } } catch (Exception ex) { action.Data.ShortMsg = new("工序异常", ShortMessage.Types.Error); action.Data.OperInfo = CurOperInfo(input.Locale); RemoveCurSnFromCurPackage(Context.ContainsKey("CurPackage") ? (Context["CurPackage"] as WipPkg).Item : null); ResetNode(); //action.CatchExceptionWithLog(ex, $"包装工序:提交操作数据异常"); action.CatchExceptionWithLog(ex, Biz.L("MES.Transaction.PackingNode.SubmitException")); } debug.Log(new StackFrame(true), "***** 9", info => { ConsoleExt.WriteLine(info.ToString()); }); return SetOutPutMqttMsg(action, input.Locale); } /// /// 工序节点工步提交数据 /// /// /// /// public ApiAction NodeSubmit(ApiAction action, SubmitInput input) { debug.Log(new StackFrame(true), "***** 10", info => { ConsoleExt.WriteLine(info.ToString()); }); var curNode = CurBatch.GetNode(PostCode); try { //工序节点逻辑增加如果根基当前岗位找不到对应工序的时候要报错 if (curNode.IsNullOrEmpty()) { action.Data.ShortMsg = new("未知工序", ShortMessage.Types.Error); action.IsSuccessed = false; //action.LocaleMsg = new($"岗位[{0}]在工单[{1}]工艺路线中找不到对应的工序,请检查工单工艺路线中工序与岗位的绑定关系是否正确"); action.LocaleMsg = new("MES.Transaction.PackingNode.NodeSubmit.OperateNodeError", PostCode, CurBatch.WO.ORDER_NO); return action; } var curNodeSetting = CurBatch.GetNodeSetting(curNode); //当工步列表为空,则执行当前工序的必要逻辑当作第一个工序节点,完成后按需求创建后续的工步列表 if (Steps.IsNullOrEmpty()) { BeginAt = DateTime.Now; //判断工单实时状态判断是否可以生产 var woStatus = CurBatch.CheckCanProduce(curNode, curNodeSetting); if (!woStatus.IsSuccessed) { woStatus.Data.ShortMsg = new("工单异常", ShortMessage.Types.Error); return woStatus; } var wipSNs = MainDB.Queryable().IncludesAllFirstLayer().Where(q => q.SN == input.SN || q.FLOW_SN == input.SN || q.TRAY_SN == input.SN).Where(q => q.FINISHED_FLAG != "Y").ToList(); if (wipSNs.IsNullOrEmpty()) { var wipSN = new MES_WIP_DATA() { SN = input.SN, 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, 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", UNBIND_FLAG = "N", INV_FLAG = "N", DFT_FLAG = "N", DFT_COUNT = 0, }; 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) { 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; } else { 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; } //设置当前指令为正常产品过站 NodeCmd = "NodeSubmit"; debug.Log(new StackFrame(true), "***** 11", info => { ConsoleExt.WriteLine(info.ToString()); }); //获取出货状态 var ship = U9CDB.Queryable().Where(q => q.MoDoc == CurBatch.WO.ORDER_NO && q.Status == 2 && SqlFunc.DateIsSame(q.BusinessDate, DateTime.Now)).First(); decimal CurShipQty = 0; int WaitShipmentCarton = 0; int WaitShipment = 0; if (ship.IsNullOrEmpty()) { IsPrintCustomerLabel = false; } else { CurShipQty = ship.ShipQty; IsPrintCustomerLabel = true; SugarParameter[] pars = MainDB.Ado.GetParameters(new { BATCH = CurBatch.Batch.BATCH_NO, NodeIdOrName = curNode.NODE_NAME }); var list = MainDB.Ado.UseStoredProcedure().GetDataTable("SP_MES_GET_NODE_BATCH_COUNT", pars).AsEnumerable().ToList(); if (!list.IsNullOrEmpty()) { var batchCount = list.Where(q => q["ProdDate"].ToString() == "Whole").FirstOrDefault(); WaitShipmentCarton = batchCount["WaitShipmentCarton"].ToInt32(); WaitShipment = batchCount["WaitShipment"].ToInt32(); IsPrintCustomerLabel = batchCount.IsNullOrEmpty() ? IsPrintCustomerLabel : batchCount["WaitShipment"].ToInt32() >= ship.ShipQty ? false : true; } } ShippingOrder = IsPrintCustomerLabel ? ship?.ShipDoc ?? "" : ""; //缓存出货数量 Context.SetOrAdd("CurShipQty", CurShipQty); //缓存出货箱数 Context.SetOrAdd("CurWaitShipmentCarton", WaitShipmentCarton); Context.SetOrAdd("CurWaitShipment", WaitShipment); debug.Log(new StackFrame(true), "***** 12", info => { ConsoleExt.WriteLine(info.ToString()); }); //绑定条码到工单 foreach (var wipSN in wipSNs) { if (!CurBatch.WoSNs.Any(q => q.WIP_ID == wipSN.ID)) { 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.WIP_ID == w.ID)).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; wipSN.SHIPPING_ORDER = ShippingOrder; wipSN.UNBIND_FLAG = "N"; 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 (curNodeSetting.IS_INPUT == "Y") { var woSN = woSNs.First(q => q.WIP_ID == wipSN.ID); woSN.STATUS = BIZ_MES_WO_SN.STATUSs.Inputed.GetValue(); wipSN.STATUS = MES_WIP_DATA.STATUSs.Input.GetValue(); wipSN.INLINE_DATE = DateTime.Now; } //如果是产出站 if (curNodeSetting.IS_OUTPUT == "Y") { var woSN = woSNs.First(q => q.WIP_ID == wipSN.ID); woSN.STATUS = BIZ_MES_WO_SN.STATUSs.Finished.GetValue(); wipSN.STATUS = MES_WIP_DATA.STATUSs.Output.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, }; wipSN.Defects.Add(defect); CurDefects.Add(defect); } } //产品被判断不良后下线,并从工单中移除正在生产的条码 if (CurDefects.Any() && curNodeSetting.IF_DFT_OFFLINE == "Y") { woStatus.Data.ShortMsg = new("产品下线", ShortMessage.Types.Error); return OfflineIfDefect(action, wipSNs, input.Locale); } //工单开工 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}]成功(TransId:{TransID})"); 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 _curNodeSetting = curNodeSetting.Clone(); var _Batch = CurBatch.Batch.Clone(); debug.Log(new StackFrame(true), "***** 13", info => { ConsoleExt.WriteLine(info.ToString()); }); //初始化工步列表 Steps.Clear(); var curStep = new WorkStep(IWorkStep.NodeTypes.Node, this) { Name = "扫描产品", Sequence = Steps.Count + 1, Node = curNode, OperSetting = CurBatch.NodeSets.FirstOrDefault(q => q.NODE_ID == curNode.ID), DBSubmitAction = () => { //使用统一的事务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 (_curNodeSetting.IS_INPUT == "Y") { db.Updateable().SetColumns(q => q.INPUT_QTY == q.INPUT_QTY + _woSns.Count).Where(q => q.ORDER_NO == _Batch.ORDER_NO).ExecuteCommand(); db.Updateable().SetColumns(q => q.INPUT_QTY == q.INPUT_QTY + _woSns.Count).Where(q => q.BATCH_NO == _Batch.BATCH_NO).ExecuteCommand(); } //如果是产出站 if (_curNodeSetting.IS_OUTPUT == "Y") { db.Updateable().SetColumns(q => q.OUTPUT_QTY == q.OUTPUT_QTY + _woSns.Count).Where(q => q.ORDER_NO == _Batch.ORDER_NO).ExecuteCommand(); db.Updateable().SetColumns(q => q.OUTPUT_QTY == q.OUTPUT_QTY + _woSns.Count).Where(q => q.BATCH_NO == _Batch.BATCH_NO).ExecuteCommand(); } } }; Steps.Add(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.IsFinished = true; curStep.Status = StepStatus.Finished; CurStep = curStep; } else if (!IsFinishNodeSteps) { var curStep = Steps.Where(q => q.NodeType == IWorkStep.NodeTypes.Node && !q.IsFinished).OrderBy(q => q.Sequence).First(); //根据工序节点工步的序号处理相应工步的处理逻辑 switch (curStep.Sequence) { case 2: { //第二工步的处理逻辑 //完成当前工序节点工步 curStep.IsFinished = true; curStep.Status = StepStatus.Finished; CurStep = curStep; } break; default: break; } } //未完成所有工步 if (!IsFinishAllSteps) { //未完成所有工序节点工步 if (!IsFinishNodeSteps) { var next = Steps.Where(q => q.NodeType == IWorkStep.NodeTypes.Node && !q.IsFinished).OrderBy(q => q.Sequence).First(); //设置后续可执行的工步列表 NextSteps.Clear(); NextSteps.Add(next); //根据后续工步返回ApiAction action.Data.SetValue(CurBatch, CurStep, next.ID, IsFinishAllSteps); //根据工序节点工步的序号返回相应的操作提示 switch (next.Sequence) { case 2: //action.LocaleMsg = new($"请执行第二步"); action.LocaleMsg = new("MES.Transaction.PackingNode.第二步操作提示"); break; default: break; } } //已完成所有工序节点工步,开始执行行为工步 else { action = BeginNextActionStep(input); } } //已完成所有工步 if (IsFinishAllSteps) { action.Data.SetValue(CurBatch, CurStep, "", IsFinishAllSteps); action = DoIfFinishAllSteps(action, input.Locale); } } catch (Exception ex) { action.Data.ShortMsg = new("工步异常", ShortMessage.Types.Error); RemoveCurSnFromCurPackage(Context.ContainsKey("CurPackage") ? (Context["CurPackage"] as WipPkg).Item : null); ResetNode(); //action.CatchExceptionWithLog(ex, $"{curNode.NODE_NAME}:工序节点工步提交数据异常,请检查工序节点设置"); action.CatchExceptionWithLog(ex, Biz.L("MES.Transaction.PackingNode.NodeSubmitException", curNode.NODE_NAME)); } debug.Log(new StackFrame(true), "***** 13.1", info => { ConsoleExt.WriteLine(info.ToString()); }); return action; } /// /// 完成所有工步后执行 /// /// /// public ApiAction DoIfFinishAllSteps(ApiAction action, string locale) { var curPkg = Context.ContainsKey("CurPackage") ? Context["CurPackage"] as WipPkg : null; var operInfo = SetOperNodeInfo(CurOperInfo(locale)); Action endAction = null; debug.Log(new StackFrame(true), "***** 14", info => { ConsoleExt.WriteLine(info.ToString()); }); //如果当前条码已经走到流程终点则记录条码完工 if (operInfo.IsReachedEndNode) { //标记当前包装信息为终点站 if (!curPkg.IsNullOrEmpty()) { curPkg.IsReachedEndNode = true; } //更新工单条码明细信息 var woSNs = CurBatch.WoSNs.Where(q => CurWipSNs.Any(w => q.WIP_ID == w.ID)).ToList(); foreach (var woSN in woSNs) { woSN.STATUS = BIZ_MES_WO_SN.STATUSs.Finished.GetValue(); } var curNode = CurBatch.Nodes.First(q => q.OPER_CODE == "EndNode"); //条码完工 foreach (var wipSN in CurWipSNs) { wipSN.FINISHED_FLAG = "Y"; wipSN.NODE_ID = curNode.ID; wipSN.NODE_NAME = curNode.NODE_NAME; wipSN.OPER_CODE = curNode.OPER_CODE; wipSN.SEGMENT = curNode.SEGMENT; wipSN.OPERATION_TIME = DateTime.Now; } var wipHiss = new List(); foreach (var wipSN in CurWipSNs) { var his = new MES_WIP_HIS(wipSN, $"工单[{wipSN.WORK_ORDER}]条码[{wipSN.SN}]在岗位[{wipSN.POST_CODE}]过站工序[{wipSN.NODE_NAME}]成功"); wipSN.History.Add(his); wipHiss.Add(his); } //创建变量克隆对象用于传入DBSubmitAction中保存当前需要暂存的数据值 var _woSns = woSNs.Clone(); var _wipSns = CurWipSNs.Clone(); var _wipHiss = wipHiss.Clone(); var _pkgList = curPkg?.IsFinished == true ? GetPackageList(curPkg.Item).Clone() : new(); var _wipIDs = curPkg?.IsFinished == true ? curPkg.Item.GetWipSnList().Select(q => q.Key).ToList().Clone() : new(); //保存数据 endAction = () => { //使用统一的事务DB对象 var db = GetCommitDB(); //数据保存逻辑 db.Updateable(_woSns, UserCode).UpdateColumns(q => new { q.UPDATE_TIME, q.UPDATE_USER, q.STATUS }).ExecuteCommand(); db.Updateable(_wipSns, UserCode).UpdateColumns(q => new { q.UPDATE_TIME, q.UPDATE_USER, q.STATUS, q.NODE_ID, q.NODE_NAME, q.OPER_CODE, q.SEGMENT, q.OPERATION_TIME }).ExecuteCommand(); db.Storageable(_wipHiss, UserCode).ExecuteCommand(); if (_pkgList.Any()) { //var wipIDs = _pkgList.Where(q => !q.WIP_ID.IsNullOrEmpty()).Select(q => q.WIP_ID).ToList(); var wo = _pkgList.First().WORK_ORDER; //包装信息保存到生产过程记录 db.Updateable(UserCode) .SetColumns(q => q.TRAY_SN == curPkg.Item.Package.SN) .SetColumns(q => q.INNER_SN == q.CARTON_SN) .SetColumns(q => q.CARTON_SN == curPkg.Item.Package.SN) .Where(q => q.WORK_ORDER == wo && q.OPER_CODE == "EndNode" && _wipIDs.Contains(q.WIP_ID)) .ExecuteCommand(); } }; } //throw new Exception("哈"); //保存数据库 SaveStepsCommitActionToDB(endAction); //保存成功,返回过站消息 operInfo.InputQty += CurWipSNs.Count; action.Data.Data = new PackingActionOutput() { PkgInfo = curPkg }; action.Data.OperInfo = operInfo; action.Data.ShortMsg = new(CurWipSNs.Any(q => q.DFT_FLAG == "Y") ? "不良过站" : "良品过站", ShortMessage.Types.Success); //action.LocaleMsg = new($"工单[{CurWipSN.WORK_ORDER}]的条码[{CurWipSN.SN}]在岗位[{CurWipSN.POST_CODE}]工序[{CurWipSN.NODE_NAME}]过站成功,状态[{CurWipSN.STATUS.GetEnumDesc()}]"); action.LocaleMsg = new("MES.Transaction.PackingNode.ScanSn.PassSuccess", CurWipSNs.First().WORK_ORDER, CurSN, CurWipSNs.First().POST_CODE, CurWipSNs.First().NODE_NAME, CurWipSNs.First().STATUS.GetEnumDesc()); //如果当前条码已经完工,检查当前工单批次和工单是否完工 if (action.Data.OperInfo.IsReachedEndNode) { CurBatch.CheckIsComplete(UserCode); } //重置工序 ResetNode(); debug.Log(new StackFrame(true), "***** 15", info => { ConsoleExt.WriteLine(info.ToString()); }); return action; } /// /// 产品被判断不良后下线 /// /// /// public ApiAction OfflineIfDefect(ApiAction action, List wipSNs, string locale) { var operInfo = SetOperNodeInfo(CurOperInfo(locale)); operInfo.CurNode = wipSNs.FirstOrDefault()?.NODE_NAME; operInfo.NextNode = "下线退库"; //更新工单条码明细信息 var woSNs = CurBatch.WoSNs.Where(q => wipSNs.Any(w => q.WIP_ID == w.ID)).ToList(); foreach (var woSN in woSNs) { woSN.STATUS = BIZ_MES_WO_SN.STATUSs.Offline.GetValue(); } //条码下线 foreach (var wipSN in wipSNs) { wipSN.STATUS = MES_WIP_DATA.STATUSs.Offline.GetValue(); wipSN.UNBIND_FLAG = "Y"; } var wipHiss = new List(); foreach (var wipSN in wipSNs) { var his = new MES_WIP_HIS(wipSN, $"工单[{wipSN.WORK_ORDER}]条码[{wipSN.SN}]在岗位[{wipSN.POST_CODE}]工序[{wipSN.NODE_NAME}]因不良[{wipSN.Defects.FirstOrDefault()?.DFT_NAME}]下线(TransId: {TransID} )"); wipSN.History.Add(his); wipHiss.Add(his); } //创建变量克隆对象用于传入DBSubmitAction中保存当前需要暂存的数据值 var _woSns = woSNs.Clone(); var _wipSns = wipSNs.Clone(); var _wipHiss = wipHiss.Clone(); var _defect = CurDefects.Clone(); //使用统一的事务DB对象 var db = GetCommitDB(); //保存数据库 var dbTran = db.UseTran(() => { //数据保存逻辑 db.Storageable(_woSns, UserCode).ExecuteCommand(); db.Storageable(_wipSns, UserCode).ExecuteCommand(); db.Storageable(_wipHiss, UserCode).ExecuteCommand(); db.Storageable(_defect, UserCode).ExecuteCommand(); }); if (!dbTran.IsSuccess) { //抛出异常 throw dbTran.ErrorException; } //从工单中移除正在生产的条码 CurBatch.RemoveWipSn(wipSNs, UserCode); //保存成功,返回过站消息 action.Data.OperInfo = operInfo; action.Data.ShortMsg = new("下线退库", ShortMessage.Types.Warning); //action.LocaleMsg = new($"工单[0]的条码[1]在岗位[2]工序[3]因不良[4]下线,状态[5]"); action.LocaleMsg = new("MES.Transaction.TestNode.OfflineIfDefect", wipSNs.First().WORK_ORDER, CurSN, wipSNs.First().POST_CODE, wipSNs.First().NODE_NAME, wipSNs.First().Defects.FirstOrDefault()?.DFT_NAME, wipSNs.First().STATUS.GetEnumDesc()); //重置工序 ResetNode(); return action; } /// /// 包装工序:手动结束包装 /// /// public async Task> CompletePkg(ApiAction input) { var action = new ApiAction(new SubmitOutput()); try { //PkgSubmit:手动结束包装,保存暂存的包装数据 if (NodeCmd.IsNullOrEmpty() && CurWipSNs.IsNullOrEmpty()) { var CurPkg = Context.ContainsKey("CurPackage") ? Context["CurPackage"] as WipPkg : null; if (CurPkg?.IsFinished == false) { TempPkgAction = new PackingAction(); TempPkgAction.Init(this); int shipQty = input.Data.ToInt32(); double 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); return SetOutPutMqttMsg(action); } NodeCmd = "PkgSubmit"; 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, $"包装工序:手动结束包装异常"); NodeCmd = null; } return SetOutPutMqttMsg(action); } /// /// 打印测试标签 /// /// /// public async Task> TestPrint() { var action = new ApiAction(); try { var CurPkg = Context.ContainsKey("CurPackage") ? Context["CurPackage"] as WipPkg : null; var LabelPV = MainDB.Queryable().ToList(); var label = CurPkg.Item.LABEL_CODE.IsNullOrEmpty() ? null : MainDB.Queryable().Where(q => q.LABEL_CODE == CurPkg.Item.LABEL_CODE).IncludesAllFirstLayer().First(); var printLb = SetLabelVariables(LabelPV, label, new PackingAction(), CurPkg); action.Data = new PackingActionOutput() { ExecCode = "PrintTest", PkgInfo = CurPkg, PrintLable = printLb, }; } catch (Exception ex) { action.CatchExceptionWithLog(ex, $"包装工序:打印测试标签异常"); } return action; } /// /// 获取是否打印客户标签还是入库标签 /// /// public async Task> GetIsCustomerLabelAndShipQty() { var action = new ApiAction(); try { action.Data = new ShipingInfo() { IsPrintCustomerLabel = IsPrintCustomerLabel, ShipQty = Context.GetOrDefault("CurWaitShipment").ToInt32() >= Context.GetOrDefault("CurShipQty").ToInt32() ? CurBatch.Batch.PLAN_QTY - Context.GetOrDefault("CurShipQty").ToInt32() : Context.GetOrDefault("CurShipQty").ToInt32() }; } catch (Exception ex) { action.CatchExceptionWithLog(ex, $"包装工序:获取是否打印客户标签还是入库标签异常"); } return action; } #endregion Functions /// /// 重置当前工步,有需要则重写此方法 /// public override void ResetNode() { base.ResetNode(); NodeCmd = null; } public void RemoveCurSnFromCurPackage(WipPkgItem pkgItem) { if (!pkgItem.IsNullOrEmpty()) { var delItem = pkgItem.Items.Where(q => CurWipSNs.Any(sn => q.WipSNs.ContainsKey(sn.ID))).ToList(); if (delItem.Any()) { pkgItem.Items.RemoveAll(q => CurWipSNs.Any(sn => q.WipSNs.ContainsKey(sn.ID))); pkgItem.IsFinished = false; } else { foreach (var item in pkgItem.Items) { RemoveCurSnFromCurPackage(item); } } } } public override bool Close(bool needSaveHistoryLog = false) { //needSaveHistoryLog = true; //保存操作日志 this.IsFinished = true; return IsFinished ? base.Close(needSaveHistoryLog) : IsFinished; } }//endClass }