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;
|
|
namespace Tiger.Business.MES.Transaction
|
{
|
/// <summary>
|
/// 包装工序事务
|
/// </summary>
|
public class PackingNode : Position, IPackingNode
|
{
|
public new IPackingNode Init(string id, string apiHost, string userCode, string postCode)
|
{
|
base.Init(id, apiHost, userCode, postCode);
|
Logger.Console.Info($"User[{userCode}] start a {this.GetType().Name}[{postCode}] Transaction[ID: {TransID}]");
|
return this;
|
}
|
|
#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
|
/// <summary>
|
/// 包装工序:提交操作数据
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
public async Task<ApiAction<SubmitOutput>> Submit(SubmitInput input)
|
{
|
var action = new ApiAction<SubmitOutput>(new SubmitOutput());
|
try
|
{
|
//PkgSubmit:手动结束包装,手动保存暂存的包装数据
|
if (NodeCmd == "PkgSubmit")
|
{
|
action = TempPkgAction.Submit(input);
|
//如果行为被标记成已完成,则重置工序
|
if (TempPkgAction.IsFinished)
|
{
|
ResetNode();
|
}
|
return action;
|
}
|
//NodeSubmit:产品过站,正常产品过站逻辑
|
else
|
{
|
NodeCmd = "NodeSubmit";
|
//工步列表为空或者工序节点工步有未完成时,优先完成工序节点工步
|
if (Steps.IsNullOrEmpty() || !IsFinishNodeSteps)
|
{
|
//先判断当前工单不为空且当前岗位在当前工单是不是首站,如果是则不允许变更当前工单,尝试把条码绑定到当前工单
|
if (!CurBatch.IsNullOrEmpty() && CurBatch.IsFirstNode(PostCode))
|
{
|
action = NodeSubmit(action, input);
|
return action;
|
}
|
//当前岗位在当前工单不是首站,则查找条码已绑定的工单当作当前工单
|
else
|
{
|
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.TRAY_SN == input.SN).Select((q, s) => new { Batch = q, SN = s }).ToList();
|
|
//查找到条码已绑定的工单
|
if (!wosns.IsNullOrEmpty())
|
{
|
if (wosns.Count > 1 && wosns.Any(q => q.SN.TRAY_SN != q.SN.OUTER_SN))
|
{
|
action.IsSuccessed = false;
|
action.LocaleMsg = new($"条码[{0}]是载具条码,请扫描载具中的产品条码继续包装操作");
|
action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.OnlyMinPackage", input.SN);
|
return action;
|
}
|
if (wosns.First().Batch.ACT_LINE != CurLine.LINE_CODE)
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = new($"条码[{0}]已在产线[{1}]投入生产,请在正确岗位扫描");
|
action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.NotCorrectLine", input.SN, wosns.First().Batch.ACT_LINE);
|
return action;
|
}
|
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.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 = BeginNextActionStep(input);
|
return action;
|
}
|
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)
|
{
|
return DoIfFinishAllSteps(result);
|
}
|
return result;
|
}
|
}
|
//没有可执行的工步
|
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.CatchExceptionWithLog(ex, $"包装工序:提交操作数据异常");
|
action.CatchExceptionWithLog(ex, Biz.L("MES.Transaction.PackingNode.SubmitException"));
|
}
|
return action;
|
}
|
|
/// <summary>
|
/// 工序节点工步提交数据
|
/// </summary>
|
/// <param name="action"></param>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
public ApiAction<SubmitOutput> NodeSubmit(ApiAction<SubmitOutput> action, SubmitInput input)
|
{
|
var curNode = CurBatch.GetNode(PostCode);
|
try
|
{
|
//判断工单实时状态判断是否可以生产
|
var woStatus = CurBatch.CheckCanProduce(curNode);
|
if (!woStatus.IsSuccessed)
|
{
|
return woStatus;
|
}
|
//非法过站防呆:进入工序时要增加判断条码是否按流程过站
|
var wipSNs = Biz.Db.Queryable<MES_WIP_DATA>().IncludesAllFirstLayer().Where(q => (q.SN == input.SN || q.TRAY_SN == input.SN) && q.WORK_ORDER == CurBatch.WO.ORDER_NO).ToList();
|
if (wipSNs.IsNullOrEmpty())
|
{
|
var wipSN = new MES_WIP_DATA()
|
{
|
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",
|
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,
|
};
|
wipSNs.Add(wipSN);
|
}
|
var canGotoNext = CurBatch.CanGotoNext(input, wipSNs.First(), curNode);
|
if (!canGotoNext.IsSuccessed)
|
{
|
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,
|
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;
|
}
|
}
|
|
//工单开工
|
CurBatch.StartWorking(UserCode);
|
|
//把当前条码增加到当前条码列表
|
CurWipSNs = wipSNs;
|
CurWipSNHiss.Clear();
|
foreach (var wipSN in wipSNs)
|
{
|
CurWipSNHiss.Add(new MES_WIP_HIS(wipSN, $"工单[{wipSN.WORK_ORDER}]条码[{wipSN.SN}]在岗位[{wipSN.POST_CODE}]过站工序[{wipSN.NODE_NAME}]成功"));
|
}
|
|
//初始化工步列表
|
Steps.Clear();
|
var curStep = new Biz.WorkStep(IWorkStep.Types.Node, this)
|
{
|
Sequence = Steps.Count + 1,
|
Node = curNode,
|
DBSubmitAction = () =>
|
{
|
var db = GetCommitDB();
|
db.Storageable(woSNs, UserCode).ExecuteCommand();
|
db.Storageable(wipSNs, UserCode).ExecuteCommand();
|
db.Storageable(CurWipSNHiss, UserCode).ExecuteCommand();
|
//如果是投入站
|
if (curNode.IS_INPUT == "Y")
|
{
|
db.Updateable<BIZ_MES_WO>().SetColumns(q => q.INPUT_QTY == q.INPUT_QTY + wipSNs.Count).Where(q => q.ORDER_NO == CurBatch.WO.ORDER_NO).ExecuteCommand();
|
db.Updateable<BIZ_MES_WO_BATCH>().SetColumns(q => q.INPUT_QTY == q.INPUT_QTY + wipSNs.Count).Where(q => q.BATCH_NO == CurBatch.Batch.BATCH_NO).ExecuteCommand();
|
}
|
//如果是产出站
|
if (curNode.IS_OUTPUT == "Y")
|
{
|
db.Updateable<BIZ_MES_WO>().SetColumns(q => q.OUTPUT_QTY == q.OUTPUT_QTY + wipSNs.Count).Where(q => q.ORDER_NO == CurBatch.WO.ORDER_NO).ExecuteCommand();
|
db.Updateable<BIZ_MES_WO_BATCH>().SetColumns(q => q.OUTPUT_QTY == q.OUTPUT_QTY + wipSNs.Count).Where(q => q.BATCH_NO == CurBatch.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 = curStep;
|
}
|
else if (!IsFinishNodeSteps)
|
{
|
var curStep = Steps.Where(q => q.Type == IWorkStep.Types.Node && !q.IsFinished).OrderBy(q => q.Sequence).First();
|
|
//完成当前工序节点工步
|
curStep.IsFinished = true;
|
CurStep = curStep;
|
}
|
|
//未完成所有工步
|
if (!IsFinishAllSteps)
|
{
|
var result = new ApiAction<SubmitOutput>(new SubmitOutput());
|
//未完成所有工序节点工步
|
if (!IsFinishNodeSteps)
|
{
|
var next = Steps.Where(q => q.Type == IWorkStep.Types.Node && !q.IsFinished).OrderBy(q => q.Sequence).First();
|
//设置后续可执行的工步列表
|
NextSteps.Clear();
|
NextSteps.Add(next);
|
//根据后续工步返回ApiAction
|
result.Data.SetValue(CurBatch, CurStep, next.ID, IsFinishAllSteps);
|
|
//根据工序节点工步的序号返回相应的操作提示
|
switch (next.Sequence)
|
{
|
case 2:
|
//action.LocaleMsg = new($"请执行第二步");
|
result.LocaleMsg = new("MES.Transaction.CollectNode.第二步操作提示");
|
break;
|
default:
|
break;
|
}
|
}
|
//已完成所有工序节点工步,开始执行行为工步
|
else
|
{
|
result = BeginNextActionStep(input);
|
}
|
return result;
|
}
|
//已完成所有工步
|
else
|
{
|
action.Data.SetValue(CurBatch, CurStep, "", IsFinishAllSteps);
|
action = DoIfFinishAllSteps(action);
|
}
|
}
|
catch (Exception ex)
|
{
|
ResetNode();
|
//action.CatchExceptionWithLog(ex, $"{curNode.NODE_NAME}:工序节点工步提交数据异常,请检查工序节点设置");
|
action.CatchExceptionWithLog(ex, Biz.L("MES.Transaction.PackingNode.NodeSubmitException", curNode.NODE_NAME));
|
}
|
return action;
|
}
|
|
/// <summary>
|
/// 完成所有工步后执行
|
/// </summary>
|
/// <param name="action"></param>
|
/// <returns></returns>
|
public ApiAction<SubmitOutput> DoIfFinishAllSteps(ApiAction<SubmitOutput> action)
|
{
|
//保存数据库
|
SaveStepsCommitActionToDB();
|
|
//保存成功,返回过站消息
|
//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()
|
{
|
var action = new ApiAction<SubmitOutput>(new SubmitOutput());
|
try
|
{
|
//PkgSubmit:手动结束包装,保存暂存的包装数据
|
if (NodeCmd.IsNullOrEmpty("PkgSubmit") == "PkgSubmit")
|
{
|
if (Context.ContainsKey("CurPackage"))
|
{
|
NodeCmd = "PkgSubmit";
|
|
TempPkgAction = new PackingAction();
|
TempPkgAction.Init(null, this, null, null);
|
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;
|
//保存操作日志
|
|
this.IsFinished = true;
|
return IsFinished ? base.Close(needSaveHistoryLog) : IsFinished;
|
}
|
}//endClass
|
}
|