服务端的TigerApi 框架,基于.NET6 2024 版本
Rodney Chen
2024-09-24 090e455687745bc64c8e32baf480d9310f8e2a57
MES 一处Tiger.IBusiness
已删除2个文件
已修改6个文件
已重命名29个文件
已添加3个文件
1341 ■■■■ 文件已修改
Tiger.Api.sln 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.Api/Tiger.Api.csproj 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.Business.MES/Common/WoContext.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.Business.MES/Common/WorkBatch.cs 508 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.Business.MES/Common/WorkStep.cs 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.Business.MES/Tiger.Business.MES.csproj 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.Business.MES/Transaction/CollectNode.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.Business.MES/Transaction/PackingNode.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.Business.MES/Transaction/TestNode.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.Business/MES/Biz.WorkBatch.cs 510 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.Business/MES/Biz.WorkStep.cs 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/BIZ/IBasRule.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/BIZ/IMES_WO.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/BIZ/IMES_WORKSHOP.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/Common/ICodeVerification.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/Common/IDoUnPack.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/Extensions/ModelExtension.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/IMESContext.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/IWorkStep.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/LabelTemplate/ILabelTemplate.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/SMT/ISmtTool.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/Tiger.IBusiness.MES.csproj 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/Transaction/ICollectNode.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/Transaction/ILoadingMaterial.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/Transaction/IMESTransaction.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/Transaction/IPackingNode.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/Transaction/IPosition.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/Transaction/ITestNode.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/WorkAction/IAssembly.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/WorkAction/IIPQC.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/WorkAction/IPackageInputCheck.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/WorkAction/IPackingAction.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/WorkAction/IPrintLabel.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/WorkAction/IThreeInOne.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/WorkAction/IVerifyCustomSN.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/WorkAction/IWeighing.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/WorkAction/IWipExtInfo.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/WorkAction/IWorkAction.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/WorkAction/IYadaAssembly.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/iERP/IU9C_MES.cs 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.Api.sln
@@ -40,6 +40,8 @@
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tiger.Controllers.MES", "Tiger.Controllers.MES\Tiger.Controllers.MES.csproj", "{016ABAEC-75DE-4553-B828-AA45C237B485}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tiger.IBusiness.MES", "Tiger.IBusiness.MES\Tiger.IBusiness.MES.csproj", "{07B13ED4-77D9-4B9C-9375-91B7ED371422}"
EndProject
Global
    GlobalSection(SolutionConfigurationPlatforms) = preSolution
        Debug|Any CPU = Debug|Any CPU
@@ -136,6 +138,14 @@
        {016ABAEC-75DE-4553-B828-AA45C237B485}.Release|Any CPU.Build.0 = Release|Any CPU
        {016ABAEC-75DE-4553-B828-AA45C237B485}.Release|x86.ActiveCfg = Release|Any CPU
        {016ABAEC-75DE-4553-B828-AA45C237B485}.Release|x86.Build.0 = Release|Any CPU
        {07B13ED4-77D9-4B9C-9375-91B7ED371422}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {07B13ED4-77D9-4B9C-9375-91B7ED371422}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {07B13ED4-77D9-4B9C-9375-91B7ED371422}.Debug|x86.ActiveCfg = Debug|Any CPU
        {07B13ED4-77D9-4B9C-9375-91B7ED371422}.Debug|x86.Build.0 = Debug|Any CPU
        {07B13ED4-77D9-4B9C-9375-91B7ED371422}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {07B13ED4-77D9-4B9C-9375-91B7ED371422}.Release|Any CPU.Build.0 = Release|Any CPU
        {07B13ED4-77D9-4B9C-9375-91B7ED371422}.Release|x86.ActiveCfg = Release|Any CPU
        {07B13ED4-77D9-4B9C-9375-91B7ED371422}.Release|x86.Build.0 = Release|Any CPU
    EndGlobalSection
    GlobalSection(SolutionProperties) = preSolution
        HideSolutionNode = FALSE
@@ -153,6 +163,7 @@
        {457563B5-E42C-448A-A924-FBEE9B585A81} = {FF44BF62-08C5-4B52-B24F-54CD79E96848}
        {0B65AB5A-2D97-4292-8210-8E0D0BD227BB} = {457563B5-E42C-448A-A924-FBEE9B585A81}
        {016ABAEC-75DE-4553-B828-AA45C237B485} = {457563B5-E42C-448A-A924-FBEE9B585A81}
        {07B13ED4-77D9-4B9C-9375-91B7ED371422} = {A994D516-643A-4543-B781-540E81F1DE55}
    EndGlobalSection
    GlobalSection(ExtensibilityGlobals) = postSolution
        SolutionGuid = {797A1D83-9F3C-4AEC-8A83-E3468102DBD1}
Tiger.Api/Tiger.Api.csproj
@@ -90,6 +90,7 @@
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\Tiger.IBusiness.MES\Tiger.IBusiness.MES.csproj" />
    <ProjectReference Include="..\Tiger.IBusiness.WMS\Tiger.IBusiness.WMS.csproj" />
    <ProjectReference Include="..\Tiger.IBusiness\Tiger.IBusiness.csproj" />
  </ItemGroup>
