kazelee
2025-05-15 1af49bdb3cdaa44a7e44ebdc843fb8180596365f
尾箱回库、移库逻辑开发
4 文件已重命名
4个文件已添加
16个文件已修改
5个文件已删除
1390 ■■■■ 已修改文件
.vs/HH.WCS.Mobox3.DSZSH/FileContentIndex/7725d916-1bef-44d2-8384-bfd94e8b6457.vsidx 补丁 | 查看 | 原始文档 | blame | 历史
.vs/HH.WCS.Mobox3.DSZSH/FileContentIndex/e39d6e75-9277-46f5-bff4-9390c52a26f9.vsidx 补丁 | 查看 | 原始文档 | blame | 历史
.vs/HH.WCS.Mobox3.DSZSH/FileContentIndex/e9728df6-3b77-4640-883e-78a872e2fb53.vsidx 补丁 | 查看 | 原始文档 | blame | 历史
.vs/HH.WCS.Mobox3.DSZSH/FileContentIndex/f2527103-d3d3-44d1-9a80-1e26bb5cf98f.vsidx 补丁 | 查看 | 原始文档 | blame | 历史
AppStart/SwaggerControllerDescProvider.cs 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
App_Start/Config.cs 补丁 | 查看 | 原始文档 | blame | 历史
App_Start/Settings.cs 补丁 | 查看 | 原始文档 | blame | 历史
App_Start/Startup.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
App_Start/SwaggerConfig.cs 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
App_Start/SwaggerControllerDescProvider.cs 237 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Consts/TaskName.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Controllers/DebugController.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Controllers/MoboxController.cs 241 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Dtos/Request/AgvRequest.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Dtos/Request/MoboxRequest.cs 311 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Dtos/Response/MoboxResponse.cs 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HH.WCS.Mobox3.DSZSH.csproj 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_CG_Detail.cs 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_Check_Detail.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_Check_Order.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_Shift_Detail.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_Shift_Order.cs 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServiceCore/CheckCore.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServiceCore/ShiftCore.cs 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Services/AgvService.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Services/MoboxService.cs 173 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/config.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages.config 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
swagger.js 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.vs/HH.WCS.Mobox3.DSZSH/FileContentIndex/7725d916-1bef-44d2-8384-bfd94e8b6457.vsidx
Binary files differ
.vs/HH.WCS.Mobox3.DSZSH/FileContentIndex/e39d6e75-9277-46f5-bff4-9390c52a26f9.vsidx
Binary files differ
.vs/HH.WCS.Mobox3.DSZSH/FileContentIndex/e9728df6-3b77-4640-883e-78a872e2fb53.vsidx
Binary files differ
.vs/HH.WCS.Mobox3.DSZSH/FileContentIndex/f2527103-d3d3-44d1-9a80-1e26bb5cf98f.vsidx
Binary files differ
AppStart/SwaggerControllerDescProvider.cs
File was deleted
App_Start/Config.cs
App_Start/Settings.cs
App_Start/Startup.cs
File was renamed from AppStart/Startup.cs
@@ -4,6 +4,8 @@
using System.Threading.Tasks;
using System.Web.Http;
using Microsoft.Owin;
using Newtonsoft.Json;
using Owin;
using Swashbuckle.Application;
@@ -20,6 +22,8 @@
                defaults: new { id = RouteParameter.Optional }
                );
            //SwaggerConfig.Register(config);
            // Web API routes
            //config.MapHttpAttributeRoutes();
