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
{
///
/// 包装工序事务
///
public class YadaPacking : Position, IYadaPacking
{
public new IYadaPacking 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
///
/// 工序当前执行的指令代码,包括:
/// {null or ""}:工序空闲,指令为空则认为工序没有执行任何指令
/// NodeSubmit:产品过站,正常产品过站逻辑
/// PkgSubmit:手动结束包装,手动保存暂存的包装数据
///
public string NodeCmd { get; set; }
public PackingAction TempPkgAction { get; set; }
public bool IsPrintCustomerLabel { get; set; }
public string ShippingOrder { get; set; }
#endregion Propertys & Variables
#region Functions
///
/// 包装工序:提交操作数据
///
///
///
public async Task> Submit(SubmitInput input)
{
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 action;
//}
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 = Biz.Db.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();
//先判断当前工单不为空且当前岗位在当前工单是不是首站,如果是则不允许变更当前工单,尝试把条码绑定到当前工单
if (!CurBatch.IsNullOrEmpty() && CurBatch.IsFirstNode(PostCode))
{
//更新CurBatch
CurBatch?.Update();
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);
//更新工序信息
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 action;
}
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 action;
}
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 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 (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();
var submitStep = Steps.Where(q => q.ID == input.CurStepID && !q.IsFinished).FirstOrDefault();
//如果客户端返回的当前工步ID找不到未完成的行为工步,则直接开始下一个行为工步
if (submitStep.IsNullOrEmpty())
{
action = BeginNextActionStep(input);
}
else
{
action = submitStep.Submit(input);
//如果当前工步已完成,开始执行下一工步
if (action.IsSuccessed && submitStep.IsFinished)
{
action = BeginNextActionStep(input);
}
//如果当前工步未完成
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);
//action.CatchExceptionWithLog(ex, $"包装工序:提交操作数据异常");
action.CatchExceptionWithLog(ex, Biz.L("MES.Transaction.PackingNode.SubmitException"));
}
return action;
}
///
/// 工序节点工步提交数据
///
///
///
///
public ApiAction NodeSubmit(ApiAction action, SubmitInput input)
{
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 = Biz.Db.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";
//绑定条码到工单
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;
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();
//初始化工步列表
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);
ResetNode();
//action.CatchExceptionWithLog(ex, $"{curNode.NODE_NAME}:工序节点工步提交数据异常,请检查工序节点设置");
action.CatchExceptionWithLog(ex, Biz.L("MES.Transaction.PackingNode.NodeSubmitException", curNode.NODE_NAME));
}
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;
//如果当前条码已经走到流程终点则记录条码完工
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) : 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();
}
};
}
//保存数据库
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();
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();
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);
return 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 action;
}
///
/// 打印测试标签
///
///
///
public async Task> TestPrint()
{
var action = new ApiAction();
try
{
var CurPkg = Context.ContainsKey("CurPackage") ? Context["CurPackage"] as WipPkg : null;
var LabelPV = Biz.Db.Queryable().ToList();
var label = CurPkg.Item.LABEL_CODE.IsNullOrEmpty() ? null : Biz.Db.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;
}
#endregion Functions
///
/// 重置当前工步,有需要则重写此方法
///
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
}