Tiger.Business.MES/Common/WoContext.cs
ÎļþÃû´Ó Tiger.Business/MES/WoContext.cs ÐÞ¸Ä
@@ -12,7 +12,7 @@
using Tiger.Model.Entitys.MES.Position;
using static Tiger.Business.Biz;
namespace Tiger.Business.MES
namespace Tiger.Business
{
    /// <summary>
    /// å·¥å• ä¸Šä¸‹æ–‡
Tiger.Business.MES/Common/WorkBatch.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,508 @@
using Tiger.Model;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Rhea.Common;
using System.Linq;
using Tiger.IBusiness;
using Tiger.Model.Entitys.MES.Position;
using Tiger.Business.MES;
namespace Tiger.Business
{
    /// <summary>
    /// ç”Ÿäº§ä¸­çš„工单批次
    /// </summary>
    public partial class WorkBatch : IWorkBatch, IDisposable
    {
        public WorkBatch(string order)
        {
            OrderNo = order;
        }
        #region Propertys & Variables
        public string OrderNo { get; set; }
        public string LineCode { get; set; }
        public BIZ_MES_WO WO { get; set; }
        public BAS_ITEM Product { get; set; }
        public BIZ_MES_WO_BATCH Batch { get; set; }
        public BAS_CUSTOMER Customer { get; set; }
        public List<BIZ_MES_WO_SN> WoSNs { get; set; }
        public List<MES_WO_EDGE> Edges { get; set; }
        public List<MES_WO_NODE> Nodes { get; set; }
        public List<MES_WO_OPER> NodeSets { get; set; }
        public List<MES_WO_NODE_ACT> NodeActs { get; set; }
        public List<MES_WO_ACTION> ActionSets { get; set; }
        public List<MES_WO_NODE_POST> NodePosts { get; set; }
        public List<MES_WO_NODE_DFTG> NodeDftgs { get; set; }
        public List<BAS_DEFECT_GRP> DefectGroups { get; set; }
        public List<BAS_DEFECT> Defects => DefectGroups.SelectMany(q => q.Defects).ToList();
        /// <summary>
        /// äº‹åС锁
        /// </summary>
        public object TransLock { get; }
        #endregion
        #region Functions
        /// <summary>
        /// åˆå§‹åŒ–工单资料
        /// </summary>
        /// <returns></returns>
        public WorkBatch Init(string lineCode)
        {
            LineCode = lineCode;
            GetBatchInfo();
            return this;
        }
        public void Update()
        {
            if (!WoContext.WoBatchDic.ContainsKey(Batch.BATCH_NO))
            {
                GetBatchInfo();
                WoContext.WoBatchDic.Add(Batch.BATCH_NO, this);
            }
        }
        private void GetBatchInfo()
        {
            WO = Biz.Db.Queryable<BIZ_MES_WO>().Where(q => q.ORDER_NO == OrderNo).IncludesAllFirstLayer().First();
            Product = Biz.Db.Queryable<BAS_ITEM>().Where(q => q.ITEM_CODE == WO.ITEM_CODE && q.AUTH_ORG == WO.AUTH_ORG).IncludesAllFirstLayer().First();
            Batch = Biz.Db.Queryable<BIZ_MES_WO_BATCH>().Where(q => q.ORDER_NO == OrderNo && q.ACT_LINE == LineCode &&
                                (q.STATUS == BIZ_MES_WO_BATCH.STATUSs.Release.GetValue() || q.STATUS == BIZ_MES_WO_BATCH.STATUSs.Working.GetValue())).First();
            Customer = Biz.Db.Queryable<BAS_CUSTOMER>().Where(q => q.CUST_CODE == WO.CUST_CODE).First();
            WoSNs = Biz.Db.Queryable<BIZ_MES_WO_SN>().Where(q => q.WORK_ORDER == OrderNo).ToList();
            Edges = Biz.Db.Queryable<MES_WO_EDGE>().Where(q => q.WORK_ORDER == OrderNo).ToList();
            Nodes = Biz.Db.Queryable<MES_WO_NODE>().Where(q => q.WORK_ORDER == OrderNo).IncludesAllFirstLayer().ToList();
            NodeSets = Biz.Db.Queryable<MES_WO_OPER>().Where(q => q.WORK_ORDER == OrderNo).ToList();
            NodeActs = Biz.Db.Queryable<MES_WO_NODE_ACT>().Where(q => q.WORK_ORDER == OrderNo).IncludesAllFirstLayer().ToList();
            ActionSets = Biz.Db.Queryable<MES_WO_ACTION>().Where(q => q.WORK_ORDER == OrderNo).ToList();
            NodePosts = Biz.Db.Queryable<MES_WO_NODE_POST>().Where(q => q.WORK_ORDER == OrderNo).ToList();
            NodeDftgs = Biz.Db.Queryable<MES_WO_NODE_DFTG>().Where(q => q.WORK_ORDER == OrderNo).ToList();
            DefectGroups = Biz.Db.Queryable<BAS_DEFECT_GRP>().IncludesAllFirstLayer().ToList();
            //工序节点排序
            var first = Nodes.First(q => q.IS_FIRST_NODE == "Y");
            first.Sequence = 1;
            NodeSorting(first);
        }
        /// <summary>
        /// æ·»åŠ èŠ‚ç‚¹çš„ä¸‹ä¸€ä¸ªè¡Œä¸ºåˆ°å·¥æ­¥åˆ—è¡¨
        /// </summary>
        /// <param name="parent"></param>
        private void NodeSorting(MES_WO_NODE parent)
        {
            var edges = Edges.Where(q => q.SRC_NODE == parent.ID && Nodes.Any(a => a.ID == q.TGT_NODE)).ToList();
            foreach (var edge in edges)
            {
                var next = Nodes.First(q => q.ID == edge.TGT_NODE);
                //排除维修工序
                if (next.Operation.OPER_TYPE != MES_OPERATION.OPER_TYPEs.Repair.GetValue())
                {
                    next.Sequence = next.Sequence > parent.Sequence ? next.Sequence : (parent.Sequence + 1);
                    //如果父节点启用且不可跳站,则添加为下一个工序的前置工序
                    var setting = NodeSets.FirstOrDefault(q => q.NODE_ID == parent.ID);
                    if (!setting.IsNullOrEmpty() && setting.IS_ACTIVE == "Y" && setting.CAN_SKIP != "Y")
                    {
                        next.PrepNodeIDs.Add(parent.ID);
                    }
                    //继承父结点的前置工序
                    next.PrepNodeIDs.AddRange(parent.PrepNodeIDs);
                    next.PrepNodeIDs = next.PrepNodeIDs.Distinct().ToList();
                    NodeSorting(next);
                }
            }
        }
        /// <summary>
        /// æ£€æŸ¥å·¥å•在当前工序节点是否可以生产
        /// </summary>
        /// <param name="curNode"></param>
        /// <returns></returns>
        public ApiAction<SubmitOutput> CheckCanProduce(MES_WO_NODE curNode)
        {
            var action = new ApiAction<SubmitOutput>(new SubmitOutput(), true);
            WO = Biz.Db.Queryable<BIZ_MES_WO>().Where(q => q.ORDER_NO == OrderNo).IncludesAllFirstLayer().First();
            Batch = Biz.Db.Queryable<BIZ_MES_WO_BATCH>().Where(q => q.ORDER_NO == OrderNo && q.ACT_LINE == LineCode).First();
            //工单批次状态不是已下发或者生产中,则不允许生产
            if (Batch.STATUS != BIZ_MES_WO_BATCH.STATUSs.Release.GetValue() && Batch.STATUS != BIZ_MES_WO_BATCH.STATUSs.Working.GetValue())
            {
                action.IsSuccessed = false;
                action.Data.SetValue(this, null);
                //action.LocaleMsg = new($"工单批次[{Batch.BATCH_NO}]状态[{Batch.STATUS.GetEnum<BIZ_MES_WO_BATCH.STATUSs>().GetName()}]不是允许生产的状态,请扫描允许生产的产品条码");
                action.LocaleMsg = new("MES.WorkBatch.WoBatchStatusCanNotWork", Batch.BATCH_NO, Batch.STATUS.GetEnum<BIZ_MES_WO_BATCH.STATUSs>().GetName());
            }
            //工单状态不是已下发或者生产中,则不允许生产
            if (WO.STATUS != BIZ_MES_WO.STATUSs.Release.GetValue() && WO.STATUS != BIZ_MES_WO.STATUSs.Working.GetValue())
            {
                action.IsSuccessed = false;
                action.Data.SetValue(this, null);
                //action.LocaleMsg = new($"工单[{WO.ORDER_NO}]状态[{Batch.STATUS.GetEnum<BIZ_MES_WO.STATUSs>().GetName()}]不是允许生产的状态,请扫描允许生产的产品条码");
                action.LocaleMsg = new("MES.WorkBatch.WoStatusCanNotWork", WO.ORDER_NO, Batch.STATUS.GetEnum<BIZ_MES_WO.STATUSs>().GetName());
            }
            //工单批次投入数量减去报废数量如果大于等于计划数量,则不允许生产
            //if (curNode.IS_INPUT == "Y" && Batch.INPUT_QTY - Batch.SCRAP_QTY >= Batch.PLAN_QTY)
            if (curNode.IS_INPUT == "Y" && WoSNs.Count(q => q.BATCH_NO == Batch.BATCH_NO) - Batch.SCRAP_QTY >= Batch.PLAN_QTY)
            {
                action.IsSuccessed = false;
                action.Data.SetValue(this, null);
                //action.LocaleMsg = new($"工单批次[{0}]已投入 {1},其中报废 {2},以满足计划数量[{3}],无需继续投入");
                action.LocaleMsg = new("MES.WorkBatch.BatchInputEnough", Batch.BATCH_NO, WoSNs.Count(q => q.BATCH_NO == Batch.BATCH_NO), Batch.SCRAP_QTY, Batch.PLAN_QTY);
            }
            //工单投入数量减去报废数量如果大于等于计划数量,则不允许生产
            if (curNode.IS_INPUT == "Y" && WoSNs.Count(q => q.WORK_ORDER == WO.ORDER_NO) - WO.SCRAP_QTY >= WO.PLAN_QTY)
            {
                action.IsSuccessed = false;
                action.Data.SetValue(this, null);
                //action.LocaleMsg = new($"工单[{0}]已投入 {1},其中报废 {2},以满足计划数量[{3}],无需继续投入");
                action.LocaleMsg = new("MES.WorkBatch.WoInputEnough", WO.ORDER_NO, WoSNs.Count(q => q.WORK_ORDER == WO.ORDER_NO), WO.SCRAP_QTY, WO.PLAN_QTY);
            }
            return action;
        }
        /// <summary>
        /// å·¥å•开工
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public bool StartWorking(string user)
        {
            if (Batch.STATUS == BIZ_MES_WO_BATCH.STATUSs.Release.GetValue() || Batch.STATUS == BIZ_MES_WO_BATCH.STATUSs.Paused.GetValue())
            {
                WO.STATUS = BIZ_MES_WO.STATUSs.Working.GetValue();
                WO.ACT_START_TIME = WO.ACT_START_TIME < new DateTime(2000, 1, 1) ? DateTime.Now : WO.ACT_START_TIME;
                Batch.STATUS = BIZ_MES_WO_BATCH.STATUSs.Working.GetValue();
                Batch.ACT_START_TIME = WO.ACT_START_TIME < new DateTime(2000, 1, 1) ? DateTime.Now : WO.ACT_START_TIME;
                //保存数据库
                var db = Biz.Db;
                var dbTran = db.UseTran(() =>
                {
                    db.Updateable(WO, user).UpdateColumns(q => new { q.UPDATE_TIME, q.UPDATE_USER, q.STATUS, q.ACT_START_TIME }).ExecuteCommand();
                    db.Updateable(Batch, user).UpdateColumns(q => new { q.UPDATE_TIME, q.UPDATE_USER, q.STATUS, q.ACT_START_TIME }).ExecuteCommand();
                });
                if (!dbTran.IsSuccess)
                {
                    //throw dbTran.ErrorException;
                    return false;
                }
            }
            return true;
        }
        /// <summary>
        /// å·¥å•暂停
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public bool PausedWorking(string user)
        {
            if (Batch.STATUS == BIZ_MES_WO_BATCH.STATUSs.Working.GetValue())
            {
                WO.STATUS = BIZ_MES_WO.STATUSs.Paused.GetValue();
                Batch.STATUS = BIZ_MES_WO_BATCH.STATUSs.Paused.GetValue();
                //保存数据库
                var db = Biz.Db;
                var dbTran = db.UseTran(() =>
                {
                    db.Updateable(WO, user).UpdateColumns(q => new { q.UPDATE_TIME, q.UPDATE_USER, q.STATUS }).ExecuteCommand();
                    db.Updateable(Batch, user).UpdateColumns(q => new { q.UPDATE_TIME, q.UPDATE_USER, q.STATUS }).ExecuteCommand();
                });
                if (!dbTran.IsSuccess)
                {
                    //throw dbTran.ErrorException;
                    return false;
                }
            }
            return true;
        }
        /// <summary>
        /// æ ¹æ®å²—位编码判断是不是首站
        /// </summary>
        /// <param name="postCode"></param>
        /// <returns></returns>
        public bool IsFirstNode(string postCode)
        {
            return Nodes.Any(q => q.IS_FIRST_NODE == "Y" && NodePosts.Any(p => p.NODE_ID == q.ID && p.POST_CODE == postCode));
        }
        /// <summary>
        /// æ ¹æ®å²—位编码返回工序节点
        /// </summary>
        /// <param name="postCode"></param>
        /// <returns></returns>
        public MES_WO_NODE GetNode(string postCode)
        {
            return Nodes.FirstOrDefault(q => NodePosts.Any(p => p.NODE_ID == q.ID && p.POST_CODE == postCode));
        }
        /// <summary>
        /// æ ¹æ®ä¼ å…¥çš„æ¡ç è¿‡ç«™ä¿¡æ¯å’Œä¸‹ä¸€ç«™çš„目标工序,判断条码是否能进入下一站
        /// </summary>
        /// <param name="input">本次提交的数据</param>
        /// <param name="wipSN">当前的条码过站记录,需要导航查询生产过程记录信息</param>
        /// <param name="nextNode">要进入的目标工序</param>
        /// <returns></returns>
        public ApiAction<SubmitOutput> CanGotoNext(SubmitInput input, MES_WIP_DATA wipSN, MES_WO_NODE nextNode)
        {
            var action = new ApiAction<SubmitOutput>(new SubmitOutput());
            //条码在本工单第一次过站
            if (wipSN.NODE_ID.IsNullOrEmpty())
            {
                if (nextNode.IS_FIRST_NODE == "Y")
                {
                    action.IsSuccessed = true;
                    return action;
                }
                else
                {
                    action.IsSuccessed = false;
                    action.Data.SetValue(this, null);
                    var nextList = Nodes.Where(q => q.IS_FIRST_NODE == "Y");
                    action.LocaleMsg = new("MES.WorkBatch.GotoNextNodeException", input.SN, nextNode.NODE_NAME, string.Join(", ", nextList.Select(q => q.NODE_NAME + (q.CAN_SKIP == "Y" ? $"({Biz.T(Biz.L("MES.WorkBatch.Optional"), input.Locale)})" : ""))));
                    return action;
                }
            }
            //条码已有过站记录
            else
            {
                var curNode = Nodes.First(q => q.ID == wipSN.NODE_ID);
                var curSetting = NodeSets.FirstOrDefault(q => q.NODE_ID == curNode.ID);
                var nextSetting = NodeSets.FirstOrDefault(q => q.NODE_ID == nextNode.ID);
                //进入维修:如果目标工序是维修工序则判断当前条码在本工单有未维修的不良记录且当前工序节点有连线到维修工序,则允许进入维修工序
                if (nextNode.Operation.OPER_TYPE == MES_OPERATION.OPER_TYPEs.Repair.GetValue()
                    && wipSN.Defects.Any(q => q.WORK_ORDER == WO.ORDER_NO && q.STATUS < MES_WIP_DFT.STATUSs.Resolved.GetValue())
                    && Edges.Any(q => q.SRC_NODE == curNode.ID && q.TGT_NODE == nextNode.ID))
                {
                    action.IsSuccessed = true;
                    return action;
                }
                //维修回流:如果条码的当前工序是维修工序,则认为是维修回流
                else if (curNode.Operation.OPER_TYPE == MES_OPERATION.OPER_TYPEs.Repair.GetValue())
                {
                    //查找所有可以回流的工序
                    var reflowNodes = Nodes.Where(q => Edges.Any(e => e.SRC_NODE == curNode.ID && e.TGT_NODE == q.ID)
                                                                && NodeSets.Any(s => s.NODE_ID == q.ID && s.IS_ACTIVE == "Y")).ToList();
                    //可回流的工序包含目标工序且条码在维修站选择的回流工序为空或者等于目标工序,则允许进站
                    if (reflowNodes.Any(q => q.ID == nextNode.ID) && (wipSN.REFLOW_NODE.IsNullOrEmpty() || wipSN.REFLOW_NODE == nextNode.NODE_NAME))
                    {
                        action.IsSuccessed = true;
                        return action;
                    }
                    else
                    {
                        action.IsSuccessed = false;
                        action.Data.SetValue(this, null);
                        var nextList = reflowNodes.Where(q => wipSN.REFLOW_NODE.IsNullOrEmpty() || wipSN.REFLOW_NODE == q.NODE_NAME);
                        action.LocaleMsg = new("MES.WorkBatch.ReflowToNodeException", nextNode.NODE_NAME, input.SN, curNode.NODE_NAME, string.Join(", ", nextList.Select(q => q.NODE_NAME + (q.CAN_SKIP == "Y" ? $"({Biz.T(Biz.L("MES.WorkBatch.Optional"), input.Locale)})" : ""))));
                        return action;
                    }
                }
                //不良品入站:如果产品有不良记录且目标工序不是维修工序且不允许不良品入站,则报错
                else if (wipSN.Defects.Any(q => q.WORK_ORDER == WO.ORDER_NO && q.STATUS < MES_WIP_DFT.STATUSs.Resolved.GetValue())
                     && nextSetting.ALLOW_DFT_IN != "Y" && nextNode.Operation.OPER_TYPE != MES_OPERATION.OPER_TYPEs.Repair.GetValue())
                {
                    action.IsSuccessed = false;
                    action.Data.SetValue(this, null);
                    action.LocaleMsg = new("MES.WorkBatch.PleaseGotoRepair", curNode.NODE_NAME, input.SN);
                    return action;
                }
                //正常工序过站
                else
                {
                    //添加条码当前工序的下一个可执行工序
                    var nextNodes = GetNextNodes(curNode, wipSN);
                    //如果下一个可执行工序包含目标工序则允许进站
                    if (nextNodes.Any(q => q.ID == nextNode.ID))
                    {
                        action.IsSuccessed = true;
                        return action;
                    }
                    //如果当前工序有必须执行的后续工序,则报错
                    if (nextNodes.Any(q => NodeSets.Any(s => s.NODE_ID == q.ID && s.IS_ACTIVE == "Y" && s.CAN_SKIP != "Y")))
                    {
                        action.IsSuccessed = false;
                        action.Data.SetValue(this, null);
                        var nextList = nextNodes.Where(q => NodeSets.Any(s => s.NODE_ID == q.ID && s.IS_ACTIVE == "Y" && s.CAN_SKIP != "Y"));
                        action.LocaleMsg = new("MES.WorkBatch.GotoNextNodeException", input.SN, nextNode.NODE_NAME, string.Join(", ", nextList.Select(q => q.NODE_NAME + (q.CAN_SKIP == "Y" ? $"({Biz.T(Biz.L("MES.WorkBatch.Optional"), input.Locale)})" : ""))));
                        return action;
                    }
                    //如果当前工序没有必须执行的后续工序,则在前置工序查找还有没有后续工序没完成的工序,有则尝试执行
                    else
                    {
                        //在前置工序查找还有没有后续工序没完成的前置工序
                        var prepIDs = curNode.PrepNodeIDs.Where(id =>
                                                             Edges.Any(e => e.SRC_NODE == id && !wipSN.History.Any(h => h.WORK_ORDER == WO.ORDER_NO && h.NODE_ID == e.TGT_NODE && h.IsFinished))
                                                             ).ToList();
                        foreach (var prepID in prepIDs)
                        {
                            //如果连线的目标工序的前置工序都已完成,则把连线的目标工序添加到可执行工序列表
                            var prep = Nodes.First(q => q.ID == prepID);
                            var next = GetNextNodes(prep, wipSN);
                            nextNodes.AddRange(next);
                        }
                        //如果下一个可执行工序包含目标工序则允许进站
                        if (nextNodes.Any(q => q.ID == nextNode.ID))
                        {
                            action.IsSuccessed = true;
                            return action;
                        }
                        else
                        {
                            action.IsSuccessed = false;
                            action.Data.SetValue(this, null);
                            var nextList = nextNodes.Where(q => NodeSets.Any(s => s.NODE_ID == q.ID && s.IS_ACTIVE == "Y"));
                            action.LocaleMsg = new("MES.WorkBatch.GotoNextNodeException", input.SN, nextNode.NODE_NAME, string.Join(", ", nextList.Select(q => q.NODE_NAME + (q.CAN_SKIP == "Y" ? $"({Biz.T(Biz.L("MES.WorkBatch.Optional"), input.Locale)})" : ""))));
                            return action;
                        }
                    }
                }
            }
            return action;
        }
        /// <summary>
        /// æ·»åŠ èŠ‚ç‚¹çš„ä¸‹ä¸€ä¸ªå¯æ‰§è¡ŒèŠ‚ç‚¹
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="wipSN">当前的条码过站记录,需要导航查询生产过程记录信息</param>
        private List<MES_WO_NODE> GetNextNodes(MES_WO_NODE parent, MES_WIP_DATA wipSN)
        {
            var result = new List<MES_WO_NODE>();
            var nextNodes = Nodes.Where(q => q.Operation.OPER_TYPE != MES_OPERATION.OPER_TYPEs.Repair.GetValue()
                                                                && Edges.Any(e => e.SRC_NODE == parent.ID && e.TGT_NODE == q.ID)
                                                                && !wipSN.History.Any(h => h.WORK_ORDER == WO.ORDER_NO && h.NODE_ID == q.ID && h.IsFinished)
                                                             ).ToList();
            //尝试将当前工序的后续工序添加到可以执行的工序列表
            foreach (var next in nextNodes)
            {
                //查找有没有前置工序找不到已良品过站的历史记录,若有则不允许继续执行
                if (!next.PrepNodeIDs.Any(id => !wipSN.History.Any(h => h.WORK_ORDER == WO.ORDER_NO && h.NODE_ID == id && h.IsFinished)) || parent.IS_FIRST_NODE == "Y")
                {
                    var setting = NodeSets.FirstOrDefault(q => q.NODE_ID == next.ID);
                    //后续工序是启用的,则添加
                    if (!result.Any(q => q.ID == next.ID) && setting.IS_ACTIVE == "Y")
                    {
                        result.Add(next);
                    }
                    //后续工序是不启用或者可跳站,则继续添加其后续工序
                    if (!setting.IsNullOrEmpty() && (setting.IS_ACTIVE != "Y" || setting.CAN_SKIP == "Y"))
                    {
                        result.AddRange(GetNextNodes(next, wipSN));
                    }
                }
            }
            return result;
        }
        /// <summary>
        /// æ ¹æ®ä¼ å…¥çš„æ¡ç è¿”回下一站可进入进入的目标工序
        /// </summary>
        /// <param name="wipSN">当前的条码过站记录,需要导航查询生产过程记录和生产不良记录信息</param>
        /// <returns></returns>
        public List<MES_WO_NODE> GetNextNodes(MES_WIP_DATA wipSN)
        {
            var result = new List<MES_WO_NODE>();
            //条码在本工单第一次过站
            if (wipSN.NODE_ID.IsNullOrEmpty())
            {
                result.AddRange(Nodes.Where(q => q.IS_FIRST_NODE == "Y"));
            }
            //条码已有过站记录
            else
            {
                var curNode = Nodes.First(q => q.ID == wipSN.NODE_ID);
                //条码在维修工序,返回可回流工序
                if (curNode.Operation.OPER_TYPE == MES_OPERATION.OPER_TYPEs.Repair.GetValue())
                {
                    //查找所有可以回流的工序
                    var reflowNodes = Nodes.Where(q => Edges.Any(e => e.SRC_NODE == curNode.ID && e.TGT_NODE == q.ID)
                                                                && NodeSets.Any(s => s.NODE_ID == q.ID && s.IS_ACTIVE == "Y")).ToList();
                    result.AddRange(reflowNodes.Where(q => wipSN.REFLOW_NODE.IsNullOrEmpty() || wipSN.REFLOW_NODE == q.NODE_NAME));
                }
                else
                {
                    //往下查找条码当前工序的下一个可执行工序
                    var nextNodes = GetNextNodes(curNode, wipSN);
                    //如果当前工序没有必须执行的后续工序,则尝试在前置工序查找还有没有后续工序没完成的工序,有则加入
                    if (!nextNodes.Any(q => NodeSets.Any(s => s.NODE_ID == q.ID && s.IS_ACTIVE == "Y" && s.CAN_SKIP != "Y")))
                    {
                        //在前置工序查找还有没有后续工序没完成的前置工序
                        var prepIDs = curNode.PrepNodeIDs.Where(id =>
                                                             Edges.Any(e => e.SRC_NODE == id && !wipSN.History.Any(h => h.WORK_ORDER == WO.ORDER_NO && h.NODE_ID == e.TGT_NODE && h.IsFinished))
                                                             ).ToList();
                        foreach (var prepID in prepIDs)
                        {
                            //如果连线的目标工序的前置工序都已完成,则把连线的目标工序添加到可执行工序列表
                            var prep = Nodes.First(q => q.ID == prepID);
                            var next = GetNextNodes(prep, wipSN);
                            nextNodes.AddRange(next);
                        }
                    }
                    result.AddRange(nextNodes);
                    //如果当前条码是不良则只返回允许不良进站的工序和工序连接的维修工序
                    if (wipSN.Defects.Any(q => q.WORK_ORDER == WO.ORDER_NO && q.STATUS < MES_WIP_DFT.STATUSs.Resolved.GetValue()))
                    {
                        result.RemoveAll(q => NodeSets.Any(s => s.NODE_ID == q.ID && s.ALLOW_DFT_IN != "Y"));
                        //加入工序连接的维修工序
                        var repairNodes = Nodes.Where(q => Edges.Any(e => e.SRC_NODE == curNode.ID && e.TGT_NODE == q.ID) && q.Operation.OPER_TYPE == MES_OPERATION.OPER_TYPEs.Repair.GetValue());
                        result.AddRange(repairNodes);
                    }
                }
            }
            return result;
        }
        /// <summary>
        /// æ ¹æ®å²—位编码返回工序不良代码
        /// </summary>
        /// <param name="postCode"></param>
        /// <returns></returns>
        public List<DefectOutput> GetNodeDefects(string postCode)
        {
            var result = new List<DefectOutput>();
            var groups = DefectGroups.Where((b, s) => NodeDftgs.Any(d => d.NODE_ID == GetNode(postCode).ID && d.DFTG_CODE == b.DFTG_CODE)).ToList();
            foreach (var group in groups)
            {
                result.AddRange(group.Defects.Select(q => new DefectOutput
                {
                    DFTG_CODE = q.DFTG_CODE,
                    DFTG_NAME = group.DFTG_NAME,
                    DFT_CODE = q.DFT_CODE,
                    DFT_NAME = q.DFT_NAME,
                    DFT_LEVEL = q.DFT_LEVEL,
                }));
            }
            return result;
        }
        #endregion
        /// <summary>
        /// å…³é—­å·¥å•
        /// </summary>
        /// <returns></returns>
        public bool Close()
        {
            Dispose();
            return true;
        }
        public virtual void Dispose()
        {
            WoContext.WoBatchDic.Remove(OrderNo);
        }
    }
}
Tiger.Business.MES/Common/WorkStep.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,143 @@
using Tiger.Model;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Rhea.Common;
using System.Net;
using System.Linq;
using Newtonsoft.Json;
using Tiger.IBusiness;
using Tiger.Model.Entitys.MES.Position;
namespace Tiger.Business
{
    /// <summary>
    /// å·¥æ­¥
    /// </summary>
    public partial class WorkStep : IWorkStep
    {
        public WorkStep(IWorkStep.NodeTypes type, IPosition position)
        {
            NodeType = type;
            CurPosition = position;
        }
        #region Propertys & Variables
        public string ID { get; set; } = Guid.NewGuid().ToString("N");
        public string Name { get; set; }
        public IWorkStep.NodeTypes NodeType { get; set; }
        public MES_WO_NODE Node { get; set; }
        public MES_WO_OPER OperSetting { get; set; }
        public MES_WO_NODE_ACT NodeAct { get; set; }
        public MES_WO_ACTION ActSetting { get; set; }
        public IPosition CurPosition { get; set; }
        public IWorkAction CurAction { get; set; }
        public int Sequence { get; set; }
        public List<string> PrepNodeIDs { get; set; } = new();
        public string NodeID => NodeType == IWorkStep.NodeTypes.Action ? NodeAct.ID : Node.ID;
        private bool _IsFinished = false;
        public bool IsFinished
        {
            get => NodeType == IWorkStep.NodeTypes.Action ? CurAction.IsFinished : _IsFinished;
            set { _IsFinished = value; }
        }
        public StepStatus Status { get; set; } = StepStatus.Normal;
        //public bool IsFinished => Status == StepStatus.Finished;
        private Locale _Message;
        public Locale Message
        {
            get => _Message;
            set { _MsgHistory.Add(_Message = value); }
        }
        private List<Locale> _MsgHistory = new();
        public List<Locale> MsgHistory => _MsgHistory;
        public Action DBSubmitAction { get; set; } = () => { };
        #endregion
        #region Functions
        /// <summary>
        /// åˆå§‹åŒ–工步
        /// </summary>
        /// <returns></returns>
        public void Init()
        {
            CurAction = DI.Resolve(NodeAct.Definition.SERVICE_TYPE) as IWorkAction;
            //CurAction = DI.Resolve("Tiger.IBusiness.IPrintLabel,Tiger.IBusiness") as IWorkAction;
            CurAction.Init(this, CurPosition, NodeAct, ActSetting);
        }
        /// <summary>
        /// å°è¯•开始执行工步
        /// </summary>
        /// <returns></returns>
        public ApiAction<SubmitOutput> TryBegin(SubmitInput input)
        {
            return CurAction.TryBegin(input);
        }
        /// <summary>
        /// èŽ·å–è¡Œä¸ºå¼€å§‹çš„æç¤ºä¿¡æ¯
        /// </summary>
        /// <returns></returns>
        public Locale GetBeginMsg()
        {
            return CurAction.GetBeginMsg();
        }
        /// <summary>
        /// å·¥æ­¥æäº¤æ•°æ®
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public ApiAction<SubmitOutput> Submit(SubmitInput input)
        {
            return CurAction.Submit(input);
        }
        /// <summary>
        /// ç»“束工步执行
        /// </summary>
        /// <returns></returns>
        public ApiAction<SubmitOutput> End(SubmitInput input)
        {
            return CurAction.End(input);
        }
        /// <summary>
        /// è®¾ç½®å·¥æ­¥çŠ¶æ€å’Œæ¶ˆæ¯
        /// </summary>
        /// <returns></returns>
        public void SetStatusMessage(StepStatus status, Locale msg)
        {
            Message = msg;
            Status = status;
        }
        /// <summary>
        /// èŽ·å–å½“å‰å·¥æ­¥çš„ä¿¡æ¯
        /// </summary>
        /// <returns></returns>
        public WorkStepInfo GetInfo(string locale)
        {
            return new WorkStepInfo()
            {
                ID = ID,
                Name = Name,
                Sequence = Sequence,
                NodeID = NodeID,
                NodeType = NodeType.ToString(),
                Node = Node,
                OperSetting = OperSetting,
                NodeAct = NodeAct,
                ActSetting = ActSetting,
                Status = Status.ToString(),
                Message = Message.IsNullOrEmpty() ? "" : Biz.T(Message, locale),
            };
        }
        #endregion
    }
}
Tiger.Business.MES/Tiger.Business.MES.csproj
@@ -8,6 +8,7 @@
  <ItemGroup>
    <ProjectReference Include="..\Tiger.Business\Tiger.Business.csproj" />
    <ProjectReference Include="..\Tiger.IBusiness.MES\Tiger.IBusiness.MES.csproj" />
    <ProjectReference Include="..\Tiger.IBusiness\Tiger.IBusiness.csproj" />
  </ItemGroup>
Tiger.Business.MES/Transaction/CollectNode.cs
@@ -412,7 +412,7 @@
                    //初始化工步列表
                    Steps.Clear();
                    var curStep = new Biz.WorkStep(IWorkStep.NodeTypes.Node, this)
                    var curStep = new WorkStep(IWorkStep.NodeTypes.Node, this)
                    {
                        Name = "扫描产品",
                        Sequence = Steps.Count + 1,
Tiger.Business.MES/Transaction/PackingNode.cs
@@ -453,7 +453,7 @@
                    //初始化工步列表
                    Steps.Clear();
                    var curStep = new Biz.WorkStep(IWorkStep.NodeTypes.Node, this)
                    var curStep = new WorkStep(IWorkStep.NodeTypes.Node, this)
                    {
                        Name = "扫描产品",
                        Sequence = Steps.Count + 1,
Tiger.Business.MES/Transaction/TestNode.cs
@@ -412,7 +412,7 @@
                    //初始化工步列表
                    Steps.Clear();
                    var curStep = new Biz.WorkStep(IWorkStep.NodeTypes.Node, this)
                    var curStep = new WorkStep(IWorkStep.NodeTypes.Node, this)
                    {
                        Name = "扫描产品",
                        Sequence = Steps.Count + 1,
Tiger.Business/MES/Biz.WorkBatch.cs
ÎļþÒÑɾ³ý
Tiger.Business/MES/Biz.WorkStep.cs
ÎļþÒÑɾ³ý
Tiger.IBusiness.MES/BIZ/IBasRule.cs
Tiger.IBusiness.MES/BIZ/IMES_WO.cs
Tiger.IBusiness.MES/BIZ/IMES_WORKSHOP.cs
Tiger.IBusiness.MES/Common/ICodeVerification.cs
Tiger.IBusiness.MES/Common/IDoUnPack.cs
Tiger.IBusiness.MES/Extensions/ModelExtension.cs
Tiger.IBusiness.MES/IMESContext.cs
Tiger.IBusiness.MES/IWorkStep.cs
Tiger.IBusiness.MES/LabelTemplate/ILabelTemplate.cs
Tiger.IBusiness.MES/SMT/ISmtTool.cs
Tiger.IBusiness.MES/Tiger.IBusiness.MES.csproj
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\Tiger.IBusiness\Tiger.IBusiness.csproj" />
  </ItemGroup>
</Project>
Tiger.IBusiness.MES/Transaction/ICollectNode.cs
Tiger.IBusiness.MES/Transaction/ILoadingMaterial.cs
Tiger.IBusiness.MES/Transaction/IMESTransaction.cs
Tiger.IBusiness.MES/Transaction/IPackingNode.cs
Tiger.IBusiness.MES/Transaction/IPosition.cs
Tiger.IBusiness.MES/Transaction/ITestNode.cs
Tiger.IBusiness.MES/WorkAction/IAssembly.cs
Tiger.IBusiness.MES/WorkAction/IIPQC.cs
Tiger.IBusiness.MES/WorkAction/IPackageInputCheck.cs
Tiger.IBusiness.MES/WorkAction/IPackingAction.cs
Tiger.IBusiness.MES/WorkAction/IPrintLabel.cs
Tiger.IBusiness.MES/WorkAction/IThreeInOne.cs
Tiger.IBusiness.MES/WorkAction/IVerifyCustomSN.cs
Tiger.IBusiness.MES/WorkAction/IWeighing.cs
Tiger.IBusiness.MES/WorkAction/IWipExtInfo.cs
Tiger.IBusiness.MES/WorkAction/IWorkAction.cs
Tiger.IBusiness.MES/WorkAction/IYadaAssembly.cs
Tiger.IBusiness.MES/iERP/IU9C_MES.cs