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 static Tiger.Business.Biz;
|
using Tiger.Model.Entitys.MES.Position;
|
|
namespace Tiger.Business.MES.Transaction
|
{
|
/// <summary>
|
/// MES岗位事务
|
/// </summary>
|
public class Position : MESTransactionBase, IPosition
|
{
|
public IPosition Init(string id, string apiHost, string userCode, string postCode)
|
{
|
TransID = id;
|
UserCode = userCode;
|
ApiHost = apiHost;
|
PostCode = postCode;
|
|
CurPosition = Biz.Db.Queryable<MES_POSITION>().Where(q => q.POST_CODE == postCode).First();
|
//if (CurPosition.IsNullOrEmpty()) throw new InvalidDataException($"MES.Transaction.Position.PositionNotExistsException", new Exception($"{postCode}|"));
|
if (CurPosition.IsNullOrEmpty()) throw new InvalidDataException($"岗位[{postCode}]不存在,请提交正确的岗位代码", new Exception($"{postCode}|"));
|
CurLine = Biz.Db.Queryable<MES_LINE>().Where(q => q.LINE_CODE == CurPosition.LINE_CODE).First();
|
//if (CurLine.IsNullOrEmpty()) throw new InvalidDataException($"MES.Transaction.Position.LineNotExistsException", new Exception($"{postCode}|{CurPosition.LINE_CODE}"));
|
if (CurLine.IsNullOrEmpty()) throw new InvalidDataException($"岗位[{postCode}]所属的产线[{CurPosition.LINE_CODE}]不存在,请先设置所属产线", new Exception($"{postCode}|{CurPosition.LINE_CODE}"));
|
CurWorkshop = Biz.Db.Queryable<MES_WORKSHOP>().Where(q => q.WS_CODE == CurLine.WS_CODE).First();
|
//if (CurWorkshop.IsNullOrEmpty()) throw new InvalidDataException($"MES.Transaction.Position.WorkshopNotExistsException", new Exception($"{postCode}|{CurLine.WS_CODE}"));
|
if (CurWorkshop.IsNullOrEmpty()) throw new InvalidDataException($"岗位[{postCode}]所属的车间[{CurLine.WS_CODE}]不存在,请先设置所属车间", new Exception($"{postCode}|{CurLine.WS_CODE}"));
|
CurFactory = Biz.Db.Queryable<MES_FACTORY>().Where(q => q.FTY_CODE == CurWorkshop.FTY_CODE).First();
|
//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;
|
}
|
|
#region Propertys & Variables
|
public string UserCode { get; set; }
|
public string PostCode { get; set; }
|
public MES_FACTORY CurFactory { get; set; }
|
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 Dictionary<string, object> Context { get; set; } = new();
|
public List<WorkStep> Steps { get; set; } = new();
|
public WorkStep CurStep { get; set; }
|
public List<WorkStep> NextSteps { get; set; } = new();
|
public bool IsFinishNodeSteps => !Steps.Any(q => q.Type == IWorkStep.Types.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 List<Action> DBCommitList { get; set; } = new();
|
|
#endregion Propertys & Variables
|
|
#region Functions
|
/// <summary>
|
/// 获取提交数据的DbClient对象
|
/// </summary>
|
/// <returns></returns>
|
public DbClient GetCommitDB()
|
{
|
return CommitDB ??= Biz.Db;
|
}
|
/// <summary>
|
/// 选择工单
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
public async Task<ApiAction> SelectOrder(WoInput input)
|
{
|
var action = new ApiAction();
|
try
|
{
|
if (!WoContext.ExistsBatch(input.OrderNo, CurLine.LINE_CODE))
|
{
|
var wo = await Biz.Db.Queryable<BIZ_MES_WO>().ByAuth(input.AuthOption).Where(q => q.ORDER_NO == input.OrderNo).FirstAsync();
|
//验证明细是否正确
|
if (wo.IsNullOrEmpty())
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = new($"工单[{input.OrderNo}]不存在", input.OrderNo);
|
action.LocaleMsg = new("MES.Transaction.Position.SelectOrder.EmptyException", input.OrderNo);
|
return action;
|
}
|
if (wo.STATUS != BIZ_MES_WO.STATUSs.Release.GetValue() && wo.STATUS != BIZ_MES_WO.STATUSs.Working.GetValue())
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = new($"工单[{input.OrderNo}]状态[{wo.STATUS.GetEnum<BIZ_MES_WO.STATUSs>().GetName()}]不能生产");
|
action.LocaleMsg = new("MES.Transaction.Position.SelectOrder.StatusException", input.OrderNo, wo.STATUS.GetEnum<BIZ_MES_WO.STATUSs>().GetName());
|
return action;
|
}
|
var batch = await Biz.Db.Queryable<BIZ_MES_WO_BATCH>().ByAuth(input.AuthOption).Where(q => q.ORDER_NO == input.OrderNo && q.ACT_LINE == CurLine.LINE_CODE).FirstAsync();
|
if (batch.IsNullOrEmpty())
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = new($"工单[{input.OrderNo}]没有下发到产线状态[{CurLine.LINE_CODE}]");
|
action.LocaleMsg = new("MES.Transaction.Position.SelectOrder.LineException", input.OrderNo, CurLine.LINE_CODE);
|
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);
|
action.Data = new { WorkOrder = CurBatch.WO, Bacth = CurBatch.Batch };
|
}
|
catch (Exception ex)
|
{
|
action.CatchExceptionWithLog(ex, $"采集工序:选择工单异常");
|
}
|
return action;
|
}
|
|
/// <summary>
|
/// 获取当前时间所属的班制班次时段
|
/// </summary>
|
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;
|
CurWipSNHis = null;
|
CurStep = null;
|
}
|
|
/// <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.Types.Action, this)
|
{
|
Sequence = parent.Sequence + 1,
|
Node = parent.Node,
|
NodeAct = act,
|
Setting = 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()
|
{
|
//保存工步的数据库提交操作到提交操作列表
|
foreach (var step in Steps.OrderBy(q => q.Sequence))
|
{
|
DBCommitList.Add(step.DBSubmitAction);
|
}
|
//如果不需要临时存储数据库提交操作,则把提交操作列表提交到数据库
|
if (!NeedTemporaryStoreDBCommitAction)
|
{
|
//恢复临时存储标记为false
|
NeedTemporaryStoreDBCommitAction = false;
|
|
var dbTran = GetCommitDB().UseTran(() =>
|
{
|
//在同一个事务中保存所有工步的数据
|
foreach (var action in DBCommitList)
|
{
|
action.Invoke();
|
}
|
});
|
if (dbTran.IsSuccess)
|
{
|
//保存成功则清空提交操作列表
|
DBCommitList.Clear();
|
}
|
else
|
{
|
//抛出异常
|
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)
|
{
|
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);
|
break;
|
case BAS_LABEL_VAR.VAR_TYPEs.DateVariable:
|
item.Value = DateTime.Now.ToString(item.VAR_VALUE);
|
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)
|
{
|
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 CurWipSN.SN;
|
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.WebApiWebApi:
|
break;
|
case BAS_LABEL_PV.VAR_TYPEs.StoredProcedure:
|
break;
|
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
|
|
public override bool Close(bool needSaveHistoryLog = false)
|
{
|
//needSaveHistoryLog = true;
|
//保存操作日志
|
|
this.IsFinished = true;
|
return IsFinished ? base.Close(needSaveHistoryLog) : IsFinished;
|
}
|
}//endClass
|
}
|