| | |
| | | using System.Threading.Tasks; |
| | | using Tiger.Model; |
| | | using static Tiger.Business.Biz; |
| | | using Org.BouncyCastle.Ocsp; |
| | | using Tiger.Model.Entitys.MES.Position; |
| | | using MailKit.Search; |
| | | |
| | | namespace Tiger.Business.MES.Transaction |
| | | { |
| | |
| | | //if (CurFactory.IsNullOrEmpty()) throw new InvalidDataException($"MES.Transaction.Position.FactoryNotExistsException", new Exception($"{postCode}|{CurWorkshop.FTY_CODE}")); |
| | | if (CurFactory.IsNullOrEmpty()) throw new InvalidDataException($"岗位[{postCode}]所属的工厂[{CurWorkshop.FTY_CODE}]不存在,请先设置所属工厂", new Exception($"{postCode}|{CurWorkshop.FTY_CODE}")); |
| | | |
| | | //加载当前产线的班制 |
| | | CurShiftSys = Biz.Db.Queryable<MES_SHIFT_SYS>().Where(q => q.SFTS_CODE == CurLine.SFTS_CODE).IncludesAllFirstLayer().IncludesAllSecondLayer(q => q.Shifts).First(); |
| | | |
| | | return this; |
| | | } |
| | | |
| | |
| | | public MES_WORKSHOP CurWorkshop { get; set; } |
| | | public MES_LINE CurLine { get; set; } |
| | | public MES_POSITION CurPosition { get; set; } |
| | | public MES_SHIFT_SYS CurShiftSys { get; set; } |
| | | public WorkBatch CurBatch { get; set; } |
| | | public IWorkBatch WorkBatch => CurBatch; |
| | | public MES_WIP_DATA CurWipSN { get; set; } |
| | | public MES_WIP_HIS CurWipSNHis { get; set; } |
| | | public List<MES_WIP_DATA> CurWipSNs { get; set; } = new(); |
| | | public string CurSN => (CurWipSNs.Any() ? (CurWipSNs.First().TRAY_SN.IsNullOrEmpty() ? CurWipSNs.First().SN : CurWipSNs.First().TRAY_SN) : ""); |
| | | public List<MES_WIP_HIS> CurWipSNHiss { get; set; } = new(); |
| | | public List<MES_WIP_DFT> CurDefects { get; set; } = new(); |
| | | public Dictionary<string, object> Context { get; set; } = new(); |
| | | public List<WorkStep> Steps { get; set; } = new(); |
| | | public bool IsFinishAllSteps => Steps.Any() && !Steps.Any(q => !q.IsFinished); |
| | | public int CurStep => Steps.Where(q => !q.IsFinished).OrderBy(q => q.Sequence).FirstOrDefault()?.Sequence ?? 0; |
| | | public WorkStep CurStep { get; set; } |
| | | public List<WorkStep> NextSteps { get; set; } = new(); |
| | | public bool IsFinishNodeSteps => !Steps.Any(q => q.NodeType == IWorkStep.NodeTypes.Node && !q.IsFinished); |
| | | public bool IsFinishAllSteps => !Steps.Any() || !Steps.Any(q => !q.IsFinished); |
| | | //public int CurStep => Steps.Where(q => !q.IsFinished).OrderBy(q => q.Sequence).FirstOrDefault()?.Sequence ?? 0; |
| | | private DbClient CommitDB; |
| | | /// <summary> |
| | | /// 是否需要临时存储数据库提交操作,待需要的时候再提交 |
| | | /// </summary> |
| | | public bool NeedTemporaryStoreDBCommitAction { get; set; } = false; |
| | | protected Dictionary<string, List<Action>> DBCommitList { get; set; } = new(); |
| | | protected List<Position> NodeCommitList { get; set; } = new(); |
| | | protected Dictionary<string, OperInfo> OperInfoDic { get; set; } = new(); |
| | | //protected OperInfo CurOperInfo { get; set; } |
| | | #endregion Propertys & Variables |
| | | |
| | | #region Functions |
| | |
| | | action.LocaleMsg = new("MES.Transaction.Position.SelectOrder.LineException", input.OrderNo, CurLine.LINE_CODE); |
| | | return action; |
| | | } |
| | | if (batch.STATUS != BIZ_MES_WO_BATCH.STATUSs.Release.GetValue() && batch.STATUS != BIZ_MES_WO_BATCH.STATUSs.Working.GetValue()) |
| | | { |
| | | action.IsSuccessed = false; |
| | | //action.LocaleMsg = new($"工单[{input.OrderNo}]状态[{wo.STATUS.GetEnum<BIZ_MES_WO_BATCH.STATUSs>().GetName()}]不能生产"); |
| | | action.LocaleMsg = new("MES.Transaction.Position.SelectOrder.StatusException", input.OrderNo, batch.STATUS.GetEnum<BIZ_MES_WO_BATCH.STATUSs>().GetName()); |
| | | return action; |
| | | } |
| | | var wb = new WorkBatch(input.OrderNo).Init(CurLine.LINE_CODE); |
| | | WoContext.WoBatchDic.Add(wb.Batch.BATCH_NO, wb); |
| | | } |
| | | CurBatch = WoContext.GetBatch(input.OrderNo, CurLine.LINE_CODE); |
| | | if (!OperInfoDic.ContainsKey(CurBatch.Batch.BATCH_NO)) |
| | | { |
| | | OperInfoDic.Add(CurBatch.Batch.BATCH_NO, new()); |
| | | } |
| | | |
| | | action.Data = new { WorkOrder = CurBatch.WO, Bacth = CurBatch.Batch }; |
| | | } |
| | | catch (Exception ex) |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 重置当前工步 |
| | | /// 获取当前时间所属的班制班次时段 |
| | | /// </summary> |
| | | public void ResetSteps() |
| | | public ShiftPeriod GetShiftPeriodForNow() |
| | | { |
| | | var time = DateTime.Now.ToString("HHmm").ToInt32(); |
| | | foreach (var shift in CurShiftSys.Shifts) |
| | | { |
| | | var period = shift.Periods.Where(q => q.PRD_BEGIN <= time && time <= q.PRD_END).FirstOrDefault(); |
| | | if (!period.IsNullOrEmpty()) |
| | | { |
| | | var result = new ShiftPeriod(); |
| | | result.ShiftSys = CurShiftSys; |
| | | result.Shift = shift; |
| | | result.Period = period; |
| | | return result; |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 重置工序操作 |
| | | /// </summary> |
| | | /// <returns></returns> |
| | | public ApiAction Reset() |
| | | { |
| | | var action = new ApiAction(); |
| | | |
| | | ResetNode(); |
| | | action.IsSuccessed = true; |
| | | action.LocaleMsg = new($"工序操作已重置,请重新扫描进站产品条码"); |
| | | action.LocaleMsg = new("MES.Transaction.Position.ResetNode"); |
| | | |
| | | return action; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 重置当前工序,有需要则重写此方法 |
| | | /// </summary> |
| | | public virtual void ResetNode() |
| | | { |
| | | Steps.Clear(); |
| | | CurWipSN = null; |
| | | CurWipSNs.Clear(); |
| | | CurWipSNHiss.Clear(); |
| | | CurStep = null; |
| | | CurDefects.Clear(); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 设置当前条码的工序信息 |
| | | /// </summary> |
| | | public OperInfo SetOperNodeInfo(OperInfo info, string locale) |
| | | { |
| | | if (CurWipSNs.Any()) |
| | | { |
| | | info.CurNode = CurWipSNs.First().NODE_NAME; |
| | | info.NextNode = string.Join(",", CurBatch.GetNextNodes(CurWipSNs.First()).Select(q => q.NODE_NAME)); |
| | | info.StepsInfo = Steps.Select(q => q.GetInfo(locale)).ToList(); |
| | | } |
| | | else |
| | | { |
| | | info.CurNode = " — "; |
| | | info.NextNode = " — "; |
| | | info.StepsInfo = new(); |
| | | } |
| | | return info; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 生成传入工步后续的行为到工步列表 |
| | | /// </summary> |
| | | /// <param name="parent"></param> |
| | | public void GenerateSteps(WorkStep parent) |
| | | { |
| | | //递归添加所有工步 |
| | | AddNextActToSteps(parent); |
| | | //调整工步的序号 |
| | | //while (CurBatch.Edges.Any(q => Steps.First(s => s.NodeID == q.SRC_NODE).Sequence <= Steps.First(s => s.NodeID == q.TGT_NODE).Sequence)) |
| | | //{ |
| | | // var edges = CurBatch.Edges.Where(q => Steps.First(s => s.NodeID == q.SRC_NODE).Sequence >= Steps.First(s => s.NodeID == q.TGT_NODE).Sequence).ToList(); |
| | | // foreach (var edge in edges) |
| | | // { |
| | | // var source = Steps.First(s => s.NodeID == edge.SRC_NODE); |
| | | // var target = Steps.First(s => s.NodeID == edge.TGT_NODE); |
| | | // target.Sequence = source.Sequence + 1; |
| | | // } |
| | | //} |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 添加节点的下一个行为到工步列表 |
| | | /// </summary> |
| | | /// <param name="parent"></param> |
| | | private void AddNextActToSteps(WorkStep parent) |
| | | { |
| | | var edges = CurBatch.Edges.Where(q => q.SRC_NODE == parent.NodeID && CurBatch.NodeActs.Any(a => a.NODE_ID == parent.Node.ID && a.ID == q.TGT_NODE)).ToList(); |
| | | foreach (var edge in edges) |
| | | { |
| | | var act = CurBatch.NodeActs.First(q => q.ID == edge.TGT_NODE); |
| | | if (Steps.Any(q => q.NodeID == act.ID)) |
| | | { |
| | | var next = Steps.First(q => q.NodeID == act.ID); |
| | | next.Sequence = next.Sequence > parent.Sequence ? next.Sequence : (parent.Sequence + 1); |
| | | next.PrepNodeIDs.Add(parent.NodeID); |
| | | next.PrepNodeIDs.AddRange(parent.PrepNodeIDs); |
| | | next.PrepNodeIDs = next.PrepNodeIDs.Distinct().ToList(); |
| | | AddNextActToSteps(next); |
| | | } |
| | | else |
| | | { |
| | | var next = new WorkStep(IWorkStep.NodeTypes.Action, this) |
| | | { |
| | | Name = act.ACT_NAME, |
| | | Sequence = parent.Sequence + 1, |
| | | Node = parent.Node, |
| | | OperSetting = CurBatch.NodeSets.FirstOrDefault(q => q.NODE_ID == parent.Node.ID), |
| | | NodeAct = act, |
| | | ActSetting = CurBatch.ActionSets.FirstOrDefault(q => q.ACT_ID == act.ID), |
| | | }; |
| | | next.Init(); |
| | | next.PrepNodeIDs.Add(parent.NodeID); |
| | | next.PrepNodeIDs.AddRange(parent.PrepNodeIDs); |
| | | next.PrepNodeIDs = next.PrepNodeIDs.Distinct().ToList(); |
| | | Steps.Add(next); |
| | | AddNextActToSteps(next); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 根据传入的工步,返回下一步可执行的工步列表 |
| | | /// </summary> |
| | | /// <param name="curStep"></param> |
| | | public List<WorkStep> GetNextSteps(WorkStep curStep) |
| | | { |
| | | var result = new List<WorkStep>(); |
| | | var nextSteps = Steps.Where(q => !q.IsFinished |
| | | && CurBatch.Edges.Any(e => e.SRC_NODE == curStep.NodeID && e.TGT_NODE == q.NodeID) |
| | | ).ToList(); |
| | | //尝试将当前工步的后续工步添加到可以执行的工步列表 |
| | | foreach (var step in nextSteps) |
| | | { |
| | | //查找有没有前置工步未完成,若有则不允许继续执行 |
| | | if (!Steps.Any(q => step.PrepNodeIDs.Any(id => id == q.NodeID && !q.IsFinished))) |
| | | { |
| | | result.Add(step); |
| | | } |
| | | } |
| | | //如果当前工步没有可执行的后续工步,则在前置工步查找还有没有后续工步没完成的工步,有则执行 |
| | | if (!result.Any() && Steps.Any()) |
| | | { |
| | | //查找有没有前置工步未完成,若有则先完成未完成的前置工步 |
| | | var prepIDs = curStep.PrepNodeIDs.Where(id => CurBatch.Edges.Any(e => e.SRC_NODE == id && Steps.Any(q => !q.IsFinished && e.TGT_NODE == q.NodeID))).ToList(); |
| | | var prepSteps = Steps.Where(q => prepIDs.Contains(q.NodeID)).OrderByDescending(q => q.Sequence).ToList(); |
| | | while (prepSteps.Any() && !result.Any()) |
| | | { |
| | | var prep = prepSteps.First(); |
| | | var prepNext = GetNextSteps(prep); |
| | | if (prepNext.Any()) |
| | | { |
| | | result = prepNext; |
| | | } |
| | | else |
| | | { |
| | | prepSteps.Remove(prep); |
| | | } |
| | | } |
| | | |
| | | } |
| | | return result.OrderBy(q => q.NodeAct.ACT_NAME).ToList(); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 开始执行下一行为工步 |
| | | /// </summary> |
| | | /// <param name="input"></param> |
| | | public ApiAction<SubmitOutput> BeginNextActionStep(SubmitInput input) |
| | | { |
| | | var result = new ApiAction<SubmitOutput>(new SubmitOutput()); |
| | | //设置后续可执行的工步列表 |
| | | NextSteps = GetNextSteps(CurStep); |
| | | //尝试有没有可以直接开始的后续工步 |
| | | foreach (var step in NextSteps) |
| | | { |
| | | //尝试执行后续工步 |
| | | var canBegin = step.TryBegin(input); |
| | | //如果后续工步可以直接开始则直接执行 |
| | | if (canBegin.IsSuccessed) |
| | | { |
| | | //更新当前执行工步为已开始工步 |
| | | CurStep = step; |
| | | //更新后续可执行的工步列表 |
| | | NextSteps = GetNextSteps(CurStep); |
| | | //返回结果到客户端 |
| | | result = canBegin; |
| | | result.Data.SetValue(CurBatch, CurStep, CurStep?.ID, IsFinishAllSteps); |
| | | |
| | | return result; |
| | | } |
| | | } |
| | | |
| | | //没有可以直接开始的后续工步,根据后续工步返回ApiAction |
| | | result.Data.SetValue(CurBatch, CurStep, "", IsFinishAllSteps); |
| | | |
| | | //没有可以直接开始的后续工步,根据后续可执行工步列表返回相应的操作提示 |
| | | if (NextSteps.Count == 1) |
| | | { |
| | | result.LocaleMsg = NextSteps.First().GetBeginMsg(); |
| | | } |
| | | else |
| | | { |
| | | result.LocaleMsg = new(T(L("MES.Transaction.Position.PleaseSelectNextStep"), input.Locale) + "\r\n" + |
| | | string.Join("\r\n", NextSteps.Select(q => " >> " + T(q.GetBeginMsg(), input.Locale)))); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 保存工步的数据库提交操作到数据库 |
| | | /// </summary> |
| | | protected void SaveStepsCommitActionToDB() |
| | | { |
| | | //保存工步的数据库提交操作到提交操作列表 |
| | | var commitList = new List<Action>(); |
| | | foreach (var step in Steps.OrderBy(q => q.Sequence)) |
| | | { |
| | | commitList.Add(step.DBSubmitAction); |
| | | } |
| | | DBCommitList.Add(CurSN, commitList); |
| | | //如果不需要临时存储数据库提交操作,则把提交操作列表提交到数据库 |
| | | if (!NeedTemporaryStoreDBCommitAction) |
| | | { |
| | | //恢复临时存储标记为false |
| | | NeedTemporaryStoreDBCommitAction = false; |
| | | |
| | | var dbTran = GetCommitDB().UseTran(() => |
| | | { |
| | | //在同一个事务中保存所有工步的数据 |
| | | foreach (var wipSn in DBCommitList.Keys) |
| | | { |
| | | foreach (var action in DBCommitList[wipSn]) |
| | | { |
| | | action.Invoke(); |
| | | } |
| | | } |
| | | }); |
| | | if (dbTran.IsSuccess) |
| | | { |
| | | //保存成功则清空提交操作列表 |
| | | DBCommitList.Clear(); |
| | | } |
| | | else |
| | | { |
| | | //抛出异常 |
| | | throw dbTran.ErrorException; |
| | | } |
| | | } |
| | | } |
| | | |
| | | protected void DoSaveToDB() |
| | | { |
| | | var dbTran = GetCommitDB().UseTran(() => |
| | | { |
| | | //在同一个事务中保存所有工步的数据 |
| | | foreach (var step in Steps.OrderBy(q => q.Sequence)) |
| | | { |
| | | step.DBSubmitAction.Invoke(); |
| | | } |
| | | }); |
| | | if (!dbTran.IsSuccess) |
| | | { |
| | | //抛出异常 |
| | | throw dbTran.ErrorException; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取打印标签模板过程变量值 |
| | | /// </summary> |
| | | /// <param name="labelPVs">过程变量列表</param> |
| | | /// <param name="label">标签模板</param> |
| | | /// <returns></returns> |
| | | public BAS_LABEL_TEMP SetLabelVariables(List<BAS_LABEL_PV> labelPVs, BAS_LABEL_TEMP label, IWorkAction action) |
| | | { |
| | | foreach (var item in label.Variables) |
| | | { |
| | | switch (item.VAR_TYPE.GetEnum<BAS_LABEL_VAR.VAR_TYPEs>()) |
| | | { |
| | | case BAS_LABEL_VAR.VAR_TYPEs.Constant: |
| | | item.Value = item.VAR_VALUE; |
| | | break; |
| | | case BAS_LABEL_VAR.VAR_TYPEs.ProcessVariable: |
| | | item.Value = GetPrintProcessValue(labelPVs, item, action); |
| | | break; |
| | | case BAS_LABEL_VAR.VAR_TYPEs.DateVariable: |
| | | item.Value = DateTime.Now.ToString(item.VAR_VALUE); |
| | | break; |
| | | case BAS_LABEL_VAR.VAR_TYPEs.BarcodeGenerate: |
| | | item.Value = GetGeneratePValue(labelPVs, item); |
| | | break; |
| | | case BAS_LABEL_VAR.VAR_TYPEs.CustomVariable: |
| | | default: |
| | | item.Value = ""; |
| | | break; |
| | | } |
| | | } |
| | | return label; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取打印标签模板过程变量值 |
| | | /// </summary> |
| | | /// <param name="labelPVs">过程变量列表</param> |
| | | /// <param name="lv">标签模板变量</param> |
| | | /// <returns></returns> |
| | | public string GetPrintProcessValue(List<BAS_LABEL_PV> labelPVs, BAS_LABEL_VAR lv, IWorkAction action) |
| | | { |
| | | var pv = labelPVs.FirstOrDefault(q => q.VAR_CODE == lv.VAR_VALUE); |
| | | if (!pv.IsNullOrEmpty()) |
| | | { |
| | | switch (pv.VAR_TYPE.GetEnum<BAS_LABEL_PV.VAR_TYPEs>()) |
| | | { |
| | | case BAS_LABEL_PV.VAR_TYPEs.ServerMethod: |
| | | { |
| | | switch (pv.VAR_METHOD) |
| | | { |
| | | case "GetSN": |
| | | return CurSN; |
| | | case "GetBAS_ITEM": |
| | | return WorkBatch.Product.ToJson(); |
| | | case "GetCustomer": |
| | | return WorkBatch.Batch.ToJson(); |
| | | case "GetVarByWo": |
| | | return GetLabelVarWo(lv); |
| | | default: |
| | | return ""; |
| | | } |
| | | } |
| | | case BAS_LABEL_PV.VAR_TYPEs.WebApi: |
| | | break; |
| | | case BAS_LABEL_PV.VAR_TYPEs.StoredProcedure: |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | return ""; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取根据条码规则生成标签模板的过程变量值 |
| | | /// </summary> |
| | | /// <param name="labelPVs"></param> |
| | | /// <param name="lv"></param> |
| | | /// <returns></returns> |
| | | public string GetGeneratePValue(List<BAS_LABEL_PV> labelPVs, BAS_LABEL_VAR lv) |
| | | { |
| | | var pv = labelPVs.FirstOrDefault(q => q.VAR_CODE == lv.VAR_VALUE); |
| | | if (!pv.IsNullOrEmpty()) |
| | | { |
| | | switch (pv.VAR_TYPE.GetEnum<BAS_LABEL_PV.VAR_TYPEs>()) |
| | | { |
| | | case BAS_LABEL_PV.VAR_TYPEs.BarcodeGenerate: |
| | | { |
| | | switch (pv.VAR_METHOD) |
| | | { |
| | | case "GetCartonGenerate": |
| | | return Biz.CodeRule[lv.BARCODE_RULE ?? ""]?.Generate($"{WorkBatch.Batch.BATCH_NO}-{WorkBatch.Batch.PLAN_QTY}-").Data.ToString() ?? ""; |
| | | case "GetHW21SNGenerate": |
| | | return Biz.CodeRule[lv.BARCODE_RULE ?? ""]?.Generate("SN:","05").Data.ToString() ?? ""; |
| | | default: |
| | | return ""; |
| | | } |
| | | } |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | return ""; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取工单维护的模板变量 |
| | | /// </summary> |
| | | /// <param name="labelId"></param> |
| | | /// <returns></returns> |
| | | private string GetLabelVarWo(BAS_LABEL_VAR lv) |
| | | { |
| | | string result = ""; |
| | | var labelVarwos = Biz.Db.Queryable<BAS_LABEL_VAR_WO>().Where(x => x.LABEL_ID == lv.LABEL_ID && x.VAR_NAME == lv.VAR_NAME).ToList(); |
| | | if (labelVarwos.Any(q => q.WORK_ORDER == WorkBatch.Batch.ORDER_NO)) |
| | | { |
| | | result = labelVarwos.First(q => q.WORK_ORDER == WorkBatch.Batch.ORDER_NO).VAR_VALUE; |
| | | } |
| | | else |
| | | { |
| | | result = labelVarwos.Count > 0 ? labelVarwos[0].DEFAULT_VALUE : ""; |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | #endregion Functions |