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;
|
using System.Globalization;
|
using Tiger.Business.MES.WorkAction;
|
using System.Collections;
|
using Tiger.IBusiness.MES.WorkAction;
|
using Dm.parser;
|
|
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;
|
_MainDB = Biz.Db;
|
|
CurPosition = MainDB.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 = MainDB.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 = MainDB.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 = MainDB.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 = MainDB.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; }
|
protected DateTime BeginAt;
|
protected DateTime EndAt;
|
public TimeSpan ElapsedTime => EndAt - BeginAt;
|
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 List<MES_WIP_DATA> CurWipSNs { get; set; } = new();
|
public List<MES_WIP_DATA> LastWipSNs { 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 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 _MainDB;
|
public DbClient MainDB => _MainDB;
|
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();
|
private Dictionary<string, OperInfo> OperInfoDic = 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, string batchNo = "")
|
{
|
var action = new ApiAction();
|
try
|
{
|
if (!WoContext.ExistsBatch(input.OrderNo, CurLine.LINE_CODE, batchNo, true))
|
{
|
var wo = await MainDB.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($"工单[{0}]状态[{1}]不能生产");
|
action.LocaleMsg = new("MES.Transaction.Position.SelectOrder.StatusException", input.OrderNo, wo.STATUS.GetEnumDesc<BIZ_MES_WO.STATUSs>());
|
return action;
|
}
|
var batch = await MainDB.Queryable<BIZ_MES_WO_BATCH>().ByAuth(input.AuthOption)
|
.Where(q => q.ORDER_NO == input.OrderNo && q.ACT_LINE == CurLine.LINE_CODE)
|
.WhereIF(!batchNo.IsNullOrEmpty(), q => q.BATCH_NO == batchNo)
|
.OrderBy(q => q.STATUS).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;
|
}
|
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($"工单[{0}]批次[1]状态[{2}]不能生产");
|
action.LocaleMsg = new("MES.Transaction.Position.SelectOrder.BatchStatusException", input.OrderNo, batch.BATCH_NO, batch.STATUS.GetEnumDesc<BIZ_MES_WO_BATCH.STATUSs>());
|
return action;
|
}
|
var wb = WoContext.Add(input.OrderNo, CurLine.LINE_CODE);
|
batchNo = wb.Batch.BATCH_NO;
|
}
|
CurBatch = WoContext.GetBatch(input.OrderNo, CurLine.LINE_CODE, batchNo);
|
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)
|
{
|
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();
|
CurWipSNs.Clear();
|
CurWipSNHiss.Clear();
|
CurStep = null;
|
CurDefects.Clear();
|
}
|
|
//获取当前的工序信息
|
protected OperInfo CurOperInfo(string locale)
|
{
|
OperInfo info = new();
|
if (CurBatch?.Batch?.BATCH_NO != null)
|
{
|
info = OperInfoDic[CurBatch.Batch.BATCH_NO];
|
info.StepsInfo = Steps.Select(q => q.GetInfo(locale)).ToList();
|
}
|
return info;
|
}
|
|
/// <summary>
|
/// 设置当前条码的工序信息
|
/// </summary>
|
public OperInfo SetOperNodeInfo(OperInfo info)
|
{
|
if (CurWipSNs.Any())
|
{
|
info.CurNode = CurWipSNs.First().NODE_NAME;
|
var nextNodes = CurBatch.GetNextNodes(CurWipSNs.First());
|
info.NextNode = string.Join(",", nextNodes.Select(q => q.NODE_NAME));
|
if (nextNodes.Count == 1 && nextNodes.Single().OPER_CODE == "EndNode")
|
{
|
info.IsReachedEndNode = true;
|
}
|
}
|
else
|
{
|
info.CurNode = " — ";
|
info.NextNode = " — ";
|
}
|
return info;
|
}
|
|
/// <summary>
|
/// 设置当前条码的工序信息
|
/// </summary>
|
public ApiAction<SubmitOutput> SetOutPutMqttMsg(ApiAction<SubmitOutput> action, string locale = null)
|
{
|
MQTT.Message msg = new()
|
{
|
IsSuccessed = action.IsSuccessed,
|
Content = Biz.T(action.LocaleMsg, locale),
|
};
|
switch (action.Status)
|
{
|
case ApiAction.StatusCodes.Success:
|
msg.Voice = MQTT.Voice.Success;
|
msg.Color = "#FF1E90FF";
|
break;
|
case ApiAction.StatusCodes.Warning:
|
msg.Voice = MQTT.Voice.Warning;
|
msg.Color = "#FFB8860B";
|
break;
|
case ApiAction.StatusCodes.Error:
|
case ApiAction.StatusCodes.Failed:
|
msg.Voice = MQTT.Voice.Fail;
|
msg.Color = "#FFFF0000";
|
break;
|
case ApiAction.StatusCodes.Exception:
|
msg.Voice = MQTT.Voice.Fail;
|
msg.Color = "#FF8B0000";
|
break;
|
case ApiAction.StatusCodes.Normal:
|
case ApiAction.StatusCodes.NeedConfrim:
|
case ApiAction.StatusCodes.Confrimed:
|
default:
|
msg.Voice = MQTT.Voice.Silent;
|
msg.Color = "#FF000000";
|
break;
|
}
|
if (action.Data.IsFinished && action.IsSuccessed)
|
{
|
msg.Voice = MQTT.Voice.Pass;
|
msg.Color = "#FF228B22";
|
}
|
else if (!action.IsSuccessed)
|
{
|
msg.Voice = MQTT.Voice.Fail;
|
msg.Color = "#FFFF0000";
|
}
|
else
|
{
|
msg.Voice = MQTT.Voice.Silent;
|
msg.Color = "#FF000000";
|
}
|
action.Data.MqttMsg = msg;
|
return action;
|
}
|
|
/// <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);
|
//如果当前执行工步尝试执行后就已经完成,不需要后续交互,且,则继续开始下一工步
|
if (CurStep.IsFinished && NextSteps.Any())
|
{
|
result = BeginNextActionStep(input);
|
}
|
else
|
{
|
//返回结果到客户端
|
result = canBegin;
|
result.Data.SetValue(CurBatch, CurStep, CurStep?.ID, IsFinishAllSteps);
|
}
|
return result;
|
}
|
//工序被重置
|
else if (Steps.IsNullOrEmpty())
|
{
|
return canBegin;
|
}
|
}
|
|
//没有可以直接开始的后续工步,根据后续工步返回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>
|
/// <param name="appendToSave">提交完工步数据后,提交附加的保存内容</param>
|
public void SaveStepsCommitActionToDB(Action appendToSave = null)
|
{
|
//保存工步的数据库提交操作到提交操作列表
|
if (!Steps.IsNullOrEmpty())
|
{
|
var commitList = new List<Action>();
|
foreach (var step in Steps.OrderBy(q => q.Sequence))
|
{
|
commitList.Add(step.DBSubmitAction);
|
}
|
//记录工序耗时
|
var ids = CurWipSNHiss.Select(q => q.ID).ToList().Clone();
|
commitList.Add(() =>
|
{
|
EndAt = DateTime.Now;
|
GetCommitDB().Updateable<MES_WIP_HIS>().SetColumns(q => q.ELAPSED_TIME == ElapsedTime.TotalMilliseconds.ToInt64()).Where(q => ids.Contains(q.ID)).ExecuteCommand();
|
});
|
//增加附加的保存内容
|
if (!appendToSave.IsNullOrEmpty())
|
{
|
commitList.Add(appendToSave);
|
}
|
DBCommitList.Add(CurSN, commitList);
|
}
|
else
|
{
|
//增加附加的保存内容
|
if (!appendToSave.IsNullOrEmpty())
|
{
|
if (!DBCommitList.ContainsKey("AppendSaveAction"))
|
{
|
DBCommitList.Add("AppendSaveAction", new List<Action>());
|
}
|
var commitList = DBCommitList["AppendSaveAction"];
|
commitList.Add(appendToSave);
|
}
|
}
|
|
//如果不需要临时存储数据库提交操作,则把提交操作列表提交到数据库
|
if (!NeedTemporaryStoreDBCommitAction)
|
{
|
//恢复临时存储标记为false
|
NeedTemporaryStoreDBCommitAction = false;
|
|
var dbTran = GetCommitDB().UseTran(() =>
|
{
|
//在同一个事务中保存所有工步的数据
|
foreach (var wipSn in DBCommitList.Keys.Where(q => q != "AppendSaveAction"))
|
{
|
foreach (var action in DBCommitList[wipSn])
|
{
|
action.Invoke();
|
}
|
}
|
//附加的保存内容
|
if (DBCommitList.ContainsKey("AppendSaveAction"))
|
{
|
foreach (var action in DBCommitList["AppendSaveAction"])
|
{
|
action.Invoke();
|
}
|
}
|
});
|
if (dbTran.IsSuccess)
|
{
|
//保存成功则清空提交操作列表
|
DBCommitList.Clear();
|
}
|
else
|
{
|
//抛出异常
|
throw dbTran.ErrorException;
|
}
|
}
|
//记录最后成功过站的条码
|
LastWipSNs.Clear();
|
LastWipSNs.AddRange(CurWipSNs);
|
}
|
|
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;
|
}
|
}
|
|
#region 打印专用方法
|
/// <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)
|
{
|
return SetLabelVariables(labelPVs, label, action, null);
|
}
|
|
public BAS_LABEL_TEMP SetLabelVariables(List<BAS_LABEL_PV> labelPVs, BAS_LABEL_TEMP label, IWorkAction action, WipPkg? CurPkg)
|
{
|
foreach (var item in label.Variables.OrderBy(q => q.VAR_TYPE == BAS_LABEL_VAR.VAR_TYPEs.BarcodeGenerate.GetValue() ? 0 : 1))
|
{
|
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, label.Variables, action, CurPkg);
|
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, List<BAS_LABEL_VAR> lvars, IWorkAction curAction, WipPkg? CurPkg)
|
{
|
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 "GetItemCode":
|
return GetLabelVarWo(lv, WorkBatch.Product.ITEM_CODE);
|
case "GetBAS_ITEM":
|
return GetLabelVarWo(lv, WorkBatch.Product.CUST_PROD_CODE);
|
case "GetItemName":
|
return GetLabelVarWo(lv, WorkBatch.Product.ITEM_NAME);
|
case "GetCustomer":
|
return GetLabelVarWo(lv, WorkBatch.Batch.Customer.ToJson());
|
case "GetSalesOrder":
|
return GetLabelVarWo(lv, WorkBatch.WO.SALES_ORDER);
|
case "GetPackNumber":
|
return GetLabelVarWo(lv, WorkBatch.WO.ORDER_NO);
|
case "GetBoxQR":
|
return GetCardOrBoxQR(GetBoxCode(), curAction, CurPkg);
|
case "GetCardQR":
|
return GetCardOrBoxQR("", curAction, CurPkg);
|
case "GetCardSN":
|
List<string> minPkgList = new List<string>();
|
if (!CurPkg.IsNullOrEmpty())
|
{
|
minPkgList = CurPkg.Item.GetWipSnList().Select(q => q.Value).ToList();
|
}
|
else
|
{
|
var _pkaction = curAction is PackingAction ? curAction as PackingAction : null;
|
if (!_pkaction.IsNullOrEmpty())
|
{
|
minPkgList = GetMinPkgList(_pkaction);
|
}
|
}
|
return string.Join("\r\n", minPkgList);
|
case "GetDescription":
|
return GetLabelVarWo(lv, WorkBatch.Product.ITEM_DESC);
|
case "GetFengZ":
|
return GetLabelVarWo(lv, WorkBatch.Product.ExtInfo.Packaging);
|
case "GetPAndO":
|
return GetLabelVarWo(lv, WorkBatch.WO.SALES_CONTRACT);
|
case "GetLTD":
|
return GetLabelVarWo(lv, WorkBatch.WO.Customer?.CUST_NAME_CN);
|
case "GetHWDate":
|
List<string> list = new List<string>();
|
if (!CurPkg.IsNullOrEmpty())
|
{
|
list = CurPkg.Item.GetWipSnList().Select(q => q.Value).ToList();
|
}
|
else
|
{
|
var _action = curAction is PackingAction ? curAction as PackingAction : null;
|
if (_action != null)
|
{
|
list = _action.CurPkg.Item.GetWipSnList().Select(q => q.Value).ToList();
|
}
|
}
|
return GetHuaWeiWeek(WorkBatch.Batch.ORDER_NO, list);
|
case "GetQty":
|
List<string> qtylist = new List<string>();
|
if (!CurPkg.IsNullOrEmpty())
|
{
|
qtylist = CurPkg.Item.GetWipSnList().Select(q => q.Value).ToList();
|
}
|
else
|
{
|
if (curAction is PackingAction)
|
{
|
qtylist = (curAction as PackingAction).CurPkg.Item.GetWipSnList().Select(q => q.Value).ToList();
|
}
|
if (curAction is PrintInStoreLabel)
|
{
|
qtylist = (curAction as PrintInStoreLabel).CurPkg.Item.GetWipSnList().Select(q => q.Value).ToList();
|
}
|
}
|
return $"{qtylist.Count}";
|
case "GetModel":
|
return GetLabelVarWo(lv, WorkBatch.Product.ExtInfo?.Model);
|
case "GetInput":
|
return GetLabelVarWo(lv, WorkBatch.Product.ExtInfo?.Input);
|
case "GetOutput":
|
return GetLabelVarWo(lv, WorkBatch.Product.ExtInfo?.OutPut);
|
case "GetPower":
|
return GetLabelVarWo(lv, WorkBatch.Product.ExtInfo?.Power);
|
case "GetAccuracy":
|
return GetLabelVarWo(lv, WorkBatch.Product.ExtInfo?.Accuracy);
|
case "GetVarByWo":
|
return GetLabelVarWo(lv, "");
|
case "GetLOTNO":
|
string _lotnos = "";
|
var _orderAction = curAction is PackingAction ? curAction as PackingAction : null;
|
if (!CurPkg.IsNullOrEmpty())
|
{
|
qtylist = CurPkg.Item.GetWipSnList().Select(q => q.Value).ToList();
|
}
|
else
|
{
|
if (_orderAction != null)
|
{
|
var snList = _orderAction.CurPkg.Item.GetWipSnList().Select(q => q.Value).ToList();
|
//if (!snList.Any())
|
{
|
snList.Add(_orderAction.CurPkg.CustSN?.FLOW_SN);
|
}
|
_lotnos = string.Join(",", MainDB.Queryable<MES_CUST_SN>()
|
.Where((q) => snList.Contains(q.FLOW_SN))
|
.Select((q) => q.WORK_ORDER).Distinct().ToList());
|
return _lotnos;
|
}
|
}
|
return "";
|
case "GetNOTES":
|
return GetLabelVarWo(lv, WorkBatch.Product.ExtInfo?.Remark);
|
case "GetXH":
|
return "";
|
case "GetCardNo":
|
return $"{Context.GetOrDefault("CurWaitShipmentCarton").ToInt32() + 1}";
|
case "GetCardTotal":
|
var total = "";
|
var _curaction = curAction is PackingAction ? curAction as PackingAction : null;
|
if (!_curaction.IsNullOrEmpty()) {
|
total = $"{Math.Ceiling(Context.GetOrDefault("CurShipQty").ToDecimal()/_curaction.PkgRule.GetTotalQty().ToDecimal())}";
|
}
|
return total;
|
case "GetModelSpec": //Model+空格+SPEC,
|
return GetLabelVarWo(lv, $"{WorkBatch.Product.ExtInfo?.Model} {WorkBatch.Product.SPEC}");
|
case "GetDATE":
|
return GetLabelVarWo(lv, $"{DateTime.Now.ToString("yyyy/MM/dd")}");
|
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 Cache.CodeRule[lv.BARCODE_RULE ?? ""]?.Generate("B", $"{WorkBatch.Batch.BATCH_NO}-{WorkBatch.Batch.PLAN_QTY}-").Data.ToString() ?? "";
|
case "GetHW21SNGenerate":
|
return Cache.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 value)
|
{
|
string result = "";
|
var labelVarwos = MainDB.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 = value.IsNullOrEmpty() ? (labelVarwos.Count > 0 ? labelVarwos.First(q => q.WORK_ORDER.IsNullOrEmpty()).DEFAULT_VALUE : "") : value;
|
}
|
return result;
|
}
|
|
/// <summary>
|
/// 获取最小包装条码列表
|
/// </summary>
|
/// <param name="action"></param>
|
/// <returns></returns>
|
private List<string> GetMinPkgList(PackingAction action)
|
{
|
List<string> list = new List<string>();
|
if (action.CurPkg.IsNullOrEmpty())
|
{
|
list.Add(CurSN);
|
}
|
else
|
{
|
list = action.CurPkg.Item.GetMinPackageList().Select(q => q.SN).ToList();
|
}
|
return list;
|
}
|
|
/// <summary>
|
/// 获取白盒条码
|
/// </summary>
|
/// <returns></returns>
|
private string GetBoxCode()
|
{
|
return Cache.CodeRule["WhiteBoxNo"]?.Generate("W", $"{WorkBatch.Batch.BATCH_NO}-{WorkBatch.Batch.PLAN_QTY}-").Data.ToString() ?? "";
|
}
|
|
/// <summary>
|
/// 获取箱条码
|
/// </summary>
|
/// <returns></returns>
|
private string GetCardCode()
|
{
|
return Cache.CodeRule["CartonNo"]?.Generate("B", $"{WorkBatch.Batch.BATCH_NO}-{WorkBatch.Batch.PLAN_QTY}-").Data.ToString() ?? "";
|
}
|
|
/// <summary>
|
/// 获取白盒或者箱二维码
|
/// </summary>
|
/// <param name="code"></param>
|
/// <param name="lvars"></param>
|
/// <param name="action"></param>
|
/// <returns></returns>
|
private string GetCardOrBoxQR(string code, IWorkAction action, WipPkg? CurPkg)
|
{
|
List<string> minPkgList = new List<string>();
|
var itemCode = "";
|
double weight = 0;
|
var unit = "";
|
if (!CurPkg.IsNullOrEmpty())
|
{
|
minPkgList = CurPkg.Item.GetWipSnList().Select(q => q.Value).ToList();
|
itemCode = CurPkg.CustSN?.ITEM_CODE;
|
weight = CurPkg.WeightInfo.Weight;
|
unit = CurPkg.WeightInfo.Unit?.ToUpper() ?? "kg";
|
}
|
else
|
{
|
if (action is PackingAction)
|
{
|
var pkg = action as PackingAction;
|
//minPkgList = pkg.CurPkg.Item.GetMinPackageList().Select(q => q.SN).ToList();
|
minPkgList = pkg.CurPkg.Item.GetWipSnList().Select(q => q.Value).ToList();
|
itemCode = pkg.CurPkg.CustSN?.ITEM_CODE;
|
weight = pkg.CurPkg.WeightInfo.Weight;
|
unit = pkg.CurPkg.WeightInfo.Unit?.ToUpper() ?? "kg";
|
}
|
if (action is PrintInStoreLabel)
|
{
|
var print = action as PrintInStoreLabel;
|
//minPkgList = print.CurPkg.Item.GetMinPackageList().Select(q => q.SN).ToList();
|
minPkgList = print.CurPkg.Item.GetWipSnList().Select(q => q.Value).ToList();
|
itemCode = print.CurPkg.CustSN?.ITEM_CODE;
|
weight = print.CurPkg.WeightInfo.Weight;
|
unit = print.CurPkg.WeightInfo.Unit?.ToUpper() ?? "kg";
|
}
|
}
|
var wo = WorkBatch.Batch.ORDER_NO;
|
var snList = string.Join("\r\n", minPkgList);
|
if (snList.IsNullOrEmpty()) { snList = $"{CurSN}"; }
|
var model = WorkBatch.Product.ExtInfo?.Model;
|
var Input = WorkBatch.Product.ExtInfo?.Input;
|
var OutPut = WorkBatch.Product.ExtInfo?.OutPut;
|
var Power = WorkBatch.Product.ExtInfo?.Power;
|
var Accuracy = WorkBatch.Product.ExtInfo?.Accuracy;
|
var Display = WorkBatch.Product.ExtInfo?.Display;
|
var Remark = WorkBatch.Product.ExtInfo?.Remark;
|
|
return $"{code}|{itemCode}|{wo}|\r\n{snList}|{weight}{unit}|型号:{model},输入:{Input},显示:{Display},输出:{OutPut},电源:{Power},精度:{Accuracy},备注:{Remark}";
|
}
|
|
/// <summary>
|
/// 根据包装工单和条码获取华为要求的生产周期
|
/// </summary>
|
/// <param name="snList"></param>
|
/// <returns></returns>
|
private string GetHuaWeiWeek(string pkgOrder, List<string> snList)
|
{
|
Dictionary<string, string> dic = new();
|
var snOrder = MainDB.Queryable<MES_CUST_SN, BIZ_MES_WO>((q, w) => new JoinQueryInfos(JoinType.Left, q.WORK_ORDER == w.ORDER_NO))
|
.Where((q, w) => q.PKG_ORDER == pkgOrder && (snList.Contains(q.FLOW_SN) || snList.Contains(q.CUST_SN)))
|
.Select((q, w) => new { q.PKG_ORDER, q.WORK_ORDER, w.ACT_START_TIME, w.PLAN_START_TIME, q.FLOW_SN, q.CUST_SN }).ToList();
|
foreach (var sn in snList)
|
{
|
var item = snOrder.FirstOrDefault(q => q.FLOW_SN == sn || q.CUST_SN == sn);
|
if (!item.IsNullOrEmpty())
|
{
|
var date = item.ACT_START_TIME < new DateTime(2000, 1, 1) ? item.PLAN_START_TIME : item.ACT_START_TIME;
|
var firstDay = new DateTime(date.Year, 1, 1);
|
int daysOffset = firstDay.DayOfWeek.GetValue() > 3 ? (firstDay.DayOfWeek.GetValue() - 7) : 0;
|
int year = date.Year + ((date - firstDay).TotalDays + daysOffset < 0 ? -1 : 0);
|
int week = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Sunday);
|
dic.Add(sn, $"{year.ToString("0000").Substring(2, 2)}{week:00}");
|
}
|
else
|
{
|
dic.Add(sn, "");
|
}
|
}
|
var weeks = dic.Select(q => q.Value).Where(q => !q.IsNullOrEmpty()).Distinct().OrderBy(q => q).ToList();
|
|
switch (weeks.Count)
|
{
|
case 0:
|
return "";
|
case 1:
|
return weeks.First();
|
case 2:
|
return string.Join("、", weeks.Select(q => q + $"({dic.Count(d => d.Value == q)}PCS)"));
|
default:
|
return string.Join("、", weeks.Take(2)) + " (+)";
|
}
|
}
|
#endregion
|
#region 条码规则方法
|
/// <summary>
|
/// 获取根据条码规则生成条码
|
/// </summary>
|
/// <param name="ruleCode"></param>
|
/// <param name="curAction"></param>
|
/// <returns></returns>
|
public string GenerateSN(string ruleCode, IWorkAction curAction)
|
{
|
var rule = Cache.CodeRule[ruleCode ?? ""];
|
if (!rule.IsNullOrEmpty())
|
{
|
switch (rule.RULE_CODE)
|
{
|
case "WhiteBoxNo":
|
return rule.Generate("W", $"{WorkBatch.Batch.BATCH_NO}-{WorkBatch.Batch.PLAN_QTY}-").Data.ToString() ?? "";
|
case "CartonNo":
|
return rule.Generate("B", $"{WorkBatch.Batch.BATCH_NO}-{WorkBatch.Batch.PLAN_QTY}-").Data.ToString() ?? "";
|
default:
|
return rule.Generate().Data.ToString() ?? "";
|
}
|
}
|
return "";
|
}
|
#endregion
|
#region 包装专用方法
|
/// <summary>
|
/// 根据传入的包装对象返回所有的包装实体列表
|
/// </summary>
|
/// <param name="parent"></param>
|
/// <returns></returns>
|
public List<MES_WIP_PKG> GetPackageList(WipPkgItem parent)
|
{
|
List<MES_WIP_PKG> list = new();
|
if (!parent.Package.IsNullOrEmpty())
|
{
|
list.Add(parent.Package);
|
}
|
foreach (var item in parent.Items)
|
{
|
item.Package.PARENT_SN = parent.Package?.SN;
|
list.AddRange(GetPackageList(item));
|
}
|
return list;
|
}
|
#endregion
|
#endregion Functions
|
|
public override bool Close(bool needSaveHistoryLog = false)
|
{
|
//needSaveHistoryLog = true;
|
//保存操作日志
|
|
this.IsFinished = true;
|
return IsFinished ? base.Close(needSaveHistoryLog) : IsFinished;
|
}
|
|
}//endClass
|
}
|