App_Start/SwaggerConfig.cs
File was renamed from AppStart/SwaggerConfig.cs
@@ -1,14 +1,49 @@
using System.Web.Http;
using WebActivatorEx;
using HH.WCS.Mobox3.Template;
using HH.WCS.Mobox3.DSZSH;
using Swashbuckle.Application;
using System.IO;
using System.Reflection;
using System;
using Swashbuckle.Examples;
[assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")]
namespace HH.WCS.Mobox3.Template
namespace HH.WCS.Mobox3.DSZSH
{
    public class SwaggerConfig
    {
        public static void Register(HttpConfiguration config) {
            var thisAssembly = typeof(SwaggerConfig).Assembly;
            config
                .EnableSwagger(c => {
                    c.SingleApiVersion("v1", "HH.WCS.Mobox3.DSZSH");
                    // 设置 XML 注释路径
                    var baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
                    //var commentsFileName = Assembly.GetExecutingAssembly().GetName().Name + ".xml";
                    var commentsFileName = "bin\\Debug\\HH.WCS.Mobox3.DSZSH.xml";
                    var commentsFile = Path.Combine(baseDirectory, commentsFileName);
                    if (File.Exists(commentsFile)) {
                        c.IncludeXmlComments(commentsFile);
                    }
                    // 其他配置...
                    c.UseFullTypeNameInSchemaIds();
                    // 启用示例数据
                    //c.DescribeAllEnumsAsStrings();
                    //c.OperationFilter<ExamplesOperationFilter>();
                    //c.ModelFilter<ExamplesModelFilter>();
                })
                .EnableSwaggerUi(c => {
                    // UI 配置
                });
        }
        public static void Register()
        {
            var thisAssembly = typeof(SwaggerConfig).Assembly;
@@ -32,7 +67,7 @@
                        // hold additional metadata for an API. Version and title are required but you can also provide
                        // additional fields by chaining methods off SingleApiVersion.
                        //
                        c.SingleApiVersion("v1", "HH.WCS.Mobox3.Template");
                        c.SingleApiVersion("v1", "HH.WCS.Mobox3.DSZSH");
                        // If you want the output Swagger docs to be indented properly, enable the "PrettyPrint" option.
                        //
@@ -61,7 +96,7 @@
                        //c.BasicAuth("basic")
                        //    .Description("Basic HTTP Authentication");
                        //
                        // NOTE: You must also configure 'EnableApiKeySupport' below in the SwaggerUI section
                        // NOTE: You must also configure 'EnableApiKeySupport' below in the SwaggerUI section
                        //c.ApiKey("apiKey")
                        //    .Description("API Key Authentication")
                        //    .Name("apiKey")
@@ -103,6 +138,10 @@
                        //
                        //c.IncludeXmlComments(GetXmlCommentsPath());
                        // 启用 XML 注释
                        var xmlFile = $"{System.AppDomain.CurrentDomain.BaseDirectory}\\HH.WCS.Mobox3.DSZSH.xml";
                        c.IncludeXmlComments(xmlFile);
                        // Swashbuckle makes a best attempt at generating Swagger compliant JSON schemas for the various types
                        // exposed in your API. However, there may be occasions when more control of the output is needed.
                        // This is supported through the "MapType" and "SchemaFilter" options:
App_Start/SwaggerControllerDescProvider.cs
New file
@@ -0,0 +1,237 @@
using Swashbuckle.Swagger;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
namespace HH.WCS.Mobox3.DSZSH {
    /// <summary>
    /// Swagger 显示控制器的描述
    /// </summary>
    public class SwaggerCacheProvider : ISwaggerProvider
    {
        private readonly ISwaggerProvider _swaggerProvider;
        private static ConcurrentDictionary<string, SwaggerDocument> _cache = new ConcurrentDictionary<string, SwaggerDocument>();
        private readonly string _xmlPath;
        /// <summary>
        ///
        /// </summary>
        /// <param name="swaggerProvider"></param>
        /// <param name="xmlpath">xml文档路径</param>
        public SwaggerCacheProvider(ISwaggerProvider swaggerProvider, string xmlpath)
        {
            _swaggerProvider = swaggerProvider;
            _xmlPath = xmlpath;
        }
        public SwaggerDocument GetSwagger(string rootUrl, string apiVersion)
        {
            var cacheKey = string.Format("{0}_{1}", rootUrl, apiVersion);
            // 只读取一次
            if (!_cache.TryGetValue(cacheKey, out SwaggerDocument srcDoc))
            {
                srcDoc = _swaggerProvider.GetSwagger(rootUrl, apiVersion);
                srcDoc.vendorExtensions = new Dictionary<string, object>
            {
                { "ControllerDesc", GetControllerDesc() }
            };
                _cache.TryAdd(cacheKey, srcDoc);
            }
            return srcDoc;
        }
        /// <summary>
        /// 从API文档中读取控制器描述
        /// </summary>
        /// <returns>所有控制器描述</returns>
        public ConcurrentDictionary<string, string> GetControllerDesc()
        {
            ConcurrentDictionary<string, string> controllerDescDict = new ConcurrentDictionary<string, string>();
            if (File.Exists(_xmlPath))
            {
                XmlDocument xmldoc = new XmlDocument();
                xmldoc.Load(_xmlPath);
                string[] arrPath;
                int cCount = "Controller".Length;
                foreach (XmlNode node in xmldoc.SelectNodes("//member"))
                {
                    string type = node.Attributes["name"].Value;
                    if (type.StartsWith("T:"))
                    {
                        arrPath = type.Split('.');
                        string controllerName = arrPath[arrPath.Length - 1];
                        if (controllerName.EndsWith("Controller")) // 控制器
                        {
                            // 获取控制器注释
                            XmlNode summaryNode = node.SelectSingleNode("summary");
                            string key = controllerName.Remove(controllerName.Length - cCount, cCount);
                            if (summaryNode != null && !string.IsNullOrEmpty(summaryNode.InnerText) && !controllerDescDict.ContainsKey(key))
                            {
                                controllerDescDict.TryAdd(key, summaryNode.InnerText.Trim());
                            }
                        }
                    }
                }
            }
            return controllerDescDict;
        }
    }
    /// <summary>
    /// 增强版Swagger提供程序,支持显示控制器和模型的完整文档注释
    /// </summary>
    public class EnhancedSwaggerCacheProvider : ISwaggerProvider {
        private readonly ISwaggerProvider _swaggerProvider;
        private static readonly ConcurrentDictionary<string, SwaggerDocument> _cache = new ConcurrentDictionary<string, SwaggerDocument>();
        private readonly string _xmlPath;
        public EnhancedSwaggerCacheProvider(ISwaggerProvider swaggerProvider, string xmlpath) {
            _swaggerProvider = swaggerProvider;
            _xmlPath = xmlpath;
        }
        public SwaggerDocument GetSwagger(string rootUrl, string apiVersion) {
            var cacheKey = $"{rootUrl}_{apiVersion}";
            return _cache.GetOrAdd(cacheKey, _ =>
            {
                var srcDoc = _swaggerProvider.GetSwagger(rootUrl, apiVersion);
                // 添加控制器描述
                var (controllerDesc, modelDesc) = GetXmlComments();
                srcDoc.vendorExtensions = new Dictionary<string, object>
                {
                { "ControllerDesc", controllerDesc },
                { "ModelDesc", modelDesc }
            };
                // 为模型添加描述
                EnhanceModelDescriptions(srcDoc, modelDesc);
                return srcDoc;
            });
        }
        /// <summary>
        /// 从XML文档中提取控制器和模型描述
        /// </summary>
        private (ConcurrentDictionary<string, string> controllerDesc,
                 ConcurrentDictionary<string, string> modelDesc) GetXmlComments() {
            var controllerDesc = new ConcurrentDictionary<string, string>();
            var modelDesc = new ConcurrentDictionary<string, string>();
            if (!File.Exists(_xmlPath)) return (controllerDesc, modelDesc);
            var xmldoc = new XmlDocument();
            xmldoc.Load(_xmlPath);
            foreach (XmlNode node in xmldoc.SelectNodes("//member")) {
                var type = node.Attributes?["name"]?.Value;
                if (string.IsNullOrEmpty(type)) continue;
                if (type.StartsWith("T:")) {
                    var arrPath = type.Split('.');
                    var typeName = arrPath[arrPath.Length - 1]; // 获取最后一部分
                    // 处理控制器
                    if (typeName.EndsWith("Controller")) {
                        var summaryNode = node.SelectSingleNode("summary");
                        if (summaryNode != null && !string.IsNullOrWhiteSpace(summaryNode.InnerText)) {
                            //var key = typeName[..^"Controller".Length];
                            string key = typeName;
                            const string controllerSuffix = "Controller";
                            if (typeName.EndsWith(controllerSuffix)) {
                                key = typeName.Substring(0, typeName.Length - controllerSuffix.Length);
                            }
                            controllerDesc.TryAdd(key, summaryNode.InnerText.Trim());
                        }
                    }
                    // 处理模型类
                    else if (IsModelType(node)) {
                        var summaryNode = node.SelectSingleNode("summary");
                        if (summaryNode != null && !string.IsNullOrWhiteSpace(summaryNode.InnerText)) {
                            modelDesc.TryAdd(typeName, summaryNode.InnerText.Trim());
                        }
                        // 处理模型属性
                        EnhancePropertyDescriptions(node, modelDesc);
                    }
                }
            }
            return (controllerDesc, modelDesc);
        }
        /// <summary>
        /// 判断是否为模型类型
        /// </summary>
        private bool IsModelType(XmlNode node) {
            // 这里可以根据实际需求调整判断逻辑
            // 例如:排除Controller、排除特定命名空间等
            var type = node.Attributes?["name"]?.Value ?? "";
            return type.StartsWith("T:") &&
                   !type.EndsWith("Controller") &&
                   !type.Contains(".Controllers.") &&
                   !type.Contains(".Infrastructure.");
        }
        /// <summary>
        /// 增强模型属性的描述
        /// </summary>
        private void EnhancePropertyDescriptions(XmlNode typeNode, ConcurrentDictionary<string, string> modelDesc) {
            var typeName = typeNode.Attributes?["name"]?.Value?.Split('.')?.LastOrDefault()?.Substring(2);
            if (string.IsNullOrEmpty(typeName)) return;
            foreach (XmlNode propNode in typeNode.SelectNodes("field|property")) {
                var propName = propNode.Attributes?["name"]?.Value;
                if (string.IsNullOrEmpty(propName)) continue;
                var summaryNode = propNode.SelectSingleNode("summary");
                var exampleNode = propNode.SelectSingleNode("example");
                if (summaryNode != null && !string.IsNullOrWhiteSpace(summaryNode.InnerText)) {
                    var fullPropKey = $"{typeName}.{propName}";
                    var description = summaryNode.InnerText.Trim();
                    if (exampleNode != null && !string.IsNullOrWhiteSpace(exampleNode.InnerText)) {
                        description += $"\n\n示例: {exampleNode.InnerText.Trim()}";
                    }
                    modelDesc.TryAdd(fullPropKey, description);
                }
            }
        }
        /// <summary>
        /// 增强Swagger文档中的模型描述
        /// </summary>
        private void EnhanceModelDescriptions(SwaggerDocument swaggerDoc, ConcurrentDictionary<string, string> modelDesc) {
            if (swaggerDoc.definitions == null) return;
            foreach (var definition in swaggerDoc.definitions) {
                // 处理模型类本身描述
                if (modelDesc.TryGetValue(definition.Key, out var classDesc)) {
                    definition.Value.description = classDesc;
                }
                // 处理模型属性描述
                if (definition.Value.properties != null) {
                    foreach (var property in definition.Value.properties) {
                        var fullPropKey = $"{definition.Key}.{property.Key}";
                        if (modelDesc.TryGetValue(fullPropKey, out var propDesc)) {
                            property.Value.description = propDesc;
                        }
                    }
                }
            }
        }
    }
}
Consts/TaskName.cs
@@ -18,5 +18,8 @@
        public const string 抽检_不合格移库 = "抽检-不合格移库";
        public const string 抽检_出库 = "抽检-出库";
        public const string 抽检_合格回库 = "抽检-合格回库";
        public const string 移库 = "移库";
        public const string 尾箱回库 = "尾箱回库";
    }
}
Controllers/DebugController.cs
@@ -72,58 +72,6 @@
            return DebugService.InsertLocCntrCg();
        }
        public string AddCgCntrLocRel(CgInfo model) {
            var db = DbHelper.GetDbClient();
            try {
                using (var tran = db.Ado.UseTran()) {
                    var loc = db.Queryable<TN_Location>().First(a => a.S_CODE == model.LocId);
                    if (loc == null) {
                        var newLoc = new TN_Location { S_CODE = model.LocId, N_CURRENT_NUM = 1 };
                        if (db.Insertable<TN_Location>(newLoc).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            return $"插入位置{model.LocId}失败";
                        }
                        loc = db.Queryable<TN_Location>().First(a => a.S_CODE == model.LocId);
                    }
                    if (loc.N_CURRENT_NUM == 0) {
                        loc.N_CURRENT_NUM = 1;
                        if (db.Updateable<TN_Location>(loc).UpdateColumns(
                            it => new { it.N_CURRENT_NUM, it.T_MODIFY }).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            return $"修改位置{model.LocId}失败";
                        }
                    }
                    var locCntrRel = db.Queryable<TN_Loc_Container>().First(a => a.S_LOC_CODE == model.LocId && a.S_CNTR_CODE == model.CntId);
                    if (locCntrRel == null) {
                        var locList = new List<TN_Loc_Container>();
                        locList.Add(new TN_Loc_Container { S_LOC_CODE = model.LocId, S_CNTR_CODE = model.CntId });
                        if (db.Insertable<TN_Loc_Container>(locList).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            return $"插入位置托盘关系{model.LocId}-{model.CntId}失败";
                        }
                    }
                    var cgDetail = db.Queryable<TN_CG_Detail>().First(a => a.S_CNTR_CODE == model.CntId && a.S_ITEM_CODE == model.ItemCode);
                    if (cgDetail == null) {
                        var locList = new List<TN_CG_Detail>();
                        locList.Add(new TN_CG_Detail { S_CNTR_CODE = model.CntId, S_ITEM_CODE = model.ItemCode });
                        if (db.Insertable<TN_CG_Detail>(locList).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            return $"插入托盘物料关系{model.CntId}-{model.ItemCode}失败";
                        }
                    }
                    tran.CommitTran();
                }
            }
            catch (Exception ex) {
                return $"出错:{ex}";
            }
            return "success";
        }
    }
    public class CgInfo {
Controllers/MoboxController.cs
@@ -25,47 +25,49 @@
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("GoodpackOffline")]
        [Route("goodpack-offline")]
        public SimpleResult GoodpackOffline(GoodpackOfflineInfo model) {
            LogHelper.InfoApi("好运箱-满托下线入库(PDA)", model);
            return MoboxService.GoodpackOffline(model);
        }
        /// <summary>
        /// 空托/空箱绑定
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public SimpleResult EmptyBind(EmptyBindInfo model) {
            LogHelper.InfoApi("空托/空箱绑定", model);
        ///// <summary>
        ///// 空托/空箱入库绑定(PDA)
        ///// </summary>
        ///// <param name="model"></param>
        ///// <returns></returns>
        //public SimpleResult EmptyBind(EmptyBindInfo model) {
        //    LogHelper.InfoApi("空托/空箱绑定", model);
            if (model.CntrType == "托盘") {
                //LogHelper.Info($"触发API:空托绑定 " + JsonConvert.SerializeObject(model), "API");
                return MoboxService.EmptyBindPallet(model);
            }
            else if (model.CntrType == "好运箱") {
                //LogHelper.Info($"触发API:空箱绑定 " + JsonConvert.SerializeObject(model), "API");
                return MoboxService.EmptyBindGoodpack(model);
            }
            else {
                return BuildSimpleResult(-1, $"不合法的容器类型:'{model.CntrType}'");
            }
        }
        //    if (model.CntrType == "托盘") {
        //        //LogHelper.Info($"触发API:空托绑定 " + JsonConvert.SerializeObject(model), "API");
        //        return MoboxService.EmptyBindPallet(model);
        //    }
        //    else if (model.CntrType == "好运箱") {
        //        //LogHelper.Info($"触发API:空箱绑定 " + JsonConvert.SerializeObject(model), "API");
        //        return MoboxService.EmptyBindGoodpack(model);
        //    }
        //    else {
        //        return BuildSimpleResult(-1, $"不合法的容器类型:'{model.CntrType}'");
        //    }
        //}
        /// <summary>
        /// 空托/空箱入库(PDA)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("empty-inbound")]
        public SimpleResult EmptyInbound(EmptyInboundInfo model) {
            LogHelper.InfoApi("空托/空箱入库", model);
            var db = DbHelper.GetDbClient();
            var locCntrRel = db.Queryable<TN_Loc_Container>()
                .Where(lc => lc.S_LOC_CODE == model.LocCode).First();
                .Where(lc => lc.S_CNTR_CODE == model.CntrCode).First();
            if (locCntrRel == null) {
                return BuildSimpleResult(-1, $"不存在已绑定容器的货位号:'{model.LocCode}'");
                return BuildSimpleResult(-1, $"不存在已绑定容器的货位号:'{model.CnteType}'");
            }
            if (locCntrRel.S_CNTR_TYPE == "托盘") {
@@ -87,95 +89,146 @@
            }
        }
        /// <summary>
        /// 空托/空箱上线(PDA)
        /// </summary>
        /// <returns>
        /// 人工使用PDA扫码,根据物料类型判断上线空托/空箱
        /// </returns>
        public SimpleResult EmptyOnline(EmptyOnlineInfo model) {
            var db = DbHelper.GetDbClient();
            var locCntrRel = db.Queryable<TN_Loc_Container>()
                .LeftJoin<TN_CG_Detail>((lc, cd) => lc.S_CNTR_CODE == cd.S_CNTR_CODE)
                .Where((lc, cd) => cd.S_ITEM_CODE == model.ItemCode)
                .First();
        ///// <summary>
        ///// 空托/空箱上线(PDA)
        ///// </summary>
        ///// <returns>
        ///// 人工使用PDA扫码,根据物料类型判断上线空托/空箱
        ///// </returns>
        //public SimpleResult EmptyOnline(EmptyOnlineInfo model) {
        //    var db = DbHelper.GetDbClient();
        //    var locCntrRel = db.Queryable<TN_Loc_Container>()
        //        .LeftJoin<TN_CG_Detail>((lc, cd) => lc.S_CNTR_CODE == cd.S_CNTR_CODE)
        //        .Where((lc, cd) => cd.S_ITEM_CODE == model.ItemCode)
        //        .First();
            if (locCntrRel.S_CNTR_TYPE == "托盘") {
        //    if (locCntrRel.S_CNTR_TYPE == "托盘") {
                return MoboxService.EmptyOnlinePallet(new EmptyOnlinePalletInfo {
                    CntId = locCntrRel.S_CNTR_CODE,
                    EndLoc = model.EndLoc
                });
            }
            else if (locCntrRel.S_CNTR_TYPE == "好运箱") {
        //        return MoboxService.EmptyOnlinePallet(new EmptyOnlinePalletInfo {
        //            CntId = locCntrRel.S_CNTR_CODE,
        //            EndLoc = model.EndLoc
        //        });
        //    }
        //    else if (locCntrRel.S_CNTR_TYPE == "好运箱") {
                return MoboxService.EmptyOnlineGoodpack(new EmptyOnlineGoodpackInfo {
                    CntId = locCntrRel.S_CNTR_CODE,
                    EndLoc = model.EndLoc
                });
            }
            else {
                return BuildSimpleResult(-1, $"不合法的容器类型:'{locCntrRel.S_CNTR_CODE}'");
            }
        }
        //        return MoboxService.EmptyOnlineGoodpack(new EmptyOnlineGoodpackInfo {
        //            CntId = locCntrRel.S_CNTR_CODE,
        //            EndLoc = model.EndLoc
        //        });
        //    }
        //    else {
        //        return BuildSimpleResult(-1, $"不合法的容器类型:'{locCntrRel.S_CNTR_CODE}'");
        //    }
        //}
        /// <summary>
        /// 创建抽检单
        /// 托盘-空托上线(PDA)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("empty-online-pallet")]
        public SimpleResult EmptyOnlinePallet(EmptyOnlinePalletInfo model) {
            return MoboxService.EmptyOnlinePallet(model);
        }
        /// <summary>
        /// 好运箱-空箱上线(PDA)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("empty-online-goodpack")]
        public SimpleResult EmptyOnlineGoodpack(EmptyOnlineGoodpackInfo model) {
            return MoboxService.EmptyOnlineGoodpack(model);
        }
        ///// <summary>
        ///// 合格回库/不合格移库
        ///// </summary>
        ///// <param name="model"></param>
        ///// <returns></returns>
        //[HttpPost]
        //[Route("CheckShift")]
        //public SimpleResult CheckShift(CheckShiftInfo model) {
        //    LogHelper.InfoApi("合格回库/不合格移库", model);
        //    if (model.Qualified) {
        //        return MoboxService.QualifiedBack(model);
        //    }
        //    else {
        //        return MoboxService.UnqualifiedShift(model);
        //    }
        //}
        /// <summary>
        /// 合格回库(PDA)
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [Route("qualified-back")]
        public SimpleResult QualifiedBack(QualifiedBackInfo model) {
            return MoboxService.QualifiedBack(model);
        }
        /// <summary>
        /// 不合格移库(PDA)
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [Route("unqualified-shift")]
        public SimpleResult UnqualifiedShift(UnqualifiedShiftInfo model) {
            return MoboxService.UnqualifiedShift(model);
        }
        /// <summary>
        /// 余料尾箱回库(PDA)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("rest-back")]
        public SimpleResult RestBack(RestBackInfo model) {
            return MoboxService.RestBack(model);
        }
        #endregion
        #region Mobox 接口
        /// <summary>
        /// 成品胶出库(PDA)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("finished-outbound")]
        public SimpleResult FinishedOutbound(FinishedOutboundInfo model) {
            LogHelper.InfoApi("成品胶出库(PDA)", model);
            if (model.ForcedOut) {
                return MoboxService.FinishedOutboundForce(model);
            }
            return MoboxService.FinishedOutbound(model);
        }
        /// <summary>
        /// 抽检-创建抽检单(WMS)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("create-check-order")]
        public SimpleResult CreateCheckOrder(CreateCheckOrderInfo model) {
            return MoboxService.CreateCheckOrder(model);
        }
        /// <summary>
        /// 合格回库/不合格移库
        /// 移库-创建移库任务(WMS)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("CheckShift")]
        public SimpleResult CheckShift(CheckShiftInfo model) {
            LogHelper.InfoApi("合格回库/不合格移库", model);
            if (model.Qualified) {
                return MoboxService.QualifiedBack(model);
            }
            else {
                return MoboxService.UnqualifiedShift(model);
            }
        }
        ///// <summary>
        ///// 合格回库
        ///// </summary>
        ///// <returns></returns>
        //public SimpleResult QualifiedBack(QualifiedBackInfo model) {
        //    return new SimpleResult();
        //}
        ///// <summary>
        ///// 不合格移库
        ///// </summary>
        ///// <returns></returns>
        //public SimpleResult UnqualifiedShift() {
        //    return new SimpleResult();
        //}
        #endregion
        #region Mobox 接口
        /// <summary>
        /// 成品胶出库(WMS)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public SimpleResult FinishedOutbound(FinishedOutboundInfo model) {
            LogHelper.InfoApi("成品胶出库(WMS)", model);
            if (model.ForcedOut) {
                return MoboxService.FinishedOutboundForce(model);
            }
            return MoboxService.FinishedOutbound(model);
        [Route("shift-storage")]
        public SimpleResult CreateShiftOrder(CreateShiftOrderInfo model) {
            return MoboxService.CreateShiftOrder(model);
        }
        #endregion
    }
Dtos/Request/AgvRequest.cs
@@ -34,7 +34,7 @@
            /// 请求码
            /// </summary>
            public string apply_code { get; set; }
            public string TaskNo { set; get; }
            public string task_no { set; get; }
        }
    }
}
Dtos/Request/MoboxRequest.cs
@@ -1,42 +1,79 @@
using Newtonsoft.Json;
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
namespace HH.WCS.Mobox3.DSZSH.Dtos.Request {
    public class MoboxRequest {
        #region PDA 数据
        /// <summary>
        /// 成品胶好运箱下线信息
        /// 好运箱-满托下线入库(PDA)数据类
        /// </summary>
        public class GoodpackOfflineInfo : StartCntInfo {
            public string CgId { get; set; }
        public class GoodpackOfflineInfo {
            /// <summary>
            /// 物料编码
            /// </summary>
            [JsonProperty("s_item_code")]
            public string ItemCode { get; set; }
            ///// <summary>
            ///// 物料名称
            ///// </summary>
            //
            //[JsonProperty("item_name")]
            //public string ItemName { get; set; }
            /// <summary>
            /// 批次号
            /// </summary>
            [JsonProperty("s_batch_no")]
            public string BatchNo { get; set; }
            /// <summary>
            /// 物料规格
            /// </summary>
            [JsonProperty("s_spec")]
            public string Spec { get; set; }
            /// <summary>
            /// 数量
            /// </summary>
            [JsonProperty("n_num")]
            public int Qty { get; set; }
            /// <summary>
            /// 货位信息
            /// </summary>
            [JsonProperty("s_start_loc")]
            public string StartLoc { get; set; }
        }
        /// <summary>
        /// API:空托/空箱绑定数据类
        /// 空托/空箱入库绑定(PDA)数据类
        /// </summary>
        public class EmptyBindInfo {
            /// <summary>
            /// 容器编码
            /// </summary>
            [JsonProperty("s_cntr_code")]
            public string CntrCode { get; set; }
            /// <summary>
            /// 容器类型
            /// </summary>
            [JsonProperty("s_cntr_type")]
            public string CntrType { get; set; }
            /// <summary>
            /// 托盘数量(仅当容器类型为托盘时使用)
            /// </summary>
            [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
            public int PalletCount { get; set; }
            ///// <summary>
            ///// 托盘数量(仅当容器类型为托盘时使用)
            ///// </summary>
            //[JsonProperty("pallet_count", NullValueHandling = NullValueHandling.Ignore)]
            //public int PalletCount { get; set; }
            /// <summary>
            /// 货位编码
            /// </summary>
            [JsonProperty("s_loc_code")]
            public string LocCode { get; set; }
        }
        /// <summary>
        /// 空托绑定
        /// </summary>
        public class EmptyBindPalletInfo {
            /// <summary>
            /// 容器编码
@@ -53,7 +90,9 @@
            /// </summary>
            public string LocCode { get; set; }
        }
        /// <summary>
        /// 空箱绑定
        /// </summary>
        public class EmptyBindGoodpackInfo {
            /// <summary>
            /// 容器编码
@@ -65,48 +104,141 @@
            /// </summary>
            public string LocCode { get; set; }
        }
        /// <summary>
        /// 空托/空箱入库(PDA)数据类
        /// </summary>
        public class EmptyInboundInfo {
            /// <summary>
            /// 货位编码
            /// 容器编码
            /// </summary>
            public string LocCode { get; set; }
            [JsonProperty("cntr_code")]
            public string CntrCode { get; set; }
            /// <summary>
            /// 容器类型
            /// </summary>
            [JsonProperty("cntr_type")]
            public string CnteType { get; set; }
            /// <summary>
            /// 终点库区编码
            /// </summary>
            [JsonProperty("end_area")]
            public string EndArea { get; set; }
        }
        public class EmptyInboundDetailInfo : StartCntInfo { }
        public class EmptyOnlineInfo {
            public string ItemCode { get; set; }
            public string ItemName { get; set; }
            public string PatchNo { get; set; }
            public string EndLoc { get; set; }
        }
        //public class EmptyOnlineInfo {
        //    public string ItemCode { get; set; }
        //    public string ItemName { get; set; }
        //    public string PatchNo { get; set; }
        //    public string EndLoc { get; set; }
        //}
        public class EmptyOnlinePalletInfo {
            public string CntId { get; set; }
            public string EndLoc { get; set; }
        }
        public class EmptyOnlineGoodpackInfo : StartCntEndInfo { }
        public class CreateCheckOrderInfo {
            public string No { get; set; }
            public string CgId { get; set; }
            public string ItemName { get; set; }
            /// <summary>
            /// 物料编码
            /// </summary>
            [JsonProperty("item_code")]
            public string ItemCode { get; set; }
            ///// <summary>
            ///// 物料名称
            ///// </summary>
            //
            //[JsonProperty("item_name")]
            //public string ItemName { get; set; }
            /// <summary>
            /// 批次号
            /// </summary>
            [JsonProperty("batch_no")]
            public string BatchNo { get; set; }
            /// <summary>
            /// 抽检数量
            /// 物料规格
            /// </summary>
            public int N_QTY { get; set; }
            [JsonProperty("spe")]
            public string Spe { get; set; }
            /// <summary>
            /// 容器编码
            /// </summary>
            [JsonProperty("cnt_id")]
            public string CntId { get; set; }
            // 容器类型 = 托盘
        }
        public class EmptyOnlineGoodpackInfo {
            // 容器类型 = 好运箱
            /// <summary>
            /// 容器编码
            /// </summary>
            [JsonProperty("cnt_id")]
            public string CntId { get; set; }
        }
        /// <summary>
        /// 抽检-创建抽检单(WMS)数据类
        /// </summary>
        public class CreateCheckOrderInfo {
            /// <summary>
            /// 物料编码
            /// </summary>
            [JsonProperty("item_code")]
            public string ItemCode { get; set; }
            ///// <summary>
            ///// 物料名称
            ///// </summary>
            //
            //[JsonProperty("item_name")]
            //public string ItemName { get; set; }
            /// <summary>
            /// 物料规格
            /// </summary>
            [JsonProperty("spe")]
            public string Spe { get; set; }
            /// <summary>
            /// 批次号
            /// </summary>
            [JsonProperty("batch_no")]
            public string BatchNo { get; set; }
            /// <summary>
            /// 需出库数量
            /// </summary>
            [JsonProperty("qty")]
            public int Qty { get; set; }
            /// <summary>
            /// 容器类型
            /// </summary>
            [JsonProperty("cntr_type")]
            public string CntrType { get; set; }
            /// <summary>
            /// 出库终点货区
            /// </summary>
            [JsonProperty("end_area")]
            public string EndArea { get; set; }
        }
        public class QualifiedBackInfo {
            /// <summary>
            /// 物料编码
            /// </summary>
            [JsonProperty("item_code")]
            public string ItemCode { get; set; }
            /// <summary>
            /// 容器编码
            /// </summary>
            [JsonProperty("cntr_code")]
            public string CntrCode { get; set; }
        }
        public class UnqualifiedShiftInfo : QualifiedBackInfo {
            /// <summary>
            /// 不合格移库终点库区
            /// </summary>
            [JsonProperty("end_area")]
            public string EndArea { get; set; }
        }
@@ -118,49 +250,102 @@
        #region WMS 数据
        /// <summary>
        /// 成品胶托盘出库信息
        /// 成品胶出库(PDA)
        /// </summary>
        public class FinishedOutboundInfo {
            //public string No { get; set; }
            /// <summary>
            /// 物料编码
            /// </summary>
            [JsonProperty("item_code")]
            public string ItemCode { get; set; }
            public string ItemName { get; set; }
            ///// <summary>
            ///// 物料名称
            ///// </summary>
            //
            //[JsonProperty("item_name")]
            //public string ItemName { get; set; }
            /// <summary>
            /// 物料规格
            /// </summary>
            [JsonProperty("spe")]
            public string Spe { get; set; }
            /// <summary>
            /// 批次号
            /// </summary>
            [JsonProperty("batch_no")]
            public string BatchNo { get; set; }
            /// <summary>
            /// 需出库数量
            /// </summary>
            [JsonProperty("qty")]
            public int Qty { get; set; }
            /// <summary>
            /// 容器类型
            /// </summary>
            [JsonProperty("cntr_type")]
            public string CntrType { get; set; }
            public float Qty { get; set; }
            /// <summary>
            /// 出库终点货区
            /// </summary>
            [JsonProperty("end_area")]
            public string EndArea { get; set; }
            /// <summary>
            /// 是否强制出库
            /// </summary>
            [JsonProperty("force_out")]
            public bool ForcedOut { get; set; }
        }
        /// <summary>
        /// 移库-创建移库任务数据类
        /// </summary>
        public class CreateShiftOrderInfo {
            /// <summary>
            /// 物料编码
            /// </summary>
            [JsonProperty("item_code")]
            public string ItemCode { get; set; }
            ///// <summary>
            ///// 物料名称
            ///// </summary>
            //
            //[JsonProperty("item_name")]
            //public string ItemName { get; set; }
            /// <summary>
            /// 批次号
            /// </summary>
            [JsonProperty("batch_no")]
            public string BatchNo { get; set; }
            /// <summary>
            /// 移库终点货区
            /// </summary>
            [JsonProperty("end_area")]
            public string EndArea { get; set; }
        }
        /// <summary>
        /// 余料尾箱回库(PDA)数据类
        /// </summary>
        public class RestBackInfo {
            /// <summary>
            /// 起点货位
            /// </summary>
            [JsonProperty("start_loc")]
            public string StartLoc { get; set; }
        }
        #endregion
        //------------------------------------------------------------------------------
        #region 模板数据
        /// <summary>
        /// 模板抽象数据类:起点货位、容器号、终点货位
        /// </summary>
        public abstract class StartCntEndInfo {
            /// <summary>
            /// 起点货位
            /// </summary>
            public string StartLoc { get; set; }
            /// <summary>
            /// 容器号
            /// </summary>
            public string CntId { get; set; }
            /// <summary>
            /// 终点货位
            /// </summary>
            public string EndLoc { get; set; }
        }
        /// <summary>
        /// 模板抽象数据类:起点货位、容器号
        /// </summary>
Dtos/Response/MoboxResponse.cs
@@ -9,9 +9,10 @@
namespace HH.WCS.Mobox3.DSZSH.Dtos.Response {
    public class MoboxResponse {
        /// <summary>
        /// Mobox 接口返回
        /// Mobox 接口返回数据类
        /// </summary>
        public class SimpleResult {
            [JsonProperty("resultCode")]
            public int Code { get; set; }
HH.WCS.Mobox3.DSZSH.csproj
@@ -53,6 +53,10 @@
  <PropertyGroup>
    <StartupObject>HH.WCS.Mobox3.DSZSH.Program</StartupObject>
  </PropertyGroup>
  <PropertyGroup>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
    <NoWarn>$(NoWarn);1591</NoWarn>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="BouncyCastle.Crypto, Version=1.9.0.0, Culture=neutral, PublicKeyToken=0e99375e54769942, processorArchitecture=MSIL">
      <HintPath>packages\Portable.BouncyCastle.1.9.0\lib\net40\BouncyCastle.Crypto.dll</HintPath>
@@ -129,11 +133,15 @@
    <Reference Include="Swashbuckle.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cd1bb07a5ac7c7bc, processorArchitecture=MSIL">
      <HintPath>..\packages\Swashbuckle.Core.5.6.0\lib\net40\Swashbuckle.Core.dll</HintPath>
    </Reference>
    <Reference Include="Swashbuckle.Examples, Version=4.1.0.0, Culture=neutral, PublicKeyToken=aa1e9c5053bfbe95, processorArchitecture=MSIL">
      <HintPath>packages\Swashbuckle.Examples.4.1.0\lib\net40\Swashbuckle.Examples.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
      <HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
    </Reference>
    <Reference Include="System.ComponentModel.Composition" />
    <Reference Include="System.ComponentModel.DataAnnotations" />
    <Reference Include="System.Configuration" />
    <Reference Include="System.Configuration.Install" />
    <Reference Include="System.Core" />
@@ -216,7 +224,8 @@
    </Reference>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="AppStart\Config.cs" />
    <Compile Include="App_Start\Config.cs" />
    <Compile Include="App_Start\SwaggerConfig.cs" />
    <Compile Include="Consts\AgvStateCode.cs" />
    <Compile Include="Consts\AgvStateName.cs" />
    <Compile Include="Consts\AreaName.cs" />
@@ -228,7 +237,7 @@
    <Compile Include="Controllers\ErpController.cs" />
    <Compile Include="Controllers\MoboxController.cs" />
    <Compile Include="Controllers\AgvController.cs" />
    <Compile Include="AppStart\SwaggerControllerDescProvider.cs" />
    <Compile Include="App_Start\SwaggerControllerDescProvider.cs" />
    <Compile Include="Models\DebugModel.cs" />
    <Compile Include="Dtos\Request\DebugRequest.cs" />
    <Compile Include="Dtos\Response\DebugResponse.cs" />
@@ -244,8 +253,11 @@
    <Compile Include="Models\TN_Order_Task.cs" />
    <Compile Include="Models\TN_Outbound_Order.cs" />
    <Compile Include="Models\TN_Outbound_Detail.cs" />
    <Compile Include="Models\TN_Shift_Detail.cs" />
    <Compile Include="Models\TN_Shift_Order.cs" />
    <Compile Include="ServiceCore\CheckCore.cs" />
    <Compile Include="ServiceCore\OutboundCore.cs" />
    <Compile Include="ServiceCore\ShiftCore.cs" />
    <Compile Include="Services\DebugService.cs" />
    <Compile Include="Devices\ProductionLineDevice.cs" />
    <Compile Include="Devices\ModbusHelper.cs" />
@@ -285,8 +297,8 @@
    <Compile Include="Helpers\SysHelper.cs" />
    <Compile Include="Program.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="AppStart\Settings.cs" />
    <Compile Include="AppStart\Startup.cs" />
    <Compile Include="App_Start\Settings.cs" />
    <Compile Include="App_Start\Startup.cs" />
    <Compile Include="Dispatch\NDCHelper.cs" />
    <Compile Include="Helpers\HttpHelper.cs" />
  </ItemGroup>
Models/TN_CG_Detail.cs
@@ -17,6 +17,11 @@
        public string S_ITEM_CODE { get; set; }
        /// <summary>
        /// 物料规格
        /// </summary>
        public string S_SPE { get; set; }
        /// <summary>
        /// 容器编码
        /// </summary>
        public string S_CNTR_CODE { get; set; }
@@ -39,6 +44,6 @@
        /// <summary>
        /// 货品数量
        /// </summary>
        public float F_QTY { get; set; }
        public int N_QTY { get; set; }
    }
}
Models/TN_Check_Detail.cs
@@ -14,8 +14,9 @@
    public class TN_Check_Detail : BaseModel {
        public string S_NO { get; set; }
        public string S_CG_ID { get; set; }
        public string S_ITEM_NAME { get; set; }
        public string S_ITEM_CODE { get; set; }
        //public string S_ITEM_NAME { get; set; }
        public string S_BATCH_NO { get; set; }
        public string S_END_AREA { get; set; }
        /// <summary>
Models/TN_Check_Order.cs
@@ -13,7 +13,7 @@
    [SugarTable("TN_Check_Order")]
    public class TN_Check_Order : BaseModel {
        public string S_NO { get; set; }
        public string S_CG_ID { get; set; }
        public string S_ITEM_CODE { get; set; }
        public string S_ITEM_NAME { get; set; }
        public string S_BATCH_NO { get; set; }
        public int N_COUNT { get; set; }
Models/TN_Shift_Detail.cs
New file
@@ -0,0 +1,25 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HH.WCS.Mobox3.DSZSH.Models {
    internal class TN_Shift_Detail : BaseModel {
        public string S_NO { get; set; }
        public string S_ITEM_CODE { get; set; }
        public string S_ITEM_NAME { get; set; }
        public string S_LOC_CODE { get; set; }
        public string S_CNTR_CODE { get; set; }
        public string S_BATCH_NO { get; set; }
        public string S_END_AREA { get; set; }
        /// <summary>
        /// 业务状态:0等待执行 1已执行待生成任务 2任务执行中 3任务完成
        /// </summary>
        public int N_B_STATE { get; set; } = 1; // 创建即执行
    }
}
Models/TN_Shift_Order.cs
New file
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HH.WCS.Mobox3.DSZSH.Models {
    /// <summary>
    /// 移库单
    /// </summary>
    public class TN_Shift_Order : BaseModel {
        public string S_NO { get; set; }
        public string S_ITEM_CODE { get; set; }
        public string S_ITEM_NAME { get; set; }
        public string S_BATCH_NO { get; set; }
        public int N_COUNT { get; set; }
        public string S_END_AREA { get; set; }
        /// <summary>
        /// 业务状态:0等待执行 1已执行待生成任务 2任务执行中 3任务完成
        /// </summary>
        public int N_B_STATE { get; set; } = 0; // 创建后需要确认执行
    }
}
ServiceCore/CheckCore.cs
@@ -30,7 +30,7 @@
                foreach (var detail in detailList) {
                    var startLocCntrRel = db.Queryable<TN_Location, TN_Loc_Container, TN_CG_Detail>
                        ((l, c, d) => l.S_CODE == c.S_LOC_CODE && c.S_CNTR_CODE == d.S_CNTR_CODE)
                        .Where((l, c, d) => d.S_ITEM_CODE == detail.S_CG_ID && d.S_BATCH_NO == detail.S_BATCH_NO)
                        .Where((l, c, d) => d.S_ITEM_CODE == detail.S_ITEM_CODE && d.S_BATCH_NO == detail.S_BATCH_NO)
                        .Select((l, c) => c) // 选择 LocCntrRel
                        .First(); // 随机抽检,不排序
ServiceCore/ShiftCore.cs
New file
@@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HH.WCS.Mobox3.DSZSH.Consts;
using HH.WCS.Mobox3.DSZSH.Helpers;
using HH.WCS.Mobox3.DSZSH.Models;
namespace HH.WCS.Mobox3.DSZSH.ServiceCore {
    /// <summary>
    /// 移库业务核心
    /// </summary>
    internal class ShiftCore {
        public static void CheckOrderState() {
            var db = DbHelper.GetDbClient();
            // 目前按出库的逻辑:逐个移库
            try {
                var order = db.Queryable<TN_Shift_Order>()
                    .Where(c => c.N_B_STATE == SpotStateCode.已执行待生成任务)
                    .OrderBy(c => c.T_CREATE, SqlSugar.OrderByType.Asc).First();
                if (order == null) {
                    LogHelper.Info("轮询:暂无待执行的移库单");
                    return;
                }
                if (db.Queryable<TN_Shift_Detail>()
                    .First(d => d.S_NO == order.S_NO && d.N_B_STATE == SpotStateCode.任务执行中) != null) {
                    LogHelper.Debug("轮询:上一个移库任务仍在进行中");
                    return;
                }
                var detail = db.Queryable<TN_Shift_Detail>()
                    .Where(d => d.S_NO == order.S_NO && d.N_B_STATE == SpotStateCode.已执行待生成任务)
                    .OrderBy(d => d.T_CREATE, SqlSugar.OrderByType.Asc).First();
                if (detail == null) {
                    //order.N_B_STATE = SpotStateCode.任务执行中;
                    LogHelper.Info("轮询移库:暂无待执行的任务");
                    return;
                }
                var startLocCntrRel = db.Queryable<TN_Loc_Container>()
                    .Where(c => c.S_CNTR_CODE == detail.S_CNTR_CODE)
                    .First();
                if (startLocCntrRel == null) {
                    LogHelper.Info("轮询:移库:没有找到合适的起点货位");
                    return;
                }
                var endLoc = db.Queryable<TN_Location>()
                    .Where(l => l.S_AREA_CODE == detail.S_END_AREA)
                    .Where(ExprHelper.LocIsFree)
                    .Where(ExprHelper.LocIsEmpty).First();
                if (endLoc == null) {
                    LogHelper.Info("轮询:出库:没有找到合适的终点货位");
                    return;
                }
                using (var tran = db.Ado.UseTran()) {
                    detail.N_B_STATE = SpotStateCode.任务执行中;
                    if (db.Updateable<TN_Shift_Detail>(detail).UpdateColumns(it => it.N_B_STATE).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        LogHelper.Info($"轮询:出库:更新失败:修改明细表状态为完成");
                    }
                    if (TaskHelper.LogCreateTask(startLocCntrRel.S_LOC_CODE, startLocCntrRel.S_CNTR_CODE,
                        endLoc.S_CODE, TaskName.成品胶出库)) {
                        tran.CommitTran();
                    }
                    else {
                        tran.RollbackTran();
                    }
                }
            }
            catch (Exception ex) {
                LogHelper.InfoEx(ex);
            }
        }
        public static void UpdateTaskState(int spotStateCode) {
            var db = DbHelper.GetDbClient();
            var detail = db.Queryable<TN_Shift_Detail>()
                .First(d => d.N_B_STATE == SpotStateCode.任务执行中);
            if (detail == null) {
                LogHelper.Info("当前没有执行中的移库单明细项目");
                return;
            }
            using (var tran = db.Ado.UseTran()) {
                detail.N_B_STATE = spotStateCode;
                db.Updateable<TN_Shift_Detail>(detail).UpdateColumns(it => it.N_B_STATE).ExecuteCommand();
                if (db.Queryable<TN_Shift_Detail>().Count(d => d.S_NO == detail.S_NO && d.N_B_STATE == 2) ==
                    db.Queryable<TN_Shift_Detail>().Count(d => d.S_NO == detail.S_NO)) {
                    if (db.Updateable<TN_Shift_Order>().SetColumns(it => it.N_B_STATE == SpotStateCode.任务执行完成)
                        .Where(it => it.S_NO == detail.S_NO)
                        .ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        LogHelper.Info("修改Order状态错误");
                    }
                }
                tran.CommitTran();
            }
        }
    }
}
Services/AgvService.cs
@@ -19,8 +19,8 @@
            try {
                switch (model.state) {
                    case 1023:
                        break;
                    case 1025:
                        break;
                    case 1012:
                        break;
@@ -117,9 +117,7 @@
            return true;
        }
        public static ReturnResult SafetyInteraction(SafetyInteractionInfo model) {
            var gzResult = new ReturnResult();
            var db = DbHelper.GetDbClient();
@@ -134,9 +132,9 @@
                    LogHelper.Info("加载设备信息失败");
                }
                var tn_task = db.Queryable<TN_Task>().First(a => a.S_CODE == model.TaskNo);
                var tn_task = db.Queryable<TN_Task>().First(a => a.S_CODE == model.task_no);
                if (tn_task == null) {
                    LogHelper.Info($"任务号 '{model.TaskNo}' 不存在");
                    LogHelper.Info($"任务号 '{model.task_no}' 不存在");
                }
                // 待修改:补充不同分支AGV判断
Services/MoboxService.cs
@@ -18,45 +18,6 @@
namespace HH.WCS.Mobox3.DSZSH.Services {
    public class MoboxService {
        #region 模板功能
        public static SimpleResult BaseBound(StartCntEndInfo model) {
            var taskName = ""; // 用于生成任务类型、打印日志信息
            var db = DbHelper.GetDbClient();
            try {
                var startLoc = db.Queryable<TN_Location>()
                    .Where(ExprHelper.LocCode(model.StartLoc))
                    .Where(ExprHelper.LocIsFree)
                    .Where(ExprHelper.LocIsEmpty.Negate()).First();
                if (startLoc == null) {
                    return BuildSimpleResult(2, $"起点位置 '{model.StartLoc}' 不存在或不具备取货要求");
                }
                var locCntrRel = db.Queryable<TN_Loc_Container>()
                    .Where(ExprHelper.LocCntrIsBind(startLoc.S_CODE, model.CntId)).First();
                if (locCntrRel == null) {
                    return BuildSimpleResult(3, $"起点位置 '{startLoc.S_CODE}' 没有绑定容器 '{model.CntId}'");
                }
                var endLoc = db.Queryable<TN_Location>()
                    .Where(ExprHelper.LocCode(model.EndLoc))
                    .Where(ExprHelper.LocIsFree)
                    .Where(ExprHelper.LocIsEmpty).First();
                if (endLoc == null) {
                    return BuildSimpleResult(4, $"终点位置 '{model.EndLoc}' 不存在或不具备放货要求");
                }
                return TaskHelper.SimpleCreateTask(startLoc.S_CODE, locCntrRel.S_CNTR_CODE, endLoc.S_CODE, taskName);
            }
            catch (Exception ex) {
                return BuildSimpleEx(ex);
            }
        }
        #endregion
        #region PDA 功能
        /// <summary>
        /// 好运箱-满托下线入库(PDA)
@@ -82,10 +43,9 @@
                // 查看容器与起点货位是否绑定
                var locCntrRel = db.Queryable<TN_Loc_Container>().First(
                    a => a.S_LOC_CODE == model.StartLoc
                    && a.S_CNTR_CODE == model.CntId);
                    a => a.S_LOC_CODE == startLoc.S_CODE);
                if (locCntrRel == null) {
                    return BuildSimpleResult(3, $"起点位置 '{model.StartLoc}' 没有绑定容器 '{model.CntId}' ,无可出库的物料");
                    return BuildSimpleResult(3, $"起点位置 '{model.StartLoc}' 无可出库的物料");
                }
                // TODO 满箱入库算法待优化
@@ -133,7 +93,7 @@
                    return BuildSimpleResult(3, $"当前容器 '{model.CntrCode}' 已经与 '{locCntrRel.S_LOC_CODE}' 绑定");
                }
                loc.N_CURRENT_NUM = model.PalletCount;
                //loc.N_CURRENT_NUM = model.PalletCount;
                locCntrRel = new TN_Loc_Container {
                    S_LOC_CODE = model.LocCode,
@@ -325,13 +285,13 @@
                }
                var endLoc = db.Queryable<TN_Location>()
                    .Where(ExprHelper.LocCode(model.EndLoc))
                    .Where(ExprHelper.LocBelongsToArea(AreaName.包装区))
                    .Where(l => taskInfo.EndAreas.Contains(l.S_AREA_CODE))
                    .Where(ExprHelper.LocIsFree)
                    .Where(ExprHelper.LocIsEmpty).First();
                if (endLoc == null) {
                    return BuildSimpleResult(4, $"终点位置 '{model.EndLoc}' 不存在或不具备放货要求");
                    return BuildSimpleResult(4, $"终点位置 不存在或不具备放货要求");
                }
                return TaskHelper.SimpleCreateTask(startLoc.S_CODE, locCntrRel.S_CNTR_CODE, endLoc.S_CODE, taskName);
@@ -371,13 +331,13 @@
                }
                var endLoc = db.Queryable<TN_Location>()
                    .Where(ExprHelper.LocCode(model.EndLoc))
                    .Where(ExprHelper.LocBelongsToArea(AreaName.包装区))
                    .Where(l => taskInfo.EndAreas.Contains(l.S_AREA_CODE))
                    .Where(ExprHelper.LocIsFree)
                    .Where(ExprHelper.LocIsEmpty).First();
                if (endLoc == null) {
                    return BuildSimpleResult(4, $"终点位置 '{model.EndLoc}' 不存在或不具备放货要求");
                    return BuildSimpleResult(4, $"终点位置 不存在或不具备放货要求");
                }
                return TaskHelper.SimpleCreateTask(startLoc.S_CODE, locCntrRel.S_CNTR_CODE, endLoc.S_CODE, taskName);
@@ -399,11 +359,11 @@
                using (var tran = db.Ado.UseTran()) {
                    var order = new TN_Check_Order {
                        S_NO = model.No,
                        S_CG_ID = model.CgId,
                        S_ITEM_NAME = model.ItemName,
                        S_NO = GenerateOrderNo("抽检单号", "CN"),
                        S_ITEM_CODE = model.ItemCode,
                        //S_ITEM_NAME = model.ItemName,
                        S_BATCH_NO = model.BatchNo,
                        N_COUNT = model.N_QTY,
                        N_COUNT = model.Qty,
                        S_END_AREA = model.EndArea,
                    };
@@ -412,10 +372,10 @@
                        return BuildSimpleResult(2, "生成 抽检单 失败:" + JsonConvert.SerializeObject(order));
                    }
                    for (int i = 0; i < model.N_QTY; i++) {
                    for (int i = 0; i < model.Qty; i++) {
                        var detail = new TN_Check_Detail {
                            S_NO = model.No,
                            S_CG_ID = model.CgId,
                            S_NO = order.S_NO,
                            S_ITEM_CODE = model.ItemCode,
                            S_BATCH_NO = model.BatchNo,
                            S_END_AREA = model.EndArea
                        };
@@ -428,7 +388,7 @@
                    tran.CommitTran();
                }
                return BuildSimpleResult(0, $"创建 抽检单 成功:单号 {model.No}");
                return BuildSimpleResult(0, $"创建 抽检单 成功");
            }
            catch (Exception ex) {
                return BuildSimpleEx(ex);
@@ -437,6 +397,7 @@
        public static SimpleResult QualifiedBack(QualifiedBackInfo model) {
            var db = DbHelper.GetDbClient();
            try {
                var cgDetail = db.Queryable<TN_CG_Detail>()
@@ -549,6 +510,34 @@
            }
        }
        public static SimpleResult RestBack(RestBackInfo model) {
            var db = DbHelper.GetDbClient();
            try {
                var locCntrRel = db.Queryable<TN_Loc_Container>()
                    .Where(c => c.S_LOC_CODE == model.StartLoc)
                    .First();
                if (locCntrRel == null) {
                    return BuildSimpleResult(2, $"起点货位 {model.StartLoc}");
                }
                var endLoc = db.Queryable<TN_Location, TN_Loc_Container>((l, c) => l.S_CODE == c.S_LOC_CODE)
                    .Where((l, c) => c.S_CNTR_TYPE == locCntrRel.S_CNTR_TYPE)
                    .First();
                if (endLoc == null) {
                    return BuildSimpleResult(3, $"不存在合适的终点货位可以回库");
                }
                return TaskHelper.SimpleCreateTask(locCntrRel.S_LOC_CODE, locCntrRel.S_CNTR_CODE, endLoc.S_CODE, TaskName.尾箱回库);
            }
            catch (Exception ex) {
                return BuildSimpleEx(ex);
            }
        }
        #endregion
        #region Mobox 功能
@@ -598,7 +587,7 @@
                        S_ITEM_CODE = model.ItemCode,
                        S_BATCH_NO = model.BatchNo,
                        F_QTY = model.Qty,
                        F_OUT_QTY = cgDetailList.Sum(a => a.F_QTY),
                        F_OUT_QTY = cgDetailList.Sum(a => a.N_QTY),
                        S_END_AREA = model.EndArea
                    };
@@ -659,7 +648,7 @@
                        S_ITEM_CODE = model.ItemCode,
                        S_BATCH_NO = model.BatchNo,
                        F_QTY = model.Qty,
                        F_OUT_QTY = cgDetailList.Sum(a => a.F_QTY),
                        F_OUT_QTY = cgDetailList.Sum(a => a.N_QTY),
                        S_END_AREA = model.EndArea
                    };
@@ -685,8 +674,8 @@
            try {
                var targetCg = db.Queryable<TN_CG_Detail>().Where(a => a.S_ITEM_CODE == model.ItemCode
                && a.F_QTY > targetNum).
                OrderBy(a => a.F_QTY, OrderByType.Asc).First();
                && a.N_QTY > targetNum).
                OrderBy(a => a.N_QTY, OrderByType.Asc).First();
                if (targetCg != null) //刚好有一行满足条件
                {
                    result.Add(targetCg);
@@ -695,12 +684,12 @@
                // NOTE 根据总量选detail时,是否需要考虑货位的高低?
                var sortedMaterials = db.Queryable<TN_CG_Detail, TN_Loc_Container>((d, c) => d.S_CNTR_CODE == c.S_CNTR_CODE)
                    .Where(d => d.S_ITEM_CODE == model.ItemCode && d.S_BATCH_NO == model.BatchNo && d.F_QTY > 0)
                    .Where(d => d.S_ITEM_CODE == model.ItemCode && d.S_BATCH_NO == model.BatchNo && d.N_QTY > 0)
                    .Where((d, c) => c.S_CNTR_TYPE == model.CntrType)
                    .Where(d => (model.ForcedOut && d.N_ITEM_STATE == 0 && d.S_ITEM_STATE == "合格")
                        || (!model.ForcedOut && (d.N_ITEM_STATE == 0 && d.S_ITEM_STATE == "合格"
                        || d.N_ITEM_STATE == 1 && d.S_ITEM_STATE == "待检")))
                    .OrderBy(d => d.F_QTY, OrderByType.Desc)
                    .OrderBy(d => d.N_QTY, OrderByType.Desc)
                    .OrderBy(d => d.N_ITEM_STATE, OrderByType.Asc).ToList();
                if (sortedMaterials.Count == 0)//没有满足条件的
@@ -710,13 +699,13 @@
                float countNum = 0;
                foreach (var mat in sortedMaterials) {
                    countNum += mat.F_QTY;
                    countNum += mat.N_QTY;
                    result.Add(mat);
                    if (countNum >= targetNum) {
                        break;
                    }
                }
                if (result.Sum(a => a.F_QTY) >= targetNum) {
                if (result.Sum(a => a.N_QTY) >= targetNum) {
                    return result;
                }
                else {
@@ -729,6 +718,64 @@
            }
        }
        /// <summary>
        /// 移库-创建移库任务(WMS)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public static SimpleResult CreateShiftOrder(CreateShiftOrderInfo model) {
            var db = DbHelper.GetDbClient();
            try {
                var locCntrRelList = db.Queryable<TN_Loc_Container, TN_CG_Detail>((c, d) => c.S_CNTR_CODE == d.S_CNTR_CODE)
                    .Where((c, d) => d.S_ITEM_CODE == model.ItemCode && d.S_BATCH_NO == model.ItemCode)
                    .ToList();
                if (locCntrRelList.Count == 0) {
                    return BuildSimpleResult(2, "没有找到待移库的物料");
                }
                // 绑定操作:插入出库单、所有的出库单明细
                using (var tran = db.Ado.UseTran()) {
                    var order = new TN_Shift_Order {
                        S_NO = GenerateOrderNo("移库单号", "SN"),
                        S_ITEM_CODE = model.ItemCode,
                        //S_ITEM_NAME = model.ItemName,
                        S_BATCH_NO = model.BatchNo,
                        N_COUNT = locCntrRelList.Count,
                        S_END_AREA = model.EndArea,
                    };
                    if (db.Insertable<TN_Shift_Order>(order).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return BuildSimpleResult(2, "生成 移库单 失败:" + JsonConvert.SerializeObject(order));
                    }
                    foreach (var locCntrRel in locCntrRelList) {
                        var detail = new TN_Shift_Detail {
                            S_NO = order.S_NO,
                            S_ITEM_CODE = model.ItemCode,
                            S_LOC_CODE = locCntrRel.S_LOC_CODE,
                            S_CNTR_CODE = locCntrRel.S_CNTR_CODE,
                            S_BATCH_NO = model.BatchNo,
                            S_END_AREA = model.EndArea
                        };
                        if (db.Insertable<TN_Shift_Detail>(detail).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            return BuildSimpleResult(3, "生成 移库单明细 失败:" + JsonConvert.SerializeObject(detail));
                        }
                    }
                    tran.CommitTran();
                }
                return BuildSimpleResult(0, $"创建 移库单 成功");
            }
            catch (Exception ex) {
                return BuildSimpleEx(ex);
            }
        }
        private static string GenerateOrderNo(string snType, string prefix) {
            var id = SysHelper.GetSerialNumber(snType, prefix);
            var date = DateTime.Now.ToString("yyMMdd");
config/config.json
@@ -1,5 +1,5 @@
{
    "WebApiUrl": "http://127.0.0.1:8901/",
    "WebApiUrl": "http://192.168.1.141:8901/",
    "NdcApiUrl": "http://127.0.0.1:5201/api/order/",
    "SqlServer": "Data Source=(local);Initial Catalog=DSZSHTest;User ID=sa;Password=123456;",
    "TcpServerIp": "127.0.0.1",
packages.config
@@ -33,6 +33,7 @@
  <package id="SqlSugar" version="5.1.4.169" targetFramework="net462" />
  <package id="Swashbuckle" version="5.6.0" targetFramework="net461" />
  <package id="Swashbuckle.Core" version="5.6.0" targetFramework="net461" />
  <package id="Swashbuckle.Examples" version="4.1.0" targetFramework="net462" />
  <package id="System.Buffers" version="4.5.1" targetFramework="net461" />
  <package id="System.Diagnostics.DiagnosticSource" version="6.0.1" targetFramework="net462" />
  <package id="System.Formats.Asn1" version="8.0.1" targetFramework="net462" />
swagger.js
@@ -40,6 +40,27 @@
            }
        });
    },
    setModelDescriptions: function() {
        $.ajax({
            type: "get",
            url: $("#input_baseUrl").val(),
            dataType: "json",
            success: function(data) {
                var modelDesc = data.ModelDesc;
                // 在模型展示区域添加描述
                $(".model-box").each(function() {
                    var modelName = $(this).find(".model-title").text();
                    if (modelDesc[modelName]) {
                        $(this).find(".model-description").remove();
                        $(this).append(
                            `<div class="model-description">${modelDesc[modelName]}</div>`
                        );
                    }
                });
            }
        });
    },
    _tryTranslate: function (word)
    {
        return this._words[$.trim(word)] !== undefined ? this._words[$.trim(word)] : word;