kazelee
2025-05-21 5ad394cf1708a4629f90f40bfd9b48d9a9f6f0c8
测试出库抽检流程,重构代码修复事务处理的问题
19 文件已重命名
13个文件已添加
27个文件已修改
1 文件已复制
33个文件已删除
7901 ■■■■ 已修改文件
.vs/HH.WCS.Mobox3.DSZSH/FileContentIndex/3f7a8a50-7623-4582-a788-1727f4b734e0.vsidx 补丁 | 查看 | 原始文档 | blame | 历史
.vs/HH.WCS.Mobox3.DSZSH/FileContentIndex/74bd051a-76c6-4faf-9203-3846175eb944.vsidx 补丁 | 查看 | 原始文档 | blame | 历史
.vs/HH.WCS.Mobox3.DSZSH/FileContentIndex/79fc4010-d1c2-42bb-9f8f-a7b972ec34f6.vsidx 补丁 | 查看 | 原始文档 | blame | 历史
.vs/HH.WCS.Mobox3.DSZSH/FileContentIndex/cfeb14b9-8063-424f-8831-ddd93cd9c6f5.vsidx 补丁 | 查看 | 原始文档 | blame | 历史
App_Start/Config.cs 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
App_Start/Settings.cs 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
App_Start/SwaggerConfig.cs 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
App_Start/SwaggerControllerDescProvider.cs 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Consts/AgvStateCode.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Consts/AgvStateName.cs 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Consts/AreaName.cs 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Consts/LockStateCode.cs 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Consts/LockStateName.cs 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Consts/SpotStateCode.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Consts/TaskName.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Controllers/DebugController.cs 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Dispatch/GZRobot.cs 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Dispatch/HanAo.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Dispatch/NDC.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Dispatch/NDCApi.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Dispatch/NDCHelper.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Dtos/Request/AgvRequest.cs 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Dtos/Request/DebugRequest.cs 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Dtos/Request/MoboxRequest.cs 362 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Dtos/Request/WmsRequest.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Dtos/Response/AgvResponse.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Dtos/Response/DebugResponse.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Dtos/Response/MoboxResponse.cs 245 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Dtos/Response/WmsResponse.cs 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HH.WCS.Mobox3.DSZSH.csproj 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Helpers/AgvHelper.cs 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Helpers/DbHelper.cs 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Helpers/ExprHelper.cs 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Helpers/PathHelper.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Helpers/TaskHelper.cs 381 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/BaseModel.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/DebugModel.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_CAR_IN.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_CG_Detail.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_Check_Detail.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_Check_Order.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_Container.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_Container_Item.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_Inbound_Order.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_Loc_Container.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_Location.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_Order_Task.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_Outbound_Detail.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_Outbound_Order.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_Shift_Detail.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_Shift_Order.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_Task.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_Task_Action.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Models/TN_WorkOrder.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Program.cs 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServiceCore/CheckCore.cs 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServiceCore/OfflineCore.cs 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServiceCore/OutboundCore.cs 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServiceCore/ShiftCore.cs 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServiceCore/TaskCore.cs 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Services/AgvService.cs 159 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Services/DebugService.cs 235 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Services/MoboxService.cs 884 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Services/WmsService.cs 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/AgvController.cs 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/ApiHelper.cs 1330 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/ApiModel.cs 573 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/DebugController.cs 216 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/ErpController.cs 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/MesController.cs 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/MoboxController.cs 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/WMSController.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
core/Monitor.cs 395 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
core/WCSCore.cs 284 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
core/WMSCore.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/ModbusHelper.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/OpcUaHelper.cs 补丁 | 查看 | 原始文档 | blame | 历史
device/PlcHelper.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/ProductionLineDevice.cs 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/S7Helper.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/TcpClient.cs 补丁 | 查看 | 原始文档 | blame | 历史
device/TcpServer.cs 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
process/DeviceProcess.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
process/TaskProcess.cs 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
util/HttpHelper.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
util/LogHelper.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
util/Settings.cs 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
util/SqlHelper.cs 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/ContainerHelper.cs 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/LocationHelper.cs 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/SYSHelper.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/WCSHelper.cs 224 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/WMSHelper.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.vs/HH.WCS.Mobox3.DSZSH/FileContentIndex/3f7a8a50-7623-4582-a788-1727f4b734e0.vsidx
Binary files differ
.vs/HH.WCS.Mobox3.DSZSH/FileContentIndex/74bd051a-76c6-4faf-9203-3846175eb944.vsidx
Binary files differ
.vs/HH.WCS.Mobox3.DSZSH/FileContentIndex/79fc4010-d1c2-42bb-9f8f-a7b972ec34f6.vsidx
Binary files differ
.vs/HH.WCS.Mobox3.DSZSH/FileContentIndex/cfeb14b9-8063-424f-8831-ddd93cd9c6f5.vsidx
Binary files differ
App_Start/Config.cs
File was deleted
App_Start/Settings.cs
File was deleted
App_Start/SwaggerConfig.cs
@@ -1,49 +1,14 @@
using System.Web.Http;
using WebActivatorEx;
using HH.WCS.Mobox3.DSZSH;
using HH.WCS.Mobox3.Template;
using Swashbuckle.Application;
using System.IO;
using System.Reflection;
using System;
using Swashbuckle.Examples;
[assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")]
namespace HH.WCS.Mobox3.DSZSH
namespace HH.WCS.Mobox3.Template
{
    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;
@@ -67,7 +32,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.DSZSH");
                        c.SingleApiVersion("v1", "HH.WCS.Mobox3.Template");
                        // If you want the output Swagger docs to be indented properly, enable the "PrettyPrint" option.
                        //
@@ -96,7 +61,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")
@@ -137,10 +102,6 @@
                        // more Xml comment files.
                        //
                        //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.
App_Start/SwaggerControllerDescProvider.cs
@@ -80,158 +80,4 @@
            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/AgvStateCode.cs
File was deleted
Consts/AgvStateName.cs
File was deleted
Consts/AreaName.cs
File was deleted
Consts/LockStateCode.cs
File was deleted
Consts/LockStateName.cs
File was deleted
Consts/SpotStateCode.cs
File was deleted
Consts/TaskName.cs
File was deleted
Controllers/DebugController.cs
File was deleted
Dispatch/GZRobot.cs
@@ -1,18 +1,15 @@
using HH.WCS.Mobox3.DSZSH.device;
using HH.WCS.Mobox3.DSZSH.ServiceCore;
using HH.WCS.Mobox3.DSZSH.AppStart;
using HH.WCS.Mobox3.DSZSH.core;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Web.Caching;
using HH.WCS.Mobox3.DSZSH.Helpers;
using HH.WCS.Mobox3.DSZSH.Devices;
using HH.WCS.Mobox3.DSZSH.Models;
using HH.WCS.Mobox3.DSZSH.Services;
using static HH.WCS.Mobox3.DSZSH.Dtos.Request.AgvRequest;
using static HH.WCS.Mobox3.DSZSH.Dtos.Response.MoboxResponse;
using HH.WCS.Mobox3.DSZSH.models;
namespace HH.WCS.Mobox3.DSZSH.Dispatch
using HH.WCS.Mobox3.DSZSH.util;
using static HH.WCS.Mobox3.DSZSH.api.ApiModel;
namespace HH.WCS.Mobox3.DSZSH.dispatch
{
    /// <summary>
    /// 国自调度辅助类
@@ -154,7 +151,7 @@
            if (agv.state != 0) {
                agv.task_no = model.orderName;
                agv.forklift_no = model.agvIDList;
                AgvService.OperateAgvTaskStatus(agv);
                WCSCore.OperateAgvTaskStatus(agv);
            }
            result.resultCode = 0;
Dispatch/HanAo.cs
@@ -1,10 +1,11 @@
using HH.WCS.Mobox3.DSZSH.AppStart;
using HH.WCS.Mobox3.DSZSH.util;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Web.Services.Description;
namespace HH.WCS.Mobox3.DSZSH.Dispatch {
namespace HH.WCS.Mobox3.DSZSH.dispatch {
    /// <summary>
    /// 国自调度辅助类
    /// </summary>
Dispatch/NDC.cs
@@ -1,11 +1,10 @@
using HH.WCS.Mobox3.DSZSH.AppStart;
using Newtonsoft.Json;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
namespace HH.WCS.Mobox3.DSZSH.Dispatch
namespace HH.WCS.Mobox3.DSZSH.dispatch
{
    public class NDC
    {
Dispatch/NDCApi.cs
@@ -1,12 +1,13 @@
using HH.WCS.Mobox3.DSZSH;
using HH.WCS.Mobox3.DSZSH.AppStart;
using HH.WCS.Mobox3.DSZSH.util;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
namespace HH.WCS.Mobox3.DSZSH.Dispatch
namespace HH.WCS.Mobox3.DSZSH.dispatch
{
    /// <summary>
    /// NDC的API接口,用于替代原NDC、NDCHelper和HostToAGV模块
@@ -17,7 +18,7 @@
        static NDCApi()
        {
            NDCApiUrl = AppStart.Settings.Config.NdcApiUrl;
            NDCApiUrl = Settings.NdcApiUrl;
        }
Dispatch/NDCHelper.cs
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Text;
namespace HH.WCS.Mobox3.DSZSH.Dispatch
namespace HH.WCS.Mobox3.DSZSH.dispatch
{
    public class NDCHelper
    {
Dtos/Request/AgvRequest.cs
File was deleted
Dtos/Request/DebugRequest.cs
File was deleted
Dtos/Request/MoboxRequest.cs
File was deleted
Dtos/Request/WmsRequest.cs
File was deleted
Dtos/Response/AgvResponse.cs
File was deleted
Dtos/Response/DebugResponse.cs
File was deleted
Dtos/Response/MoboxResponse.cs
File was deleted
Dtos/Response/WmsResponse.cs
File was deleted
HH.WCS.Mobox3.DSZSH.csproj
@@ -224,87 +224,63 @@
    </Reference>
  </ItemGroup>
  <ItemGroup>
    <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" />
    <Compile Include="Consts\LockStateCode.cs" />
    <Compile Include="Consts\LockStateName.cs" />
    <Compile Include="Consts\SpotStateCode.cs" />
    <Compile Include="Consts\TaskName.cs" />
    <Compile Include="Controllers\DebugController.cs" />
    <Compile Include="Controllers\ErpController.cs" />
    <Compile Include="Controllers\MesController.cs" />
    <Compile Include="Controllers\MoboxController.cs" />
    <Compile Include="Controllers\AgvController.cs" />
    <Compile Include="api\ApiHelper.cs" />
    <Compile Include="api\ApiModel.cs" />
    <Compile Include="api\DebugController.cs" />
    <Compile Include="api\ErpController.cs" />
    <Compile Include="api\MesController.cs" />
    <Compile Include="api\MoboxController.cs" />
    <Compile Include="api\AgvController.cs" />
    <Compile Include="App_Start\SwaggerControllerDescProvider.cs" />
    <Compile Include="Dtos\Request\WmsRequest.cs" />
    <Compile Include="Dtos\Response\WmsResponse.cs" />
    <Compile Include="Models\DebugModel.cs" />
    <Compile Include="Dtos\Request\DebugRequest.cs" />
    <Compile Include="Dtos\Response\DebugResponse.cs" />
    <Compile Include="Helpers\AgvHelper.cs" />
    <Compile Include="Helpers\ExprHelper.cs" />
    <Compile Include="Helpers\PathHelper.cs" />
    <Compile Include="Helpers\DbHelper.cs" />
    <Compile Include="Models\TN_Container_Item.cs" />
    <Compile Include="Models\TN_Check_Detail.cs" />
    <Compile Include="Models\TN_Check_Order.cs" />
    <Compile Include="Models\TN_Inbound_Order.cs" />
    <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\DebugCore.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" />
    <Compile Include="Devices\OpcUaHelper.cs" />
    <Compile Include="Devices\PlcHelper.cs" />
    <Compile Include="Devices\S7Helper.cs" />
    <Compile Include="Devices\TcpClient.cs" />
    <Compile Include="Devices\TcpServer.cs" />
    <Compile Include="Dispatch\GZRobot.cs" />
    <Compile Include="Dispatch\HanAo.cs" />
    <Compile Include="Dispatch\NDC.cs" />
    <Compile Include="ServiceCore\OfflineCore.cs" />
    <Compile Include="Dispatch\NDCApi.cs" />
    <Compile Include="Dtos\Request\AgvRequest.cs" />
    <Compile Include="Dtos\Request\MoboxRequest.cs" />
    <Compile Include="Dtos\Response\AgvResponse.cs" />
    <Compile Include="Dtos\Response\MoboxResponse.cs" />
    <Compile Include="Models\BaseModel.cs" />
    <Compile Include="Models\TN_CG_Detail.cs" />
    <Compile Include="Models\TN_CAR_IN.cs" />
    <Compile Include="Models\TN_Container.cs" />
    <Compile Include="Models\TN_Location.cs" />
    <Compile Include="Models\TN_Loc_Container.cs" />
    <Compile Include="Models\TN_WorkOrder.cs" />
    <Compile Include="Models\TN_Task.cs" />
    <Compile Include="Models\TN_Task_Action.cs" />
    <Compile Include="Helpers\DeviceProcess.cs" />
    <Compile Include="ServiceCore\TaskCore.cs" />
    <Compile Include="Controllers\WMSController.cs" />
    <Compile Include="Dispatch\HostToAGV.cs" />
    <Compile Include="Helpers\TaskHelper.cs" />
    <Compile Include="Services\AgvService.cs" />
    <Compile Include="Services\WmsService.cs" />
    <Compile Include="Services\MoboxService.cs" />
    <Compile Include="Helpers\LogHelper.cs" />
    <Compile Include="Helpers\ContainerHelper.cs" />
    <Compile Include="Helpers\LocationHelper.cs" />
    <Compile Include="Helpers\SysHelper.cs" />
    <Compile Include="core\Monitor.cs" />
    <Compile Include="core\WCSCore.cs" />
    <Compile Include="core\WMSCore.cs" />
    <Compile Include="process\TaskProcess.cs" />
    <Compile Include="util\SqlHelper.cs" />
    <Compile Include="models\TN_Container_Item.cs" />
    <Compile Include="models\TN_Check_Detail.cs" />
    <Compile Include="models\TN_Check_Order.cs" />
    <Compile Include="models\TN_Inbound_Order.cs" />
    <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="device\ProductionLineDevice.cs" />
    <Compile Include="device\ModbusHelper.cs" />
    <Compile Include="device\OpcUaHelper.cs" />
    <Compile Include="device\PlcHelper.cs" />
    <Compile Include="device\S7Helper.cs" />
    <Compile Include="device\TcpClient.cs" />
    <Compile Include="device\TcpServer.cs" />
    <Compile Include="dispatch\GZRobot.cs" />
    <Compile Include="dispatch\HanAo.cs" />
    <Compile Include="dispatch\NDC.cs" />
    <Compile Include="dispatch\NDCApi.cs" />
    <Compile Include="models\BaseModel.cs" />
    <Compile Include="models\TN_CG_Detail.cs" />
    <Compile Include="models\TN_CAR_IN.cs" />
    <Compile Include="models\TN_Container.cs" />
    <Compile Include="models\TN_Location.cs" />
    <Compile Include="models\TN_Loc_Container.cs" />
    <Compile Include="models\TN_WorkOrder.cs" />
    <Compile Include="models\TN_Task.cs" />
    <Compile Include="models\TN_Task_Action.cs" />
    <Compile Include="process\DeviceProcess.cs" />
    <Compile Include="api\WMSController.cs" />
    <Compile Include="dispatch\HostToAGV.cs" />
    <Compile Include="util\LogHelper.cs" />
    <Compile Include="wms\ContainerHelper.cs" />
    <Compile Include="wms\LocationHelper.cs" />
    <Compile Include="wms\SYSHelper.cs" />
    <Compile Include="Program.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="App_Start\Settings.cs" />
    <Compile Include="util\Settings.cs" />
    <Compile Include="App_Start\Startup.cs" />
    <Compile Include="Dispatch\NDCHelper.cs" />
    <Compile Include="Helpers\HttpHelper.cs" />
    <Compile Include="dispatch\NDCHelper.cs" />
    <Compile Include="util\HttpHelper.cs" />
    <Compile Include="wms\WCSHelper.cs" />
    <Compile Include="wms\WMSHelper.cs" />
  </ItemGroup>
  <ItemGroup>
    <None Include=".editorconfig" />
@@ -312,12 +288,9 @@
    <None Include="config\config.json">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </None>
    <None Include="debug\loc_cntr_cg.csv" />
    <None Include="debug\task.csv" />
    <None Include="packages.config" />
  </ItemGroup>
  <ItemGroup>
    <None Include="debug\outbound_order.csv" />
    <Content Include="readme.md" />
    <EmbeddedResource Include="swagger.js">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
Helpers/AgvHelper.cs
File was deleted
Helpers/DbHelper.cs
File was deleted
Helpers/ExprHelper.cs
File was deleted
Helpers/PathHelper.cs
File was deleted
Helpers/TaskHelper.cs
File was deleted
Models/BaseModel.cs
@@ -2,7 +2,7 @@
using SqlSugar;
namespace HH.WCS.Mobox3.DSZSH.Models {
namespace HH.WCS.Mobox3.DSZSH.models {
    /// <summary>
    /// 【框架】模板抽象类:基本表数据模型
    /// </summary>
Models/DebugModel.cs
File was deleted
Models/TN_CAR_IN.cs
@@ -1,6 +1,6 @@
using SqlSugar;
namespace HH.WCS.Mobox3.DSZSH.Models {
namespace HH.WCS.Mobox3.DSZSH.models {
    /// <summary>
    /// 【框架】容器生产车数关联子表
    /// </summary>
Models/TN_CG_Detail.cs
@@ -1,12 +1,12 @@
using SqlSugar;
namespace HH.WCS.Mobox3.DSZSH.Models {
namespace HH.WCS.Mobox3.DSZSH.models {
    /// <summary>
    /// 【框架】物料-容器 关系表
    /// </summary>
    [SugarTable("TN_CG_Detail")]
    public class TN_CG_Detail : BaseModel {
        #region 基本属性
        /// <summary>
        /// 料箱商品标识
        /// </summary>
@@ -28,7 +28,7 @@
        public string S_CNTR_CODE { get; set; }
        /// <summary>
        /// 货品状态:0合格 1待检 2不合格 3正在检验;下线即待检
        /// 货品状态:0合格 1待检 2不合格 3抽检中;下线即待检
        /// </summary>
        public string S_ITEM_STATE { get; set; } = "待检";
@@ -51,10 +51,9 @@
        /// 物料规格
        /// </summary>
        public string S_ITEM_SPEC { get; set; } = string.Empty;
        #endregion
        // -------------------------------
        #region 拓展
        /// <summary>
        /// 执行标准
        /// </summary>
@@ -74,6 +73,5 @@
        ///// 产线号
        ///// </summary>
        //public int N_PRODUCT_LINE { get; set; } = 0; // NOTE 后续MES可能会提供,先创建 
        #endregion
    }
}
Models/TN_Check_Detail.cs
@@ -6,7 +6,7 @@
using SqlSugar;
namespace HH.WCS.Mobox3.DSZSH.Models {
namespace HH.WCS.Mobox3.DSZSH.models {
    /// <summary>
    /// 抽检单明细
    /// </summary>
Models/TN_Check_Order.cs
@@ -6,7 +6,7 @@
using SqlSugar;
namespace HH.WCS.Mobox3.DSZSH.Models {
namespace HH.WCS.Mobox3.DSZSH.models {
    /// <summary>
    /// 抽检单
    /// </summary>
Models/TN_Container.cs
@@ -2,7 +2,7 @@
using SqlSugar;
namespace HH.WCS.Mobox3.DSZSH.Models {
namespace HH.WCS.Mobox3.DSZSH.models {
    /// <summary>
    /// 【框架】容器表
    /// </summary>
Models/TN_Container_Item.cs
@@ -6,7 +6,7 @@
using SqlSugar;
namespace HH.WCS.Mobox3.DSZSH.Models {
namespace HH.WCS.Mobox3.DSZSH.models {
    /// <summary>
    /// 容器-物料类型 关系表
    /// </summary>
Models/TN_Inbound_Order.cs
@@ -4,7 +4,7 @@
using System.Text;
using System.Threading.Tasks;
namespace HH.WCS.Mobox3.DSZSH.Models {
namespace HH.WCS.Mobox3.DSZSH.models {
    /// <summary>
    /// 出库单
    /// </summary>
Models/TN_Loc_Container.cs
@@ -2,7 +2,7 @@
using SqlSugar;
namespace HH.WCS.Mobox3.DSZSH.Models {
namespace HH.WCS.Mobox3.DSZSH.models {
    /// <summary>
    /// 【框架】货位-容器 关系表
    /// </summary>
Models/TN_Location.cs
@@ -2,7 +2,7 @@
using SqlSugar;
namespace HH.WCS.Mobox3.DSZSH.Models {
namespace HH.WCS.Mobox3.DSZSH.models {
    /// <summary>
    /// 【框架】货位表
    /// </summary>
Models/TN_Order_Task.cs
@@ -4,7 +4,7 @@
using System.Text;
using System.Threading.Tasks;
namespace HH.WCS.Mobox3.DSZSH.Models {
namespace HH.WCS.Mobox3.DSZSH.models {
    public class TN_Order_Task {
    }
Models/TN_Outbound_Detail.cs
@@ -1,6 +1,6 @@
using SqlSugar;
namespace HH.WCS.Mobox3.DSZSH.Models {
namespace HH.WCS.Mobox3.DSZSH.models {
    /// <summary>
    /// 出库单明细
    /// </summary>
@@ -12,7 +12,7 @@
        public string S_OO_NO { get; set; }
        /// <summary>
        /// 业务状态:0等待执行 1已执行待生成任务 2任务执行中 3任务完成
        /// 业务状态:0待执行 1已下发 2执行中 3已完成
        /// </summary>
        public int N_B_STATE { get; set; } = 1; // 创建即执行
Models/TN_Outbound_Order.cs
@@ -2,7 +2,7 @@
using SqlSugar;
namespace HH.WCS.Mobox3.DSZSH.Models {
namespace HH.WCS.Mobox3.DSZSH.models {
    /// <summary>
    /// 出库单
    /// </summary>
@@ -14,9 +14,9 @@
        public string S_NO { get; set; }
        /// <summary>
        /// 业务状态:0等待执行 1已执行待生成任务 2任务执行中 3任务完成
        /// 业务状态:0待执行 1已下发 2执行中 3已完成
        /// </summary>
        public int N_B_STATE { get; set; } = 1; // 创建即执行
        public int N_B_STATE { get; set; } = 1;
        /// <summary>
        /// 物料号
Models/TN_Shift_Detail.cs
@@ -5,7 +5,7 @@
using System.Text;
using System.Threading.Tasks;
namespace HH.WCS.Mobox3.DSZSH.Models {
namespace HH.WCS.Mobox3.DSZSH.models {
    /// <summary>
    /// 移库单明细
    /// </summary>
Models/TN_Shift_Order.cs
@@ -4,7 +4,7 @@
using System.Text;
using System.Threading.Tasks;
namespace HH.WCS.Mobox3.DSZSH.Models {
namespace HH.WCS.Mobox3.DSZSH.models {
    /// <summary>
    /// 移库单
    /// </summary>
Models/TN_Task.cs
@@ -2,7 +2,7 @@
using SqlSugar;
namespace HH.WCS.Mobox3.DSZSH.Models {
namespace HH.WCS.Mobox3.DSZSH.models {
    /// <summary>
    /// 【框架】任务表
    /// </summary>
Models/TN_Task_Action.cs
@@ -1,6 +1,6 @@
using SqlSugar;
namespace HH.WCS.Mobox3.DSZSH.Models {
namespace HH.WCS.Mobox3.DSZSH.models {
    /// <summary>
    /// 【框架】任务动作表
    /// </summary>
Models/TN_WorkOrder.cs
@@ -1,6 +1,6 @@
using SqlSugar;
namespace HH.WCS.Mobox3.DSZSH.Models {
namespace HH.WCS.Mobox3.DSZSH.models {
    /// <summary>
    /// 生产工单
    /// </summary>
Program.cs
@@ -1,17 +1,15 @@
using System;
using System.Collections.Generic;
using System.Threading;
using HH.WCS.Mobox3.DSZSH.AppStart;
using HH.WCS.Mobox3.DSZSH.device;
using HH.WCS.Mobox3.DSZSH.Helpers;
using HH.WCS.Mobox3.DSZSH.ServiceCore;
using HH.WCS.Mobox3.DSZSH.core;
using Microsoft.Owin.Hosting;
using Topshelf;
using Task = System.Threading.Tasks.Task;
using Monitor = HH.WCS.Mobox3.DSZSH.core.Monitor;
namespace HH.WCS.Mobox3.DSZSH {
    internal class Program
@@ -26,7 +24,7 @@
            // 3.0 开启S7
            StartS7();
            // 4.0 开启Modbus
            //StartModbus();
            StartModbus();
            // 5.0 开启线程
            var rc = HostFactory.Run(x => {
@@ -54,7 +52,7 @@
            Console.WriteLine("Startup ApiController");
            Task.Run(() =>
            {
                var url = AppStart.Settings.Config.WebApiUrl; // 运行时修改 config.json 无效
                var url = Settings.WebApiUrl; // 运行时修改 config.json 无效
                Console.WriteLine(url);
                using (WebApp.Start<Startup>(url))
                {
@@ -69,8 +67,8 @@
        /// </summary>
        private static void StartTcp()
        {
            var tcpServerIP = AppStart.Settings.Config.TcpServerIp; // 运行时修改 config.json 无效
            var tcpServerPort = AppStart.Settings.Config.TcpServerPort; // 运行时修改 config.json 无效
            var tcpServerIP = Settings.TcpServerIp; // 运行时修改 config.json 无效
            var tcpServerPort = Settings.TcpServerPort; // 运行时修改 config.json 无效
            new TcpServer(tcpServerIP, tcpServerPort);
        }
@@ -91,18 +89,6 @@
            //        Console.WriteLine("S7ProductionLineHelper," + item.ProductionLine_IP);
            //    }
            //}
            ////称重的S7设备
            //var weightPLCDevice = Settings.WeightDevices;
            //if (weightPLCDevice.Count > 0)
            //{
            //    foreach (var item in weightPLCDevice)
            //    {
            //        new S7Helper(item.WeightDevice_IP, (short)item.WeightDevice_Rack, (short)item.WeightDevice_Slot);
            //        Console.WriteLine("S7WeightDeviceHelper," + item.WeightDevice_Name);
            //    }
            //}
        }
        /// <summary>
@@ -111,7 +97,7 @@
        private static void StartModbus()
        {
            // 所有的Modbus设备
            var allPLCDevice = AppStart.Settings.Config.ProductionLines; // 运行时修改 config.json 无效
            var allPLCDevice = Settings.ProductionLines; // 运行时修改 config.json 无效
            
            if (allPLCDevice.Count > 0) {
                foreach (var item in allPLCDevice) {
@@ -128,13 +114,13 @@
                List<Task> tasks = new List<Task>();
                // 轮询:出库单状态
                tasks.Add(GetTask(OutboundCore.CheckOrderState));
                tasks.Add(GetTask(Monitor.CheckOutboundOrder));
                // 轮询:抽检单状态
                tasks.Add(GetTask(CheckCore.CheckOrderState));
                tasks.Add(GetTask(Monitor.CheckCheckOrder));
                // 轮询:移库单状态
                tasks.Add(GetTask(ShiftCore.CheckOrderState));
                tasks.Add(GetTask(Monitor.CheckShiftOrder));
                Task.WaitAll(tasks.ToArray());
            }
ServiceCore/CheckCore.cs
File was deleted
ServiceCore/OfflineCore.cs
File was deleted
ServiceCore/OutboundCore.cs
File was deleted
ServiceCore/ShiftCore.cs
File was deleted
ServiceCore/TaskCore.cs
File was deleted
Services/AgvService.cs
File was deleted
Services/DebugService.cs
File was deleted
Services/MoboxService.cs
File was deleted
Services/WmsService.cs
File was deleted
api/AgvController.cs
File was renamed from Controllers/AgvController.cs
@@ -1,13 +1,13 @@
using System.Web.Http;
using HH.WCS.Mobox3.DSZSH.Services;
using HH.WCS.Mobox3.DSZSH.core;
using Newtonsoft.Json;
using static HH.WCS.Mobox3.DSZSH.Dtos.Request.AgvRequest;
using static HH.WCS.Mobox3.DSZSH.Dtos.Response.AgvResponse;
using static HH.WCS.Mobox3.DSZSH.api.ApiModel;
namespace HH.WCS.Mobox3.DSZSH.Controllers {
namespace HH.WCS.Mobox3.DSZSH.api {
    /// <summary>
    /// 设备信息上报(HostToAGV上报、杭奥堆垛机、国自AGV)
    /// </summary>
@@ -24,7 +24,7 @@
        public ReturnResult AGVCallbackState(AgvTaskState model)
        {
            LogHelper.Info("NDC HostToAGV 任务状态回报:" + JsonConvert.SerializeObject(model), "HosttoagvTask");
            return AgvService.OperateAgvTaskStatus(model);
            return WCSCore.OperateAgvTaskStatus(model);
        }
        /// <summary>
@@ -35,7 +35,7 @@
        [HttpPost]
        [Route("SafetyInteraction")]
        public ReturnResult SafetyInteraction(SafetyInteractionInfo model) {
            return AgvService.SafetyInteraction(model);
            return WCSCore.SafetyInteraction(model);
        }
    }
}
api/ApiHelper.cs
New file
@@ -0,0 +1,1330 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HH.WCS.Mobox3.DSZSH.models;
using HH.WCS.Mobox3.DSZSH.util;
using HH.WCS.Mobox3.DSZSH.wms;
using Newtonsoft.Json;
using SqlSugar;
using Swashbuckle.Swagger;
using static HH.WCS.Mobox3.DSZSH.api.ApiModel;
using static HH.WCS.Mobox3.DSZSH.api.OtherModel;
using static HH.WCS.Mobox3.DSZSH.Config;
namespace HH.WCS.Mobox3.DSZSH.api {
    public class ApiHelper {
        /// <summary>
        /// 好运箱-满托下线入库(PDA)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public static SimpleResult GoodpackOffline(GoodpackOfflineInfo model) {
            var taskName = TaskName.好运箱_满箱下线入库; // 用于生成任务类型、打印日志信息
            var db = new SqlHelper<object>().GetInstance();
            var info = "";
            try {
                // 将PDA提供的物料编码与贴标机的信息比对
                var cgDetail = db.Queryable<TN_CG_Detail>()
                    .Where(d => d.S_ITEM_CODE == model.s_item_code && d.S_BATCH_NO == model.s_batch) // 指定:物料编码、批次号
                    .Where(d => d.N_ITEM_STATE == 1 && d.S_ITEM_STATE == "待检") // NOTE 冗余检查:物料状态应该为 1待检
                    .First();
                if (cgDetail == null) {
                    info = $"PDA扫码物料信息与贴标机传递的信息不一致:" + JsonConvert.SerializeObject(model);
                    LogHelper.Info(info);
                    return NewSimpleResult(1, info);
                }
                var startLoc = db.Queryable<TN_Location>()
                    .Where(a => a.S_CODE == model.s_start_loc) // 指定:起点货位号
                    .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                    .First();
                if (startLoc == null) {
                    info = $"起点位置 '{model.s_start_loc}' 不存在或不具备取货要求";
                    LogHelper.Info(info);
                    return NewSimpleResult(3, info);
                }
                // 绑定货位和容器号
                var locCntrRel = new TN_Loc_Container {
                    S_LOC_CODE = model.s_start_loc,
                    S_CNTR_CODE = cgDetail.S_CNTR_CODE,
                    S_CNTR_TYPE = "好运箱",
                };
                if (db.Insertable<TN_Loc_Container>(locCntrRel).ExecuteCommand() <= 0) {
                    info = $"插入货位容器关系失败:" + JsonConvert.SerializeObject(locCntrRel);
                    LogHelper.Info(info);
                    return NewSimpleResult(4, info);
                }
                // TODO 满箱入库算法待优化
                var endLoc = db.Queryable<TN_Location>()
                    .Where(a => Settings.AreaMap[AreaName.满托存放区].Contains(a.S_AREA_CODE))
                    .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                    .Where(a => a.N_CURRENT_NUM == 0) // 筛选:空货位
                    .OrderBy(l => l.N_LAYER)
                    .OrderBy(l => l.S_AREA_CODE).First();
                if (endLoc == null) {
                    info = "满箱入库暂时没有合适的货位可以入库";
                    LogHelper.Info(info);
                    return NewSimpleResult(4, info);
                }
                var cntId = locCntrRel.S_CNTR_CODE;
                var task = WCSHelper.BuildTask(startLoc, endLoc, cntId, taskName);
                LocationHelper.LockLoc(ref startLoc, 1); // 起点出库锁
                LocationHelper.LockLoc(ref endLoc, 2); // 终点入库锁
                using (var tran = db.Ado.UseTran()) {
                    if (db.Updateable<TN_Location>(startLoc).UpdateColumns(it => new {
                        it.N_LOCK_STATE,
                        it.S_LOCK_STATE,
                        it.S_LOCK_OP,
                        it.T_MODIFY
                    }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货位 {endLoc.S_CODE}";
                        LogHelper.Info(info);
                        return NewSimpleResult(4, info);
                    }
                    if (db.Updateable<TN_Location>(endLoc).UpdateColumns(it => new {
                        it.N_LOCK_STATE,
                        it.S_LOCK_STATE,
                        it.S_LOCK_OP,
                        it.T_MODIFY
                    }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}";
                        LogHelper.Info(info);
                        return NewSimpleResult(4, info);
                    }
                    if (db.Insertable<TN_Task>(task).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}";
                        LogHelper.Info(info);
                        return NewSimpleResult(4, info);
                    }
                    tran.CommitTran();
                    info = $"生成 {taskName} 成功,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}";
                    LogHelper.Info(info);
                    return NewSimpleResult(0, info);
                }
            }
            catch (Exception ex) {
                info = $"发生了异常:{ex.Message}";
                LogHelper.Info(info);
                return NewSimpleResult(1, info);
            }
        }
        ///// <summary>
        ///// 空托盘绑定(PDA)
        ///// </summary>
        ///// <param name="model"></param>
        ///// <returns></returns>
        //public static SimpleResult EmptyBindPallet(EmptyBindInfo model) {
        //    var db = DbHelper.GetDbClient();
        //    try {
        //        var loc = db.Queryable<TN_Location>()
        //            .Where(ExprHelper.LocCode(model.LocCode))
        //            .Where(ExprHelper.LocBelongsToArea(AreaName.空托盘接驳区))
        //            .Where(ExprHelper.LocIsFree)
        //            .Where(ExprHelper.LocIsEmpty).First();
        //        if (loc == null) {
        //            return BuildSimpleResult(2, $"当前货位 '{model.LocCode}' 无法再绑定容器");
        //        }
        //        var locCntrRel = db.Queryable<TN_Loc_Container>()
        //            .Where(lc => lc.S_CNTR_CODE == model.LocCode).First();
        //        if (locCntrRel != null) {
        //            return BuildSimpleResult(3, $"当前容器 '{model.CntrCode}' 已经与 '{locCntrRel.S_LOC_CODE}' 绑定");
        //        }
        //        //loc.N_CURRENT_NUM = model.PalletCount;
        //        locCntrRel = new TN_Loc_Container {
        //            S_LOC_CODE = model.LocCode,
        //            S_CNTR_CODE = model.CntrCode
        //        };
        //        using (var tran = db.Ado.UseTran()) {
        //            if (db.Insertable<TN_Loc_Container>(locCntrRel).ExecuteCommand() > 0
        //                && db.Updateable<TN_Location>(loc).UpdateColumns(it => it.N_CURRENT_NUM).ExecuteCommand() > 0) {
        //                tran.CommitTran();
        //                return BuildSimpleResult(0, $"绑定容器 '{model.CntrCode}' 与货位 '{model.LocCode}' 成功");
        //            }
        //            else {
        //                tran.RollbackTran();
        //                return BuildSimpleResult(4, $"绑定容器 '{model.CntrCode}' 与货位 '{model.LocCode}' 失败");
        //            }
        //        }
        //    }
        //    catch (Exception ex) {
        //        return BuildSimpleEx(ex);
        //    }
        //}
        ///// <summary>
        ///// 空箱绑定(PDA)
        ///// </summary>
        ///// <param name="model"></param>
        ///// <returns></returns>
        //public static SimpleResult EmptyBindGoodpack(EmptyBindInfo model) {
        //    var db = DbHelper.GetDbClient();
        //    try {
        //        var loc = db.Queryable<TN_Location>()
        //            .Where(ExprHelper.LocCode(model.LocCode))
        //            .Where(ExprHelper.LocBelongsToArea(AreaName.空箱接驳区))
        //            .Where(ExprHelper.LocIsFree)
        //            .Where(ExprHelper.LocIsEmpty).First();
        //        if (loc == null) {
        //            return BuildSimpleResult(2, $"当前货位 '{model.LocCode}' 无法再绑定容器");
        //        }
        //        var locCntrRel = db.Queryable<TN_Loc_Container>()
        //            .Where(lc => lc.S_CNTR_CODE == model.LocCode).First();
        //        if (locCntrRel != null) {
        //            return BuildSimpleResult(3, $"当前容器 '{model.CntrCode}' 已经与 '{locCntrRel.S_LOC_CODE}' 绑定");
        //        }
        //        loc.N_CURRENT_NUM = 1; // 空箱绑定时容器数必然为 1
        //        locCntrRel = new TN_Loc_Container {
        //            S_LOC_CODE = model.LocCode,
        //            S_CNTR_CODE = model.CntrCode
        //        };
        //        using (var tran = db.Ado.UseTran()) {
        //            if (db.Insertable<TN_Loc_Container>(locCntrRel).ExecuteCommand() > 0
        //                && db.Updateable<TN_Location>(loc).UpdateColumns(it => it.N_CURRENT_NUM).ExecuteCommand() > 0) {
        //                tran.CommitTran();
        //                return BuildSimpleResult(0, $"绑定容器 '{model.CntrCode}' 与货位 '{model.LocCode}' 成功");
        //            }
        //            else {
        //                tran.RollbackTran();
        //                return BuildSimpleResult(4, $"绑定容器 '{model.CntrCode}' 与货位 '{model.LocCode}' 失败");
        //            }
        //        }
        //    }
        //    catch (Exception ex) {
        //        return BuildSimpleEx(ex);
        //    }
        //}
        /// <summary>
        /// 托盘-空托入库(PDA)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public static SimpleResult EmptyInboundPallet(EmptyInboundInfo model) {
            var taskName = TaskName.托盘_空托入库;
            var db = new SqlHelper<object>().GetInstance();
            var info = "";
            try {
                var startLoc = db.Queryable<TN_Location>()
                    .Where(a => a.S_CODE == model.StartLoc) // 指定:起点货位
                    .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                    .Where(a => a.N_CURRENT_NUM == 1)
                    .First();
                if (startLoc == null) {
                    return NewSimpleResult(2, $"起点位置 '{model.StartLoc}' 不存在或不具备取货要求");
                }
                // 查看容器与起点货位是否绑定
                var locCntrRel = db.Queryable<TN_Loc_Container>()
                    .Where(c => c.S_LOC_CODE == model.StartLoc && c.S_CNTR_CODE == model.CntrCode)
                    .First();
                if (locCntrRel == null) {
                    return NewSimpleResult(3, $"起点位置 '{model.StartLoc}' 没有绑定容器 '{model.CntrCode}'");
                }
                // TODO 暂定选择最低层按区位顺序入库,后面待修改
                var endLoc = db.Queryable<TN_Location>()
                    .Where(a => Settings.AreaMap[AreaName.空托存放区].Contains(a.S_AREA_CODE))
                    .OrderBy(l => l.N_LAYER)
                    .OrderBy(l => l.S_AREA_CODE).First();
                if (endLoc == null) {
                    return NewSimpleResult(4, $"暂时没有符合条件的终点放货位");
                }
                var cntId = locCntrRel.S_CNTR_CODE;
                var task = WCSHelper.BuildTask(startLoc, endLoc, cntId, taskName);
                LocationHelper.LockLoc(ref startLoc, 1); // 起点出库锁
                LocationHelper.LockLoc(ref endLoc, 2); // 终点入库锁
                using (var tran = db.Ado.UseTran()) {
                    if (db.Updateable<TN_Location>(startLoc).UpdateColumns(it => new {
                        it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500,
                            $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货位 {endLoc.S_CODE}");
                    }
                    if (db.Updateable<TN_Location>(endLoc).UpdateColumns(it => new {
                        it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500,
                            $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}");
                    }
                    if (db.Insertable<TN_Task>(task).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500,
                            $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}");
                    }
                    tran.CommitTran();
                    return NewSimpleResult(0,
                        $"生成 {taskName} 成功,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}");
                }
            }
            catch (Exception ex) {
                info = $"发生了异常:{ex.Message}";
                LogHelper.Info(info);
                return NewSimpleResult(1, info);
            }
        }
        /// <summary>
        /// 好运箱-空箱入库(PDA)
        /// </summary>
        /// <returns></returns>
        public static SimpleResult EmptyInboundGoodpack(EmptyInboundInfo model) {
            var taskName = TaskName.好运箱_空箱入库;
            var db = new SqlHelper<object>().GetInstance();
            var info = "";
            try {
                var startLoc = db.Queryable<TN_Location>()
                    .Where(a => a.S_CODE == model.StartLoc)
                    .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                    .Where(a => a.N_CURRENT_NUM == 1) // 筛选:有货货位
                    .First();
                if (startLoc == null) {
                    return NewSimpleResult(2, $"起点位置 '{model.StartLoc}' 不存在或不具备取货要求");
                }
                // 查看容器与起点货位是否绑定
                var locCntrRel = db.Queryable<TN_Loc_Container>()
                    .Where(c => c.S_LOC_CODE == model.StartLoc && c.S_CNTR_CODE == model.CntrCode)
                    .First();
                if (locCntrRel == null) {
                    return NewSimpleResult(3, $"起点位置 '{model.StartLoc}' 没有绑定容器 '{model.CntrCode}'");
                }
                // TODO 暂定选择最低层按区位顺序入库,后面待修改
                var endLoc = db.Queryable<TN_Location>()
                    .Where(a => Settings.AreaMap[AreaName.空箱存放区].Contains(a.S_AREA_CODE))
                    .OrderBy(l => l.N_LAYER)
                    .OrderBy(l => l.S_AREA_CODE).First();
                if (endLoc == null) {
                    return NewSimpleResult(4, $"暂时没有符合条件的终点放货位");
                }
                var cntId = locCntrRel.S_CNTR_CODE;
                var task = WCSHelper.BuildTask(startLoc, endLoc, cntId, taskName);
                LocationHelper.LockLoc(ref startLoc, 1); // 起点出库锁
                LocationHelper.LockLoc(ref endLoc, 2); // 终点入库锁
                using (var tran = db.Ado.UseTran()) {
                    if (db.Updateable<TN_Location>(startLoc).UpdateColumns(it => new {
                        it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500,
                            $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货位 {endLoc.S_CODE}");
                    }
                    if (db.Updateable<TN_Location>(endLoc).UpdateColumns(it => new {
                        it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500,
                            $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}");
                    }
                    if (db.Insertable<TN_Task>(task).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500,
                            $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}");
                    }
                    tran.CommitTran();
                    return NewSimpleResult(0,
                        $"生成 {taskName} 成功,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}");
                }
            }
            catch (Exception ex) {
                return BuildSimpleEx(ex);
            }
        }
        /// <summary>
        /// 托盘-空托上线(PDA)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public static SimpleResult EmptyOnlinePallet(EmptyOnlinePalletInfo model) {
            var taskName = TaskName.托盘_空托上线;
            var db = new SqlHelper<object>().GetInstance();
            var taskInfo = Settings.TaskMap[taskName];
            var info = "";
            try {
                // TODO 符合物料信息的货位
                var startLoc = db.Queryable<TN_Location>()
                    .Where(l => taskInfo.StartAreas.Contains(l.S_AREA_CODE))
                    .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                    .Where(a => a.N_CURRENT_NUM == 1) // 筛选:有货货位
                    .First();
                if (startLoc == null) {
                    return NewSimpleResult(2, $"没有合适的起点位置");
                }
                var locCntrRel = db.Queryable<TN_Loc_Container>().First(
                    a => a.S_LOC_CODE == startLoc.S_CODE
                    && a.S_CNTR_CODE == model.CntId);
                if (locCntrRel == null) {
                    return NewSimpleResult(3, $"起点位置 '{startLoc.S_CODE}' 没有绑定容器 '{model.CntId}'");
                }
                var endLoc = db.Queryable<TN_Location>()
                    .Where(a => Settings.AreaMap[AreaName.包装区].Contains(a.S_AREA_CODE))
                    .Where(l => taskInfo.EndAreas.Contains(l.S_AREA_CODE))
                    .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                    .Where(a => a.N_CURRENT_NUM == 0) // 筛选:空货位
                    .First();
                if (endLoc == null) {
                    return NewSimpleResult(4, $"终点位置 不存在或不具备放货要求");
                }
                var cntId = locCntrRel.S_CNTR_CODE;
                var task = WCSHelper.BuildTask(startLoc, endLoc, cntId, taskName);
                LocationHelper.LockLoc(ref startLoc, 1); // 起点出库锁
                LocationHelper.LockLoc(ref endLoc, 2); // 终点入库锁
                using (var tran = db.Ado.UseTran()) {
                    if (db.Updateable<TN_Location>(startLoc).UpdateColumns(it => new {
                        it.N_LOCK_STATE,
                        it.S_LOCK_STATE,
                        it.S_LOCK_OP,
                        it.T_MODIFY
                    }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500,
                            $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货位 {endLoc.S_CODE}");
                    }
                    if (db.Updateable<TN_Location>(endLoc).UpdateColumns(it => new {
                        it.N_LOCK_STATE,
                        it.S_LOCK_STATE,
                        it.S_LOCK_OP,
                        it.T_MODIFY
                    }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500,
                            $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}");
                    }
                    if (db.Insertable<TN_Task>(task).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500,
                            $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}");
                    }
                    tran.CommitTran();
                    return NewSimpleResult(0,
                        $"生成 {taskName} 成功,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}");
                }
            }
            catch (Exception ex) {
                return BuildSimpleEx(ex);
            }
        }
        /// <summary>
        /// 好运箱-空箱上线(PDA)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public static SimpleResult EmptyOnlineGoodpack(EmptyOnlineGoodpackInfo model) {
            var taskName = TaskName.好运箱_空箱上线;
            var db = new SqlHelper<object>().GetInstance();
            var taskInfo = Settings.TaskMap[taskName];
            var info = "";
            try {
                // TODO 符合物料信息的货位
                var startLoc = db.Queryable<TN_Location>()
                    .Where(l => taskInfo.StartAreas.Contains(l.S_AREA_CODE))
                    .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                    .Where(a => a.N_CURRENT_NUM == 1).First();
                if (startLoc == null) {
                    return NewSimpleResult(2, $"没有合适的起点位置");
                }
                var locCntrRel = db.Queryable<TN_Loc_Container>().First(
                    a => a.S_LOC_CODE == startLoc.S_CODE
                    && a.S_CNTR_CODE == model.CntId);
                if (locCntrRel == null) {
                    return NewSimpleResult(3, $"起点位置 '{startLoc.S_CODE}' 没有绑定容器 '{model.CntId}'");
                }
                var endLoc = db.Queryable<TN_Location>()
                    .Where(a => Settings.AreaMap[AreaName.包装区].Contains(a.S_AREA_CODE))
                    .Where(l => taskInfo.EndAreas.Contains(l.S_AREA_CODE))
                    .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                    .Where(a => a.N_CURRENT_NUM == 0)
                    .First();
                if (endLoc == null) {
                    return NewSimpleResult(4, $"终点位置 不存在或不具备放货要求");
                }
                var cntId = locCntrRel.S_CNTR_CODE;
                var task = WCSHelper.BuildTask(startLoc, endLoc, cntId, taskName);
                LocationHelper.LockLoc(ref startLoc, 1); // 起点出库锁
                LocationHelper.LockLoc(ref endLoc, 2); // 终点入库锁
                using (var tran = db.Ado.UseTran()) {
                    if (db.Updateable<TN_Location>(startLoc).UpdateColumns(it => new {
                        it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500,
                            $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货位 {endLoc.S_CODE}");
                    }
                    if (db.Updateable<TN_Location>(endLoc).UpdateColumns(it => new {
                        it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500,
                            $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}");
                    }
                    if (db.Insertable<TN_Task>(task).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500,
                            $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}");
                    }
                    tran.CommitTran();
                    return NewSimpleResult(0,
                        $"生成 {taskName} 成功,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}");
                }
            }
            catch (Exception ex) {
                return BuildSimpleEx(ex);
            }
        }
        /// <summary>
        /// 创建抽检单
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public static SimpleResult CreateCheckOrder(CreateCheckOrderInfo model) {
            var billName = "抽检单";
            var db = new SqlHelper<object>().GetInstance();
            var info = "";
            try {
                var orderNo = GenerateOrderNo("抽检单号", "CN");
                var order = new TN_Check_Order {
                    S_NO = orderNo,
                    S_ITEM_CODE = model.ItemCode,
                    //S_ITEM_NAME = model.ItemName,
                    S_BATCH_NO = model.BatchNo,
                    N_COUNT = model.Qty,
                    S_END_AREA = model.EndArea,
                };
                var cgDetailList = SelectCgByTotalQty(model);
                if (cgDetailList.Count == 0) {
                    return NewSimpleResult(3, "没有合适的物料可以抽检");
                }
                var detailList = new List<TN_Check_Detail>();
                foreach (var cgDetail in cgDetailList) {
                    var detail = new TN_Check_Detail {
                        S_NO = orderNo,
                        S_ITEM_CODE = cgDetail.S_ITEM_CODE,
                        S_BATCH_NO = cgDetail.S_BATCH_NO,
                        S_CNTR_CODE = cgDetail.S_CNTR_CODE,
                        S_END_AREA = model.EndArea,
                    };
                    detailList.Add(detail);
                    cgDetail.N_ITEM_STATE = 3;
                    cgDetail.S_ITEM_STATE = "抽验中";
                }
                using (var tran = db.Ado.UseTran()) {
                    if (db.Insertable<TN_Check_Order>(order).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"生成{billName}失败:" + JsonConvert.SerializeObject(order);
                        LogHelper.Info(info);
                        return NewSimpleResult(2, info);
                    }
                    if (db.Insertable<TN_Outbound_Detail>(detailList).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"生成{billName}明细失败";
                        LogHelper.Info(info);
                        return NewSimpleResult(4, info);
                    }
                    if (db.Updateable<TN_CG_Detail>(cgDetailList).UpdateColumns(it => new {
                        it.N_ITEM_STATE, it.S_ITEM_STATE }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"更新物料状态失败";
                    }
                    tran.CommitTran();
                }
                return NewSimpleResult(0, $"创建 抽检单 成功");
            }
            catch (Exception ex) {
                return BuildSimpleEx(ex);
            }
        }
        /// <summary>
        /// 抽检-合格回库(PDA)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public static SimpleResult QualifiedBack(QualifiedBackInfo model) {
            var taskName = TaskName.抽检_合格回库;
            var db = new SqlHelper<object>().GetInstance();
            var info = "";
            try {
                var cgDetail = db.Queryable<TN_CG_Detail>()
                    .Where(d => d.S_ITEM_CODE == model.ItemCode && d.S_CNTR_CODE == model.CntrCode)
                    .First();
                if (cgDetail == null) {
                    return NewSimpleResult(2, "没有找到待回库的抽检物料:" + JsonConvert.SerializeObject(model));
                }
                var locCntrRel = db.Queryable<TN_Loc_Container>()
                    .Where(c => c.S_CNTR_CODE == cgDetail.S_CNTR_CODE)
                    .First();
                //var startLoc = db.Queryable<TN_Location>()
                //    .LeftJoin<TN_Loc_Container>((l, c) => l.S_CODE == c.S_LOC_CODE)
                //    .Where((l, c) => c.S_CNTR_CODE == cgDetail.S_CNTR_CODE) // 指定容器号
                //    .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                //    .First();
                var startLoc = db.Queryable<TN_Location>()
                    .Where(l => l.S_CODE == locCntrRel.S_LOC_CODE)
                    .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                    .First();
                if (startLoc == null) {
                    info = "没有找到合适的起点货位";
                    LogHelper.Info(info);
                    return NewSimpleResult(0, info);
                }
                var endLoc = new TN_Location();
                if (locCntrRel.S_CNTR_TYPE == "托盘") {
                    endLoc = db.Queryable<TN_Location>()
                        .Where(a => Settings.AreaMap[AreaName.满托存放区].Contains(a.S_AREA_CODE))
                        .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                        .Where(a => a.N_CURRENT_NUM == 0) // 筛选:空货位
                        .First();
                }
                else if (locCntrRel.S_CNTR_TYPE == "好运箱") {
                    endLoc = db.Queryable<TN_Location>()
                        .Where(a => Settings.AreaMap[AreaName.满箱存放区].Contains(a.S_AREA_CODE))
                        .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                        .Where(a => a.N_CURRENT_NUM == 0)
                        .First();
                }
                else {
                    return NewSimpleResult(2, $"托盘类型{locCntrRel.S_CNTR_TYPE}不合法:托盘号{locCntrRel.S_CNTR_CODE}");
                }
                if (endLoc == null) {
                    return NewSimpleResult(3, "查询:没有找到合适的终点货位");
                }
                cgDetail.N_ITEM_STATE = 0;
                cgDetail.S_ITEM_STATE = "合格";
                var cntId = locCntrRel.S_CNTR_CODE;
                var task = WCSHelper.BuildTask(startLoc, endLoc, cntId, taskName);
                LocationHelper.LockLoc(ref startLoc, 1); // 起点出库锁
                LocationHelper.LockLoc(ref endLoc, 2); // 终点入库锁
                using (var tran = db.Ado.UseTran()) {
                    if (db.Updateable<TN_CG_Detail>(cgDetail).UpdateColumns(it =>
                        new { it.N_ITEM_STATE, it.S_ITEM_STATE }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = "修改物料状态为合格 失败";
                        LogHelper.Info(info);
                        return NewSimpleResult(4, info);
                    }
                    if (db.Updateable<TN_Location>(startLoc).UpdateColumns(it => new {
                        it.N_LOCK_STATE,
                        it.S_LOCK_STATE,
                        it.S_LOCK_OP,
                        it.T_MODIFY
                    }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货位 {endLoc.S_CODE}";
                        LogHelper.Info(info);
                        return NewSimpleResult(4, info);
                    }
                    if (db.Updateable<TN_Location>(endLoc).UpdateColumns(it => new {
                        it.N_LOCK_STATE,
                        it.S_LOCK_STATE,
                        it.S_LOCK_OP,
                        it.T_MODIFY
                    }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}";
                        LogHelper.Info(info);
                        return NewSimpleResult(4, info);
                    }
                    if (db.Insertable<TN_Task>(task).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}";
                        LogHelper.Info(info);
                        return NewSimpleResult(4, info);
                    }
                    tran.CommitTran();
                    return NewSimpleResult(0,
                        $"生成 {taskName} 成功,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}");
                }
            }
            catch (Exception ex) {
                info = $"发生了异常:{ex.Message}";
                LogHelper.Info(info);
                return NewSimpleResult(1, info);
            }
        }
        public static SimpleResult UnqualifiedShift(UnqualifiedShiftInfo model) {
            var taskName = TaskName.抽检_不合格移库;
            var db = new SqlHelper<object>().GetInstance();
            var info = "";
            try {
                var cgDetail = db.Queryable<TN_CG_Detail>()
                    .Where(d => d.S_ITEM_CODE == model.ItemCode && d.S_CNTR_CODE == model.CntrCode)
                    .First();
                if (cgDetail == null) {
                    return NewSimpleResult(2, "没有找到待回库的抽检物料");
                }
                var locCntrRel = db.Queryable<TN_Loc_Container>()
                    .Where(c => c.S_CNTR_CODE == cgDetail.S_CNTR_CODE)
                    .First();
                //var startLoc = db.Queryable<TN_Location>()
                //    .LeftJoin<TN_Loc_Container>((l, c) => l.S_CODE == c.S_LOC_CODE)
                //    .Where((l, c) => c.S_CNTR_CODE == cgDetail.S_CNTR_CODE) // 指定容器号
                //    .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                //    .First();
                var startLoc = db.Queryable<TN_Location>()
                    .Where(l => l.S_CODE == locCntrRel.S_LOC_CODE)
                    .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                    .First();
                if (startLoc == null) {
                    info = "没有找到合适的起点货位";
                    LogHelper.Info(info);
                    return NewSimpleResult(0, info);
                }
                var endLoc = db.Queryable<TN_Location>()
                    .Where(l => l.S_AREA_CODE == model.EndArea)
                    .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                    .Where(a => a.N_CURRENT_NUM == 0).First();
                if (endLoc == null) {
                    return NewSimpleResult(3, "查询:没有找到合适的终点货位");
                }
                cgDetail.N_ITEM_STATE = 2;
                cgDetail.S_ITEM_STATE = "不合格";
                var cntId = locCntrRel.S_CNTR_CODE;
                var task = WCSHelper.BuildTask(startLoc, endLoc, cntId, taskName);
                LocationHelper.LockLoc(ref startLoc, 1); // 起点出库锁
                LocationHelper.LockLoc(ref endLoc, 2); // 终点入库锁
                using (var tran = db.Ado.UseTran()) {
                    if (db.Updateable<TN_CG_Detail>(cgDetail).UpdateColumns(it =>
                        new { it.N_ITEM_STATE, it.S_ITEM_STATE }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = "修改物料状态为合格 失败";
                        LogHelper.Info(info);
                        return NewSimpleResult(4, info);
                    }
                    if (db.Updateable<TN_Location>(startLoc).UpdateColumns(it => new {
                        it.N_LOCK_STATE,
                        it.S_LOCK_STATE,
                        it.S_LOCK_OP,
                        it.T_MODIFY
                    }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货位 {endLoc.S_CODE}";
                        LogHelper.Info(info);
                        return NewSimpleResult(4, info);
                    }
                    if (db.Updateable<TN_Location>(endLoc).UpdateColumns(it => new {
                        it.N_LOCK_STATE,
                        it.S_LOCK_STATE,
                        it.S_LOCK_OP,
                        it.T_MODIFY
                    }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}";
                        LogHelper.Info(info);
                        return NewSimpleResult(4, info);
                    }
                    if (db.Insertable<TN_Task>(task).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}";
                        LogHelper.Info(info);
                        return NewSimpleResult(4, info);
                    }
                    tran.CommitTran();
                    return NewSimpleResult(0,
                        $"生成 {taskName} 成功,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}");
                }
            }
            catch (Exception ex) {
                return BuildSimpleEx(ex);
            }
        }
        public static SimpleResult RestBack(RestBackInfo model) {
            var taskName = TaskName.尾箱回库;
            var db = new SqlHelper<object>().GetInstance();
            var info = "";
            try {
                var startLoc = db.Queryable<TN_Location>()
                    .Where(l => l.S_CODE == model.StartLoc)
                    .First();
                var locCntrRel = db.Queryable<TN_Loc_Container>()
                    .Where(c => c.S_LOC_CODE == model.StartLoc)
                    .First();
                if (locCntrRel == null) {
                    return NewSimpleResult(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 NewSimpleResult(3, $"不存在合适的终点货位可以回库");
                }
                var cntId = locCntrRel.S_CNTR_CODE;
                var task = WCSHelper.BuildTask(startLoc, endLoc, cntId, taskName);
                LocationHelper.LockLoc(ref startLoc, 1); // 起点出库锁
                LocationHelper.LockLoc(ref endLoc, 2); // 终点入库锁
                using (var tran = db.Ado.UseTran()) {
                    if (db.Updateable<TN_Location>(startLoc).UpdateColumns(it => new {
                        it.N_LOCK_STATE,
                        it.S_LOCK_STATE,
                        it.S_LOCK_OP,
                        it.T_MODIFY
                    }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货位 {endLoc.S_CODE}";
                        LogHelper.Info(info);
                        return NewSimpleResult(4, info);
                    }
                    if (db.Updateable<TN_Location>(endLoc).UpdateColumns(it => new {
                        it.N_LOCK_STATE,
                        it.S_LOCK_STATE,
                        it.S_LOCK_OP,
                        it.T_MODIFY
                    }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}";
                        LogHelper.Info(info);
                        return NewSimpleResult(4, info);
                    }
                    if (db.Insertable<TN_Task>(task).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}";
                        LogHelper.Info(info);
                        return NewSimpleResult(4, info);
                    }
                    tran.CommitTran();
                    info = $"生成 {taskName} 成功,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货架 {endLoc.S_CODE}";
                    LogHelper.Info(info);
                    return NewSimpleResult(0, info);
                }
            }
            catch (Exception ex) {
                return BuildSimpleEx(ex);
            }
        }
        public static List<TN_CG_Detail> SelectCgByTotalQty(CreateCheckOrderInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var result = new List<TN_CG_Detail>();
            var targetNum = model.Qty;
            try {
                var targetCg = db.Queryable<TN_CG_Detail>().Where(a => a.S_ITEM_CODE == model.ItemCode
                && a.N_ITEM_NUM > targetNum).
                OrderBy(a => a.N_ITEM_NUM, OrderByType.Asc).First();
                if (targetCg != null) //刚好有一行满足条件
                {
                    result.Add(targetCg);
                    return result;
                }
                var sortedMaterials = new List<TN_CG_Detail>();
                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.N_ITEM_NUM > 0)
                    .Where((d, c) => c.S_CNTR_TYPE == model.CntrType)
                    .Where(d => d.N_ITEM_STATE == 1 && d.S_ITEM_STATE == "待检")
                    .OrderBy(d => d.N_ITEM_NUM, OrderByType.Desc)
                    .OrderBy(d => d.N_ITEM_STATE, OrderByType.Asc).ToList();
                if (sortedMaterials.Count == 0) //没有满足条件的
                {
                    return result;
                }
                int countNum = 0;
                foreach (var mat in sortedMaterials) {
                    countNum += mat.N_ITEM_NUM;
                    result.Add(mat);
                    if (countNum >= targetNum) {
                        break;
                    }
                }
                if (result.Sum(a => a.N_ITEM_NUM) >= targetNum) {
                    return result;
                }
                else {
                    result.Clear();
                    return result;
                }
            }
            catch (Exception ex) {
                throw ex;
            }
        }
        /// <summary>
        /// 成品胶出库(WMS)
        /// </summary>
        /// <remarks>
        /// WMS提供出库的物料类型与数量,调用接口由WCS生成具体的出库任务,然后WCS后台轮询处理
        /// </remarks>
        /// <param name="model"></param>
        /// <returns></returns>
        public static SimpleResult FinishedOutbound(FinishedOutboundInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var orderNo = GenerateOrderNo("出库单号", "ON");
            var info = "";
            try {
                if (string.IsNullOrEmpty(orderNo)) {
                    info = "出库单号不能为空";
                    LogHelper.Info(info);
                    return NewSimpleResult(2, info);
                }
                var cgDetailList = SelectCgByTotalQty(model);
                if (cgDetailList.Count == 0) {
                    info = "没有合适的物料可以出库";
                    LogHelper.Info(info);
                    return NewSimpleResult(3, info);
                }
                var order = new TN_Outbound_Order {
                    S_NO = orderNo,
                    S_ITEM_CODE = model.ItemCode,
                    S_BATCH = model.BatchNo,
                    N_END_NUM = model.Qty,
                    //F_OUT_QTY = cgDetailList.Sum(a => a.N_QTY),
                    S_END_AREA = model.EndArea
                };
                var detailList = new List<TN_Outbound_Detail>();
                foreach (var cgDetail in cgDetailList) {
                    var detail = new TN_Outbound_Detail {
                        S_OO_NO = orderNo,
                        S_ITEM_CODE = cgDetail.S_ITEM_CODE,
                        S_BATCH_NO = cgDetail.S_BATCH_NO,
                        S_CNTR_CODE = cgDetail.S_CNTR_CODE,
                        N_COUNT = cgDetail.N_ITEM_NUM,
                        S_END_AREA = model.EndArea
                    };
                    detailList.Add(detail);
                }
                using (var tran = db.Ado.UseTran()) {
                    if (db.Insertable<TN_Outbound_Order>(order).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = "生成出库单失败:" + JsonConvert.SerializeObject(order);
                        LogHelper.Info(info);
                        return NewSimpleResult(2, info);
                    }
                    if (db.Insertable<TN_Outbound_Detail>(detailList).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = "生成出库单明细失败";
                        LogHelper.Info(info);
                        return NewSimpleResult(4, info);
                    }
                    tran.CommitTran();
                }
                return NewSimpleResult(0, "生成出库单成功");
            }
            catch (Exception ex) {
                return BuildSimpleEx(ex);
            }
        }
        /// <summary>
        /// 成品胶强制出库(WMS)
        /// </summary>
        /// <returns></returns>
        public static SimpleResult FinishedOutboundForce(FinishedOutboundInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var orderNo = GenerateOrderNo("出库单号", "ON");
            var info = "";
            try {
                if (string.IsNullOrEmpty(orderNo)) {
                    return NewSimpleResult(2, "出库单号不能为空");
                }
                using (var tran = db.Ado.UseTran()) {
                    var cgDetailList = SelectCgByTotalQty(model);
                    if (cgDetailList.Count == 0) {
                        return NewSimpleResult(3, "没有合适的物料可以出库");
                    }
                    foreach (var cgDetail in cgDetailList) {
                        var detail = new TN_Outbound_Detail {
                            S_OO_NO = orderNo,
                            S_ITEM_CODE = cgDetail.S_ITEM_CODE,
                            S_BATCH_NO = cgDetail.S_BATCH_NO,
                            S_CNTR_CODE = cgDetail.S_CNTR_CODE,
                            S_END_AREA = model.EndArea
                        };
                        if (db.Insertable<TN_Outbound_Detail>(detail).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            return NewSimpleResult(4, "生成出库单明细失败:" + JsonConvert.SerializeObject(detail));
                        }
                    }
                    var order = new TN_Outbound_Order {
                        S_NO = orderNo,
                        S_ITEM_CODE = model.ItemCode,
                        S_BATCH = model.BatchNo,
                        N_END_NUM = model.Qty,
                        //F_OUT_QTY = cgDetailList.Sum(a => a.N_QTY),
                        S_END_AREA = model.EndArea
                    };
                    if (db.Insertable<TN_Outbound_Order>(order).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(5, "生成出库单失败:" + JsonConvert.SerializeObject(order));
                    }
                    tran.CommitTran();
                }
                return NewSimpleResult(0, "生成出库单成功");
            }
            catch (Exception ex) {
                return BuildSimpleEx(ex);
            }
        }
        public static List<TN_CG_Detail> SelectCgByTotalQty(FinishedOutboundInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var result = new List<TN_CG_Detail>();
            var targetNum = model.Qty;
            var info = "";
            try {
                var targetCg = db.Queryable<TN_CG_Detail>().Where(a => a.S_ITEM_CODE == model.ItemCode
                && a.N_ITEM_NUM > targetNum).
                OrderBy(a => a.N_ITEM_NUM, OrderByType.Asc).First();
                if (targetCg != null) //刚好有一行满足条件
                {
                    result.Add(targetCg);
                    return result;
                }
                // NOTE 根据总量选detail时,是否需要考虑货位的高低?
                var sortedMaterials = new List<TN_CG_Detail>();
                if (model.ForcedOut) {
                    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.N_ITEM_NUM > 0)
                    .Where((d, c) => c.S_CNTR_TYPE == model.CntrType)
                    .Where(d => d.N_ITEM_STATE == 0 && d.S_ITEM_STATE == "合格"
                             || d.N_ITEM_STATE == 1 && d.S_ITEM_STATE == "待检")
                    .OrderBy(d => d.N_ITEM_NUM, OrderByType.Desc)
                    .OrderBy(d => d.N_ITEM_STATE, OrderByType.Asc).ToList();
                }
                else {
                    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.N_ITEM_NUM > 0)
                    .Where((d, c) => c.S_CNTR_TYPE == model.CntrType)
                    .Where(d => d.N_ITEM_STATE == 0 && d.S_ITEM_STATE == "合格")
                    .OrderBy(d => d.N_ITEM_NUM, OrderByType.Desc)
                    .OrderBy(d => d.N_ITEM_STATE, OrderByType.Asc).ToList();
                }
                if (sortedMaterials.Count == 0) //没有满足条件的
                {
                    return result;
                }
                int countNum = 0;
                foreach (var mat in sortedMaterials) {
                    countNum += mat.N_ITEM_NUM;
                    result.Add(mat);
                    if (countNum >= targetNum) {
                        break;
                    }
                }
                if (result.Sum(a => a.N_ITEM_NUM) >= targetNum) {
                    return result;
                }
                else {
                    result.Clear();
                    return result;
                }
            }
            catch (Exception ex) {
                info = $"发生了异常:{ex.Message}";
                LogHelper.Info(info);
                return result;
            }
        }
        /// <summary>
        /// 移库-创建移库任务(WMS)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public static SimpleResult CreateShiftOrder(CreateShiftOrderInfo model) {
            var billName = "抽检单";
            var db = new SqlHelper<object>().GetInstance();
            var info = "";
            try {
                var orderNo = GenerateOrderNo("抽检单号", "CN");
                var order = new TN_Check_Order {
                    S_NO = orderNo,
                    S_ITEM_CODE = model.ItemCode,
                    //S_ITEM_NAME = model.ItemName,
                    S_BATCH_NO = model.BatchNo,
                    //N_COUNT = model.Qty,
                    S_END_AREA = model.EndArea,
                };
                var cgDetailList = SelectShiftItem(model);
                if (cgDetailList.Count == 0) {
                    return NewSimpleResult(3, "没有符合要求的物料需要移库");
                }
                var detailList = new List<TN_Check_Detail>();
                foreach (var cgDetail in cgDetailList) {
                    var detail = new TN_Check_Detail {
                        S_NO = orderNo,
                        S_ITEM_CODE = cgDetail.S_ITEM_CODE,
                        S_BATCH_NO = cgDetail.S_BATCH_NO,
                        S_CNTR_CODE = cgDetail.S_CNTR_CODE,
                        S_END_AREA = model.EndArea,
                    };
                    detailList.Add(detail);
                    cgDetail.N_ITEM_STATE = 3;
                    cgDetail.S_ITEM_STATE = "抽验中";
                }
                using (var tran = db.Ado.UseTran()) {
                    if (db.Insertable<TN_Check_Order>(order).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"生成{billName}失败:" + JsonConvert.SerializeObject(order);
                        LogHelper.Info(info);
                        return NewSimpleResult(2, info);
                    }
                    if (db.Insertable<TN_Outbound_Detail>(detailList).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"生成{billName}明细失败";
                        LogHelper.Info(info);
                        return NewSimpleResult(4, info);
                    }
                    if (db.Updateable<TN_CG_Detail>(cgDetailList).UpdateColumns(it => new {
                        it.N_ITEM_STATE, it.S_ITEM_STATE }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"更新物料状态失败";
                    }
                    tran.CommitTran();
                }
                return NewSimpleResult(0, $"创建 抽检单 成功");
            }
            catch (Exception ex) {
                return BuildSimpleEx(ex);
            }
        }
        public static List<TN_CG_Detail> SelectShiftItem(CreateShiftOrderInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var result = new List<TN_CG_Detail>();
            var info = "";
            try {
                var targetItemList = db.Queryable<TN_CG_Detail>()
                    .Where(a => a.S_ITEM_CODE == model.ItemCode)
                    .Where(a => a.S_BATCH_NO == model.BatchNo)
                    .Where(a => a.N_ITEM_STATE != 3 && a.S_ITEM_CODE != "抽检中")
                    .OrderBy(a => a.N_ITEM_NUM, OrderByType.Asc)
                    .ToList();
                return targetItemList;
            }
            catch (Exception ex) {
                info = $"发生了异常:{ex.Message}";
                LogHelper.Info(info);
                return result;
            }
        }
        private static string GenerateOrderNo(string snType, string prefix) {
            var id = SYSHelper.GetSerialNumber(snType, prefix);
            var date = DateTime.Now.ToString("yyMMdd");
            return $"ON{date}{id.ToString().PadLeft(4, '0')}";
        }
        /// <summary>
        /// 博实物料信息下发同步
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public static WmsResult CgInfoSync(CgInfoSyncInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var random = new Random();
            try {
                var detail = new TN_CG_Detail {
                    S_ITEM_CODE = model.ItemCode,
                    S_ITEM_NAME = model.ItemName,
                    S_CNTR_CODE = Guid.NewGuid().ToString("D"), // NOTE 容器号:目前随机(后期可能会指定,或者PDA绑定时再填入)
                    S_BATCH_NO = model.BatchNo,
                    S_STANDARD = model.Standard,
                    S_NET_WEIGHT = model.NetWeight,
                    S_QUALITY_GRADE = model.QualityGrade,
                    //N_PRODUCT_LINE = random.Next(0, 3), // NOTE 产线号:目前随机(后期可能会指定,或者PDA绑定时再填入)
                };
                // 货位容器绑定的逻辑,在好运箱下线PDA的流程中操作
                //var locCntrRel = new TN_Loc_Container {
                //    //S_LOC_CODE = Settings.Config.ProductionLines[detail.N_PRODUCT_LINE].OffLoc[0], // 好运箱的位置是操作区,不是产线
                //    S_CNTR_CODE = detail.S_CNTR_CODE,
                //    S_CNTR_TYPE = "好运箱", // 贴标机只针对好运箱
                //};
                //using (var tran = db.Ado.UseTran()) {
                //    if (db.Insertable<TN_CG_Detail>(detail).ExecuteCommand() <= 0
                //        && db.Insertable<TN_Loc_Container>(detail).ExecuteCommand() <= 0) {
                //        tran.RollbackTran();
                //        return MesResultBuilder(2, "插入物料信息失败:" + JsonConvert.SerializeObject(detail));
                //    }
                //    tran.CommitTran();
                //}
                if (db.Insertable<TN_CG_Detail>(detail).ExecuteCommand() <= 0) {
                    return MesResultBuilder(2, "插入物料信息失败:" + JsonConvert.SerializeObject(detail));
                }
                return MesResultBuilder(0, "插入物料信息成功");
            }
            catch (Exception ex) {
                return MesResultBuilder(1, ex.Message);
            }
        }
    }
}
api/ApiModel.cs
New file
@@ -0,0 +1,573 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace HH.WCS.Mobox3.DSZSH.api {
    public class ApiModel {
        /// <summary>
        /// Mobox 接口返回数据类
        /// </summary>
        public class SimpleResult {
            public int resultCode { get; set; }
            public string resultMsg { get; set; }
            public List<object> result { get; set; } = new List<object>();
        }
        /// <summary>
        /// 构建 <see cref="SimpleResult"/> 返回值
        /// </summary>
        /// <param name="code"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public static SimpleResult NewSimpleResult(int code, string message) {
            return new SimpleResult { resultCode = code, resultMsg = message };
        }
        /// <summary>
        /// 构建 <see cref="SimpleResult"/> 异常返回值,选择打印异常日志信息(默认打印)
        /// </summary>
        /// <param name="ex"></param>
        /// <param name="exCode"></param>
        /// <param name="pringLog"></param>
        /// <returns></returns>
        public static SimpleResult BuildSimpleEx(Exception ex, int exCode = 1, bool pringLog = true) {
            if (pringLog) {
                LogHelper.InfoEx(ex);
            }
            return new SimpleResult { resultCode = exCode, resultMsg = ex.Message };
        }
        /// <summary>
        /// HostToAGV 上报任务状态
        /// </summary>
        public class AgvTaskState {
            /// <summary>
            /// AGV 回报状态号
            /// </summary>
            public int state { get; set; }
            /// <summary>
            /// 任务号
            /// </summary>
            public string task_no { get; set; }
            /// <summary>
            /// AGV 车号
            /// </summary>
            public string forklift_no { get; set; }
            /// <summary>
            /// 安全门编号
            /// </summary>
            public string lock_no { get; set; }
            /// <summary>
            /// 附加信息
            /// </summary>
            public string ext_data { get; set; }
        }
        public class SafetyInteractionInfo {
            public int station_id { get; set; }
            /// <summary>
            /// 请求上线/下线的的站台库位名称,例如work6、work8
            /// </summary>
            public string station_name { get; set; }
            /// <summary>
            /// 请求码
            /// </summary>
            public string apply_code { get; set; }
            public string task_no { set; get; }
        }
        /// <summary>
        /// 返回给 HostToAgv
        /// </summary>
        public class ReturnResult {
            public int ResultCode { get; set; }
            public string ResultMsg { get; set; }
        }
        public class orderStatusReportParme {
            /// <summary>
            /// 订单ID
            /// </summary>
            public int orderID { get; set; }
            /// <summary>
            /// 订单名
            /// </summary>
            public string orderName { get; set; }
            /// <summary>
            /// 订单状态
            /// </summary>
            public string orderStatus { get; set; }
            /// <summary>
            /// agv车号列表
            /// </summary>
            public string agvIDList { get; set; }
            /// <summary>
            /// 优先级
            /// </summary>
            public string priority { get; set; }
            /// <summary>
            /// 订单当前的目的地
            /// </summary>
            public string currentDes { get; set; }
            /// <summary>
            /// 当前指令
            /// </summary>
            public string currentCmd { get; set; }
            /// <summary>
            /// 错误码
            /// </summary>
            public int errorCode { get; set; }
            /// <summary>
            /// 订单的截至时间
            /// </summary>
            public string deadLine { get; set; }
            /// <summary>
            /// 订单的创建时间
            /// </summary>
            public string createdTime { get; set; }
            /// <summary>
            /// 额外信息1
            /// </summary>
            public string extraInfo1 { get; set; }
            /// <summary>
            /// 额外信息2
            /// </summary>
            public string extraInfo2 { get; set; }
        }
        /// <summary>
        /// 返回给GZ
        /// </summary>
        public class GzResult {
            public int resultCode { get; set; }
            public string msg { get; set; }
            public int orderID { get; set; }
        }
    }
    public class OtherModel {
        #region PDA 数据
        /// <summary>
        /// 好运箱-满托下线入库(PDA)数据类
        /// </summary>
        public class GoodpackOfflineInfo {
            /// <summary>
            /// 物料编码
            /// </summary>
            public string s_item_code { get; set; }
            ///// <summary>
            ///// 物料名称
            ///// </summary>
            //[JsonProperty("item_name")]
            //public string ItemName { get; set; }
            /// <summary>
            /// 批次号
            /// </summary>
            public string s_batch { get; set; }
            /// <summary>
            /// 物料规格
            /// </summary>
            public string s_spec { get; set; }
            /// <summary>
            /// 数量
            /// </summary>
            public int n_num { get; set; }
            /// <summary>
            /// 起点货位信息
            /// </summary>
            public string s_start_loc { get; set; }
        }
        /// <summary>
        /// 空托/空箱入库绑定(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("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>
            /// 容器编码
            /// </summary>
            public string CntrCode { get; set; }
            /// <summary>
            /// 托盘数量
            /// </summary>
            public int PalletCount { get; set; }
            /// <summary>
            /// 货位编码
            /// </summary>
            public string LocCode { get; set; }
        }
        /// <summary>
        /// 空箱绑定
        /// </summary>
        public class EmptyBindGoodpackInfo {
            /// <summary>
            /// 容器编码
            /// </summary>
            public string CntrCode { get; set; }
            /// <summary>
            /// 货位编码
            /// </summary>
            public string LocCode { get; set; }
        }
        /// <summary>
        /// 空托/空箱入库(PDA)数据类
        /// </summary>
        public class EmptyInboundInfo {
            /// <summary>
            /// 容器编码
            /// </summary>
            [JsonProperty("cntr_code")]
            public string CntrCode { get; set; }
            /// <summary>
            /// 容器类型
            /// </summary>
            [JsonProperty("cntr_type")]
            public string CntrType { get; set; }
            /// <summary>
            /// 终点库区编码
            /// </summary>
            [JsonProperty("end_area")]
            public string EndArea { get; set; }
            /// <summary>
            /// 起点货位
            /// </summary>
            [JsonProperty("start_loc")]
            public string StartLoc { get; set; }
        }
        public class EmptyInboundDetailInfo { }
        //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 {
            /// <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("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; }
        }
        /// <summary>
        /// 抽检-合格回库(PDA) 数据类
        /// </summary>
        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; }
        }
        public class CheckShiftInfo : UnqualifiedShiftInfo {
            public bool Qualified { get; set; }
        }
        #endregion
        #region WMS 数据
        /// <summary>
        /// 成品胶出库(PDA)
        /// </summary>
        public class FinishedOutboundInfo {
            /// <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; }
            /// <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
        /// <summary>
        /// 物料信息下发同步 数据类
        /// </summary>
        public class CgInfoSyncInfo {
            /// <summary>
            /// 物料名称
            /// </summary>
            [JsonProperty("itemName")]
            public string ItemName { get; set; }
            /// <summary>
            /// 产品牌号
            /// </summary>
            [JsonProperty("itemCode")]
            public string ItemCode { get; set; }
            /// <summary>
            /// 批次号
            /// </summary>
            [JsonProperty("batchNo")]
            public string BatchNo { get; set; }
            /// <summary>
            /// 执行标准
            /// </summary>
            [JsonProperty("standard")]
            public string Standard { get; set; }
            /// <summary>
            /// 净含量
            /// </summary>
            [JsonProperty("netWeight")]
            public string NetWeight { get; set; }
            /// <summary>
            /// 质量等级
            /// </summary>
            [JsonProperty("qualityGrade")]
            public string QualityGrade { get; set; }
        }
        /// <summary>
        /// MES API 响应结果类
        /// </summary>
        public class WmsResult {
            /// <summary>
            /// 接口调用结果  1-成功  0-失败
            /// </summary>
            [JsonProperty("result")]
            public int Result { get; set; }
            /// <summary>
            /// 是否成功 True-成功,False:失败
            /// </summary>
            [JsonProperty("success")]
            public bool Success { get; set; }
            /// <summary>
            /// 这里是string类型,对结果的描述
            /// </summary>
            [JsonProperty("data")]
            public string Data { get; set; }
        }
        public static WmsResult MesResultBuilder(int code, string message = "", bool printLog = true) {
            if (printLog && string.IsNullOrEmpty(message)) {
                LogHelper.Info(message);
            }
            return new WmsResult {
                Result = code,
                Success = code == 0, // 仅当code=0时,success=true
                Data = message,
            };
        }
    }
}
api/DebugController.cs
New file
@@ -0,0 +1,216 @@
using System;
using System.Collections.Generic;
using System.Runtime.ConstrainedExecution;
using System.Web.Http;
using HH.WCS.Mobox3.DSZSH.models;
using HH.WCS.Mobox3.DSZSH.util;
using Newtonsoft.Json;
using static HH.WCS.Mobox3.DSZSH.api.ApiModel;
namespace HH.WCS.Mobox3.DSZSH.api {
    /// <summary>
    /// 测试用:如果项目中要和设备对接,前期设备无法测试,用接口模拟
    /// </summary>
    [RoutePrefix("api")]
    public class DebugController : ApiController
    {
        /// <summary>
        /// 模拟 AGV 多次回报任务状态
        /// </summary>
        /// <param name="model">容器号</param>
        /// <returns></returns>
        [HttpPost]
        [Route("AgvSeriesReports")]
        public ReturnResults AgvSeriesReports(UpdateTaskState model)
        {
            return new ReturnResults();
        }
        /// <summary>
        /// 初始化数据库
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [Route("CreateDatabase")]
        public string CreateDatabase(CoverInfo model) {
            var cover = model.IsCover;
            try {
                var db = new SqlHelper<object>().GetInstance();
                var entityTypes = new Type[] {
                    //typeof(TN_CAR_IN),
                    //typeof(TN_CG_Detail),
                    //typeof(TN_Container),
                    //typeof(TN_Loc_Container),
                    //typeof(TN_Location),
                    //typeof(TN_Task),
                    //typeof(TN_Task_Action),
                    //typeof(SysHelper.OI_SYS_MAXID),
                    //typeof(TN_Inbound_Order),
                    typeof(TN_Check_Detail),
                    //typeof(TN_Check_Order),
                    //typeof(TN_CNTR_ITEM),
                    //typeof(TN_Outbound_Detail),
                    //typeof(TN_Outbound_Order),
                    //typeof(TN_Shift_Order),
                    //typeof(TN_Shift_Detail)
                };
                using (var tran = db.Ado.UseTran()) {
                    if (cover) {
                        // 删除所有表(按依赖关系倒序)
                        //var tables = db.DbMaintenance.GetTableInfoList();
                        //foreach (var table in tables.OrderByDescending(t => t.Name)) {
                        //    db.DbMaintenance.DropTable(table.Name);
                        //}
                        // 创建新表
                        db.CodeFirst.InitTables(entityTypes);
                        //db.CodeFirst.BackupTable().InitTables(entityTypes);
                    }
                    else {
                        db.CodeFirst.InitTables(entityTypes);
                    }
                    tran.CommitTran();
                }
            }
            catch (Exception ex) {
                LogHelper.Info($"发生了异常");
                return "初始化数据库错误" + ex.Message;
            }
            return "成功";
        }
        /// <summary>
        /// DEBUG:插入货位、容器、货品信息
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [Route("InsertLocCntrCg")]
        public string InsertLocCntrCg(LocCntrCg locCntrCg) {
            var db = new SqlHelper<object>().GetInstance();
            try {
                using (var tran = db.UseTran()) {
                    LogHelper.Info("LogCntrCg:" + JsonConvert.SerializeObject(locCntrCg));
                    if (string.IsNullOrEmpty(locCntrCg.LocCode)) return "";
                    var loc = db.Queryable<TN_Location>().First(a => a.S_CODE == locCntrCg.LocCode);
                    if (loc == null) {
                        var newLoc = new TN_Location {
                            S_CODE = locCntrCg.LocCode,
                            S_AREA_CODE = locCntrCg.LocArea ?? ""
                        };
                        if (db.Insertable<TN_Location>(newLoc).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            LogHelper.Info($"插入位置{locCntrCg.LocCode}失败");
                            return "插入失败";
                        }
                        loc = newLoc;
                    }
                    if (string.IsNullOrEmpty(locCntrCg.CntrCode)) {
                        LogHelper.Info("容器号为空,不再读取后面的数据");
                        return "";
                    }
                    var locCntrRel = db.Queryable<TN_Loc_Container>().First(a => a.S_LOC_CODE == locCntrCg.LocCode
                        && a.S_CNTR_CODE == locCntrCg.CntrCode);
                    if (locCntrRel == null) {
                        var newLocCntrRel = new TN_Loc_Container {
                            S_LOC_CODE = locCntrCg.LocCode,
                            S_CNTR_CODE = locCntrCg.CntrCode,
                            S_CNTR_TYPE = locCntrCg.CntrType ?? ""
                        };
                        loc.N_CURRENT_NUM = 1;
                        if (db.Insertable<TN_Loc_Container>(newLocCntrRel).ExecuteCommand() <= 0
                            && db.Updateable<TN_Location>(loc).UpdateColumns(c => c.N_CURRENT_NUM).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            LogHelper.Info($"插入位置托盘关系{locCntrCg.LocCode}-{locCntrCg.CntrCode}失败");
                            return "插入失败";
                        }
                    }
                    if (string.IsNullOrEmpty(locCntrCg.ItemCode)) {
                        LogHelper.Info("物料号为空,不再读取后面的数据");
                        return "";
                    }
                    var cgDetail = db.Queryable<TN_CG_Detail>().First(a => a.S_CNTR_CODE == locCntrCg.CntrCode
                        && a.S_ITEM_CODE == locCntrCg.ItemCode);
                    if (cgDetail == null) {
                        var locList = new List<TN_CG_Detail>();
                        locList.Add(new TN_CG_Detail { S_CNTR_CODE = locCntrCg.CntrCode, S_ITEM_CODE = locCntrCg.ItemCode, S_BATCH_NO = locCntrCg.BatchNo ?? "" });
                        if (db.Insertable<TN_CG_Detail>(locList).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            LogHelper.Info($"插入托盘物料关系{locCntrCg.CntrCode}-{locCntrCg}失败");
                            return "插入失败";
                        }
                    }
                    tran.CommitTran();
                }
                return "插入数据成功";
            }
            catch (Exception ex) {
                return $"Error reading CSV file: {ex.Message}";
            }
        }
    }
    /// <summary>
    /// 模拟 AGV 传递信号,用于更改任务状态
    /// </summary>
    public class UpdateTaskState {
        /// <summary>
        /// 任务ID
        /// </summary>
        public string TaskID { set; get; }
        /// <summary>
        /// AGV 小车号
        /// </summary>
        public string ForkliftNo { set; get; }
        /// <summary>
        /// AGV 下一个状态
        /// </summary>
        public int NextState { set; get; }
    }
    public class CoverInfo {
        public bool IsCover { set; get; } = false;
    }
    /// <summary>
    ///
    /// </summary>
    public class ReturnResults {
        public List<ReturnResult> ResultList { set; get; }
    }
    public class LocCntrCg {
        public string Note { get; set; } // 仅用于备注
        public string LocCode { get; set; }
        public string LocArea { get; set; }
        public string CntrCode { get; set; }
        public string CntrType { get; set; }
        public string ItemCode { get; set; }
        public string BatchNo { get; set; }
    }
}
api/ErpController.cs
File was renamed from Controllers/ErpController.cs
@@ -1,11 +1,10 @@
using System;
using HH.WCS.Mobox3.DSZSH.AppStart;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Web.Http;
namespace HH.WCS.Mobox3.DSZSH.Controllers {
namespace HH.WCS.Mobox3.DSZSH.api {
    /// <summary>
    /// ERP 调用的接口
    /// </summary>
api/MesController.cs
File was renamed from Controllers/MesController.cs
@@ -5,9 +5,7 @@
using System.Threading.Tasks;
using System.Web.Http;
using HH.WCS.Mobox3.DSZSH.Services;
namespace HH.WCS.Mobox3.DSZSH.Controllers {
namespace HH.WCS.Mobox3.DSZSH.api {
    /// <summary>
    /// MES 调用的接口
    /// </summary>
api/MoboxController.cs
File was renamed from Controllers/MoboxController.cs
@@ -1,23 +1,18 @@
using System.Web.Http;
using HH.WCS.Mobox3.DSZSH.Consts;
using HH.WCS.Mobox3.DSZSH.Helpers;
using HH.WCS.Mobox3.DSZSH.Models;
using HH.WCS.Mobox3.DSZSH.Services;
using HH.WCS.Mobox3.DSZSH.models;
using Newtonsoft.Json;
using static HH.WCS.Mobox3.DSZSH.Dtos.Request.MoboxRequest;
using static HH.WCS.Mobox3.DSZSH.Dtos.Response.MoboxResponse;
using static HH.WCS.Mobox3.DSZSH.api.ApiModel;
using static HH.WCS.Mobox3.DSZSH.api.OtherModel;
namespace HH.WCS.Mobox3.DSZSH.Controllers {
namespace HH.WCS.Mobox3.DSZSH.api {
    /// <summary>
    /// Mobox3 调用,脚本中调用(包括 PDA 的接口)
    /// </summary>
    [RoutePrefix("api")]
    public class MoboxController : ApiController {
        #region PDA 接口
        /// <summary>
        /// 好运箱-满托下线入库(PDA)
        /// </summary>
@@ -27,7 +22,7 @@
        [Route("goodpack-offline")]
        public SimpleResult GoodpackOffline(GoodpackOfflineInfo model) {
            LogHelper.InfoApi("好运箱-满托下线入库(PDA)", model);
            return MoboxService.GoodpackOffline(model);
            return ApiHelper.GoodpackOffline(model);
        }
        ///// <summary>
@@ -40,11 +35,11 @@
        //    if (model.CntrType == "托盘") {
        //        //LogHelper.Info($"触发API:空托绑定 " + JsonConvert.SerializeObject(model), "API");
        //        return MoboxService.EmptyBindPallet(model);
        //        return ApiHelper.EmptyBindPallet(model);
        //    }
        //    else if (model.CntrType == "好运箱") {
        //        //LogHelper.Info($"触发API:空箱绑定 " + JsonConvert.SerializeObject(model), "API");
        //        return MoboxService.EmptyBindGoodpack(model);
        //        return ApiHelper.EmptyBindGoodpack(model);
        //    }
        //    else {
        //        return BuildSimpleResult(-1, $"不合法的容器类型:'{model.CntrType}'");
@@ -62,13 +57,13 @@
            LogHelper.InfoApi("空托/空箱入库", model);
            if (model.CntrType == "托盘") {
                return MoboxService.EmptyInboundPallet(model);
                return ApiHelper.EmptyInboundPallet(model);
            }
            else if (model.CntrType == "好运箱") {
                return MoboxService.EmptyInboundGoodpack(model);
                return ApiHelper.EmptyInboundGoodpack(model);
            }
            else {
                return BuildSimpleResult(-1, $"容器类型 '{model.CntrType}' 不合法:应为 '托盘' 或 '好运箱'");
                return NewSimpleResult(-1, $"容器类型 '{model.CntrType}' 不合法:应为 '托盘' 或 '好运箱'");
            }
        }
@@ -87,14 +82,14 @@
        //    if (locCntrRel.S_CNTR_TYPE == "托盘") {
        //        return MoboxService.EmptyOnlinePallet(new EmptyOnlinePalletInfo {
        //        return ApiHelper.EmptyOnlinePallet(new EmptyOnlinePalletInfo {
        //            CntId = locCntrRel.S_CNTR_CODE,
        //            EndLoc = model.EndLoc
        //        });
        //    }
        //    else if (locCntrRel.S_CNTR_TYPE == "好运箱") {
        //        return MoboxService.EmptyOnlineGoodpack(new EmptyOnlineGoodpackInfo {
        //        return ApiHelper.EmptyOnlineGoodpack(new EmptyOnlineGoodpackInfo {
        //            CntId = locCntrRel.S_CNTR_CODE,
        //            EndLoc = model.EndLoc
        //        });
@@ -112,7 +107,7 @@
        [HttpPost]
        [Route("empty-online-pallet")]
        public SimpleResult EmptyOnlinePallet(EmptyOnlinePalletInfo model) {
            return MoboxService.EmptyOnlinePallet(model);
            return ApiHelper.EmptyOnlinePallet(model);
        }
        /// <summary>
@@ -123,7 +118,7 @@
        [HttpPost]
        [Route("empty-online-goodpack")]
        public SimpleResult EmptyOnlineGoodpack(EmptyOnlineGoodpackInfo model) {
            return MoboxService.EmptyOnlineGoodpack(model);
            return ApiHelper.EmptyOnlineGoodpack(model);
        }
        ///// <summary>
@@ -137,10 +132,10 @@
        //    LogHelper.InfoApi("合格回库/不合格移库", model);
        //    if (model.Qualified) {
        //        return MoboxService.QualifiedBack(model);
        //        return ApiHelper.QualifiedBack(model);
        //    }
        //    else {
        //        return MoboxService.UnqualifiedShift(model);
        //        return ApiHelper.UnqualifiedShift(model);
        //    }
        //}
@@ -151,7 +146,7 @@
        [HttpPost]
        [Route("qualified-back")]
        public SimpleResult QualifiedBack(QualifiedBackInfo model) {
            return MoboxService.QualifiedBack(model);
            return ApiHelper.QualifiedBack(model);
        }
        /// <summary>   
@@ -161,7 +156,7 @@
        [HttpPost]
        [Route("unqualified-shift")]
        public SimpleResult UnqualifiedShift(UnqualifiedShiftInfo model) {
            return MoboxService.UnqualifiedShift(model);
            return ApiHelper.UnqualifiedShift(model);
        }
        /// <summary>
@@ -172,7 +167,7 @@
        [HttpPost]
        [Route("rest-back")]
        public SimpleResult RestBack(RestBackInfo model) {
            return MoboxService.RestBack(model);
            return ApiHelper.RestBack(model);
        }
        /// <summary>
@@ -186,15 +181,13 @@
            LogHelper.InfoApi("成品胶出库(PDA)", model);
            if (model.ForcedOut) {
                return MoboxService.FinishedOutboundForce(model);
                return ApiHelper.FinishedOutboundForce(model);
            }
            else {
                return MoboxService.FinishedOutbound(model);
                return ApiHelper.FinishedOutbound(model);
            }
        }
        #endregion
        #region Mobox 接口
        /// <summary>
        /// 抽检-创建抽检单(WMS)
        /// </summary>
@@ -203,7 +196,7 @@
        [HttpPost]
        [Route("create-check-order")]
        public SimpleResult CreateCheckOrder(CreateCheckOrderInfo model) {
            return MoboxService.CreateCheckOrder(model);
            return ApiHelper.CreateCheckOrder(model);
        }
        /// <summary>
@@ -214,8 +207,7 @@
        [HttpPost]
        [Route("shift-storage")]
        public SimpleResult CreateShiftOrder(CreateShiftOrderInfo model) {
            return MoboxService.CreateShiftOrder(model);
            return ApiHelper.CreateShiftOrder(model);
        }
        #endregion
    }
}
api/WMSController.cs
File was renamed from Controllers/WmsController.cs
@@ -1,14 +1,10 @@
using HH.WCS.Mobox3.DSZSH.AppStart;
using HH.WCS.Mobox3.DSZSH.Services;
using Newtonsoft.Json;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Web.Http;
using static HH.WCS.Mobox3.DSZSH.Dtos.Request.WmsRequest;
using static HH.WCS.Mobox3.DSZSH.Dtos.Response.WmsResponse;
using static HH.WCS.Mobox3.DSZSH.api.OtherModel;
namespace HH.WCS.Mobox3.DSZSH.Controllers
namespace HH.WCS.Mobox3.DSZSH.api
{
    /// <summary>
    /// 第三方调用的接口
@@ -23,7 +19,7 @@
        [HttpPost]
        //[Route("WMS/CgInfoSync")]
        public WmsResult CgInfoSync(CgInfoSyncInfo model) {
            return WmsService.CgInfoSync(model);
            return ApiHelper.CgInfoSync(model);
        }
    }
}
core/Monitor.cs
New file
@@ -0,0 +1,395 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HH.WCS.Mobox3.DSZSH.models;
using HH.WCS.Mobox3.DSZSH.util;
using HH.WCS.Mobox3.DSZSH.wms;
using Newtonsoft.Json;
namespace HH.WCS.Mobox3.DSZSH.core {
    public class Monitor {
        public static void CheckOutboundOrder() {
            var taskName = TaskName.成品胶出库;
            var db = new SqlHelper<object>().GetInstance();
            var info = "";
            try {
                var orderList = db.Queryable<TN_Outbound_Order>()
                    .Where(c => c.N_B_STATE == 1)
                    .OrderBy(c => c.T_CREATE, SqlSugar.OrderByType.Asc)
                    .ToList();
                if (orderList.Count == 0) {
                    LogHelper.Info("轮询--出库--暂无待执行的Order");
                    return;
                }
                var detailList = new List<TN_Outbound_Detail>();
                foreach (var order in orderList) {
                    var doingCount = db.Queryable<TN_Outbound_Detail>()
                        .Count(d => d.S_OO_NO == order.S_NO && d.N_B_STATE >= 2); // 执行中
                    var allCount = db.Queryable<TN_Outbound_Detail>()
                        .Count(d => d.S_OO_NO == order.S_NO);
                    LogHelper.Info($"轮询--出库--统计出库单'{order.S_NO}'任务已下发:{doingCount}/{allCount}");
                    if (doingCount == allCount) {
                        order.N_B_STATE = 2; // 所有任务都已执行
                        db.Updateable<TN_Outbound_Order>(order).UpdateColumns(it => new { it.N_B_STATE }).ExecuteCommand();
                        continue;
                    }
                    var lastDetail = db.Queryable<TN_Outbound_Detail>()
                        .Where(d => d.S_OO_NO == order.S_NO && d.N_B_STATE == 2) // TODO 或者改成查task
                        .First();
                    if (lastDetail != null) {
                        LogHelper.Info($"轮询--出库--出库单'{order.S_NO}'上一个任务仍在进行中:" + JsonConvert.SerializeObject(lastDetail));
                        continue;
                    }
                    var outboundDetail = db.Queryable<TN_Outbound_Detail>()
                        .Where(a => a.S_OO_NO == order.S_NO && a.N_B_STATE == 1) // 已下发
                        .First();
                    if (outboundDetail != null) {
                        LogHelper.Info($"轮询--出库--");
                        continue;
                    }
                    detailList.Add(outboundDetail);
                }
                if (detailList.Count == 0) {
                    return;
                }
                var startLocList = new List<TN_Location>();
                var endLocList = new List<TN_Location>();
                var taskList = new List<TN_Task>();
                foreach (var detail in detailList) {
                    var startLoc = db.Queryable<TN_Location>()
                        .LeftJoin<TN_Loc_Container>((l, c) => l.S_CODE == c.S_LOC_CODE)
                        .Where((l, c) => c.S_CNTR_CODE == detail.S_CNTR_CODE)
                        .First();
                    if (startLoc == null) {
                        LogHelper.Info($"轮询--出库:没有找到合适的起点货位!");
                        continue;
                    }
                    var endLoc = db.Queryable<TN_Location>()
                        .Where(a => a.S_AREA_CODE == detail.S_END_AREA)
                        .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y")
                        .Where(a => a.N_CURRENT_NUM == 0).First();
                    if (endLoc == null) {
                        LogHelper.Info($"轮询--出库:没有找到合适的终点货位!S_NO为 '{detail.S_OO_NO}',要求Area为 '{detail.S_END_AREA}'");
                        continue;
                    }
                    detail.N_B_STATE = 2;
                    var cntId = detail.S_CNTR_CODE;
                    var task = WCSHelper.BuildTask(startLoc, endLoc, cntId, taskName);
                    LocationHelper.LockLoc(ref startLoc, 1); // 起点出库锁
                    LocationHelper.LockLoc(ref endLoc, 2); // 终点入库锁
                    using (var tran = db.Ado.UseTran()) {
                        if (db.Updateable<TN_Outbound_Detail>(detail).UpdateColumns(it => it.N_B_STATE).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            LogHelper.Info($"轮询--出库--修改明细表状态为完成失败!");
                        }
                        if (db.Updateable<TN_Location>(startLoc).UpdateColumns(it => new {
                            it.N_LOCK_STATE,
                            it.S_LOCK_STATE,
                            it.S_LOCK_OP,
                            it.T_MODIFY
                        }).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货位 {endLoc.S_CODE}";
                            LogHelper.Info(info);
                            continue;
                        }
                        if (db.Updateable<TN_Location>(endLoc).UpdateColumns(it => new {
                            it.N_LOCK_STATE,
                            it.S_LOCK_STATE,
                            it.S_LOCK_OP,
                            it.T_MODIFY
                        }).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货位 {endLoc.S_CODE}";
                            LogHelper.Info(info);
                            continue;
                        }
                        if (db.Insertable<TN_Task>(task).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货位 {endLoc.S_CODE}";
                            LogHelper.Info(info);
                            continue;
                        }
                        tran.CommitTran();
                        info = $"生成 {taskName} 成功,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货位 {endLoc.S_CODE}";
                        LogHelper.Info(info);
                        continue;
                    }
                }
            }
            catch (Exception ex) {
                LogHelper.InfoEx(ex);
            }
        }
        public static void CheckCheckOrder() {
            var taskName = TaskName.抽检_出库;
            var db = new SqlHelper<object>().GetInstance();
            var info = "";
            try {
                var orderList = db.Queryable<TN_Check_Order>()
                    .Where(c => c.N_B_STATE == 1)
                    .OrderBy(c => c.T_CREATE, SqlSugar.OrderByType.Asc)
                    .ToList();
                if (orderList.Count == 0) {
                    LogHelper.Info($"轮询--{taskName}--暂无待执行的{taskName}单");
                    return;
                }
                var detailList = new List<TN_Check_Detail>();
                foreach (var order in orderList) {
                    var doingCount = db.Queryable<TN_Check_Detail>()
                        .Count(d => d.S_NO == order.S_NO && d.N_B_STATE >= 2); // 执行中
                    var allCount = db.Queryable<TN_Check_Detail>()
                        .Count(d => d.S_NO == order.S_NO);
                    LogHelper.Info($"轮询--{taskName}--统计{taskName}单'{order.S_NO}'任务已下发:{doingCount}/{allCount}");
                    if (doingCount == allCount) {
                        order.N_B_STATE = 2; // 所有任务都已执行
                        db.Updateable<TN_Check_Order>(order).UpdateColumns(it => new { it.N_B_STATE }).ExecuteCommand();
                        continue;
                    }
                    var checkDetailList = db.Queryable<TN_Check_Detail>()
                        .Where(a => a.S_NO == order.S_NO && a.N_B_STATE == 1) // 已下发
                        .ToList();
                    if (checkDetailList.Count == 0) {
                        LogHelper.Info($"轮询--");
                        continue;
                    }
                    foreach (var checkDetail in checkDetailList) {
                        detailList.Add(checkDetail);
                    }
                }
                foreach (var detail in detailList) {
                    var startLoc = db.Queryable<TN_Location>()
                        .LeftJoin<TN_Loc_Container>((l, c) => l.S_CODE == c.S_LOC_CODE)
                        .Where((l, c) => c.S_CNTR_CODE == detail.S_CNTR_CODE)
                        .First();
                    if (startLoc == null) {
                        LogHelper.Info($"轮询--{taskName}:没有找到合适的起点货位!");
                        continue;
                    }
                    var endLoc = db.Queryable<TN_Location>()
                        .Where(l => l.S_AREA_CODE == detail.S_END_AREA)
                        .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                        .Where(a => a.N_CURRENT_NUM == 0).First();
                    if (endLoc == null) {
                        LogHelper.Info($"轮询--{taskName}:没有找到合适的终点货位!");
                        continue;
                    }
                    detail.N_B_STATE = 2;
                    var cntId = detail.S_CNTR_CODE;
                    var task = WCSHelper.BuildTask(startLoc, endLoc, cntId, taskName);
                    LocationHelper.LockLoc(ref startLoc, 1); // 起点出库锁
                    LocationHelper.LockLoc(ref endLoc, 2); // 终点入库锁
                    using (var tran = db.Ado.UseTran()) {
                        if (db.Updateable<TN_Check_Detail>(detail).UpdateColumns(it => it.N_B_STATE).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            LogHelper.Info($"轮询--{taskName}:修改{taskName}单明细表状态为完成--失败!");
                            continue;
                        }
                        if (db.Updateable<TN_Location>(startLoc).UpdateColumns(it => new {
                            it.N_LOCK_STATE,
                            it.S_LOCK_STATE,
                            it.S_LOCK_OP,
                            it.T_MODIFY
                        }).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货位 {endLoc.S_CODE}";
                            LogHelper.Info(info);
                            continue;
                        }
                        if (db.Updateable<TN_Location>(endLoc).UpdateColumns(it => new {
                            it.N_LOCK_STATE,
                            it.S_LOCK_STATE,
                            it.S_LOCK_OP,
                            it.T_MODIFY
                        }).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货位 {endLoc.S_CODE}";
                            LogHelper.Info(info);
                            continue;
                        }
                        if (db.Insertable<TN_Task>(task).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货位 {endLoc.S_CODE}";
                            LogHelper.Info(info);
                            continue;
                        }
                        tran.CommitTran();
                        info = $"生成 {taskName} 成功,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货位 {endLoc.S_CODE}";
                        LogHelper.Info(info);
                        continue;
                    }
                }
            }
            catch (Exception ex) {
                LogHelper.InfoEx(ex);
            }
        }
        public static void CheckShiftOrder() {
            var taskName = TaskName.移库;
            var db = new SqlHelper<object>().GetInstance();
            var info = "";
            try {
                var orderList = db.Queryable<TN_Shift_Order>()
                    .Where(c => c.N_B_STATE == 1)
                    .OrderBy(c => c.T_CREATE, SqlSugar.OrderByType.Asc)
                    .ToList();
                if (orderList.Count == 0) {
                    LogHelper.Info($"轮询--{taskName}--暂无待执行的{taskName}单");
                    return;
                }
                var detailList = new List<TN_Shift_Detail>();
                foreach (var order in orderList) {
                    var doingCount = db.Queryable<TN_Shift_Detail>()
                        .Count(d => d.S_NO == order.S_NO && d.N_B_STATE >= 2); // 执行中
                    var allCount = db.Queryable<TN_Shift_Detail>()
                        .Count(d => d.S_NO == order.S_NO);
                    LogHelper.Info($"轮询--{taskName}--统计{taskName}单'{order.S_NO}'任务已下发:{doingCount}/{allCount}");
                    if (doingCount == allCount) {
                        order.N_B_STATE = 2; // 所有任务都已执行
                        db.Updateable<TN_Shift_Order>(order).UpdateColumns(it => new { it.N_B_STATE }).ExecuteCommand();
                        continue;
                    }
                    var checkDetailList = db.Queryable<TN_Shift_Detail>()
                        .Where(a => a.S_NO == order.S_NO && a.N_B_STATE == 1) // 已下发
                        .ToList();
                    if (checkDetailList.Count == 0) {
                        LogHelper.Info($"轮询--");
                        continue;
                    }
                    foreach (var checkDetail in checkDetailList) {
                        detailList.Add(checkDetail);
                    }
                }
                foreach (var detail in detailList) {
                    var startLoc = db.Queryable<TN_Location>()
                        .LeftJoin<TN_Loc_Container>((l, c) => l.S_CODE == c.S_LOC_CODE)
                        .Where((l, c) => c.S_CNTR_CODE == detail.S_CNTR_CODE)
                        .First();
                    if (startLoc == null) {
                        LogHelper.Info($"轮询--{taskName}:没有找到合适的起点货位!");
                        continue;
                    }
                    var endLoc = db.Queryable<TN_Location>()
                        .Where(l => l.S_AREA_CODE == detail.S_END_AREA)
                        .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                        .Where(a => a.N_CURRENT_NUM == 0).First();
                    if (endLoc == null) {
                        LogHelper.Info($"轮询--{taskName}:没有找到合适的终点货位!");
                        continue;
                    }
                    detail.N_B_STATE = 2;
                    var cntId = detail.S_CNTR_CODE;
                    var task = WCSHelper.BuildTask(startLoc, endLoc, cntId, taskName);
                    LocationHelper.LockLoc(ref startLoc, 1); // 起点出库锁
                    LocationHelper.LockLoc(ref endLoc, 2); // 终点入库锁
                    using (var tran = db.Ado.UseTran()) {
                        if (db.Updateable<TN_Shift_Detail>(detail).UpdateColumns(it => it.N_B_STATE).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            LogHelper.Info($"轮询--{taskName}:修改{taskName}单明细表状态为完成--失败!");
                            continue;
                        }
                        if (db.Updateable<TN_Location>(startLoc).UpdateColumns(it => new {
                            it.N_LOCK_STATE,
                            it.S_LOCK_STATE,
                            it.S_LOCK_OP,
                            it.T_MODIFY
                        }).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货位 {endLoc.S_CODE}";
                            LogHelper.Info(info);
                            continue;
                        }
                        if (db.Updateable<TN_Location>(endLoc).UpdateColumns(it => new {
                            it.N_LOCK_STATE,
                            it.S_LOCK_STATE,
                            it.S_LOCK_OP,
                            it.T_MODIFY
                        }).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货位 {endLoc.S_CODE}";
                            LogHelper.Info(info);
                            continue;
                        }
                        if (db.Insertable<TN_Task>(task).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"生成 {taskName} 失败,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货位 {endLoc.S_CODE}";
                            LogHelper.Info(info);
                            continue;
                        }
                        tran.CommitTran();
                        info = $"生成 {taskName} 成功,容器号 {cntId} ,起点 {startLoc.S_CODE} ,终点货位 {endLoc.S_CODE}";
                        LogHelper.Info(info);
                        continue;
                    }
                }
            }
            catch (Exception ex) {
                LogHelper.InfoEx(ex);
            }
        }
    }
}
core/WCSCore.cs
New file
@@ -0,0 +1,284 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HH.WCS.Mobox3.DSZSH.device;
using HH.WCS.Mobox3.DSZSH.models;
using HH.WCS.Mobox3.DSZSH.process;
using HH.WCS.Mobox3.DSZSH.util;
using HH.WCS.Mobox3.DSZSH.wms;
using Newtonsoft.Json;
using static HH.WCS.Mobox3.DSZSH.api.ApiModel;
namespace HH.WCS.Mobox3.DSZSH.core {
    public class WCSCore {
        public static ReturnResult OperateAgvTaskStatus(AgvTaskState model) {
            var result = new ReturnResult();
            try {
                switch (model.state) {
                    case 1023:
                        break;
                    case 1025:
                        break;
                    case 1012:
                        break;
                    case 1004:
                        break;
                    case 1103:
                        break;
                    default:
                        // AGV 执行任务的逻辑处理
                        if (!AgvTaskProcessOk(model)) {
                            // 执行不OK,说明没有找到任务
                            result.ResultCode = 1;
                            result.ResultMsg = $"根据Model.No未找到对应的任务,{model.task_no}";
                            LogHelper.Info(result.ResultMsg, "API");
                            return result;
                        }
                        break;
                }
                result.ResultCode = 0;
                result.ResultMsg = "success";
                LogHelper.Info(result.ResultMsg, "API");
                return result;
            }
            catch (Exception ex) {
                result.ResultCode = -1;
                result.ResultMsg = $"发生了异常:+{ex.Message}";
                LogHelper.Info(result.ResultMsg, "Error");
                return result;
            }
        }
        /// <summary>
        /// 执行 AGV 任务,查询不到任务返回 <see langword="false"/>
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        private static bool AgvTaskProcessOk(AgvTaskState model) {
            var TN_Task = WCSHelper.GetTask(model.task_no); // 根据当前model编号查询任务
            if (TN_Task == null) { return false; }
            if (model.state > 7) {
                //安全请求等
                TaskProcess.OperateReq(model.task_no, model.state, model.forklift_no, model.ext_data);
                return true;
            }
            // AGV 任务 134562(7) 状态处理
            switch (model.state) {
                case 1: // 执行
                    WCSHelper.Begin(TN_Task, model.forklift_no); // 已推送的任务的状态改成执行
                    break;
                case 3: // 开始取货
                    WCSHelper.UpdateStatus(TN_Task, "开始取货"); // 任务状态改成开始取货
                    break;
                case 4: // 取货完成
                    WCSHelper.UpdateStatus(TN_Task, "取货完成"); // 任务状态改成取货完成
                    TaskProcess.OperateStatus(TN_Task, 4); // 起点容器货位解绑,解锁起点
                    if (TN_Task.S_TYPE == TaskName.成品胶出库) {
                        var nextOutboundTask = Task.Run(() => {
                            UpdateOutboundTaskState(3);
                        });
                    }
                    break;
                case 5: // 开始卸货
                    WCSHelper.UpdateStatus(TN_Task, "开始卸货"); // 任务状态改成开始卸货
                    break;
                case 6: // 卸货完成
                    WCSHelper.UpdateStatus(TN_Task, "卸货完成"); // 任务状态改成卸货完成
                    TaskProcess.OperateStatus(TN_Task, 6); // 终点容器货位绑定,解锁终点
                    break;
                case 2: // 完成
                    WCSHelper.End(TN_Task); // 任务状态改成结束
                    if (TN_Task.S_TYPE == TaskName.抽检_出库) {
                        var checkCompleteTask = Task.Run(() => {
                            UpdateCheckTaskState(3);
                        });
                    }
                    break;
                case 7: // 异常
                    TaskProcess.OperateStatus(TN_Task, 7); // 异常处理
                    WCSHelper.Fail(TN_Task); // 任务状态改成错误
                    break;
            }
            // 将AGV执行状态,加入TN_Task_Action表中
            WCSHelper.AddActionRecord(model.task_no, model.state, model.forklift_no, model.ext_data);
            //调用第三方接口(如果有)TaskProcess.ReportStatus,添加任务动作关系表
            return true;
        }
        public static ReturnResult SafetyInteraction(SafetyInteractionInfo model) {
            var gzResult = new ReturnResult();
            var db = new SqlHelper<object>().GetInstance();
            ModbusHelper.Relink();
            try {
                var prodLineInfo = Settings.ProductionLines[0];
                var prodLineDevice = new ProductionLineDevice(prodLineInfo.PlcIp, prodLineInfo.PlcPort);
                if (!prodLineDevice.LoadDeviceStateOk()) {
                    LogHelper.Info("加载设备信息失败");
                }
                var tn_task = db.Queryable<TN_Task>().First(a => a.S_CODE == model.task_no);
                if (tn_task == null) {
                    LogHelper.Info($"任务号 '{model.task_no}' 不存在");
                }
                // 待修改:补充不同分支AGV判断
                if (prodLineDevice.SystemState == 1) {
                    if (prodLineDevice.FullOffline == 1 && tn_task.S_TYPE == TaskName.托盘_满托下线入库) {
                        prodLineDevice.AgvPicking = 1;
                    }
                    if (prodLineDevice.AllowAgvPlacePallet == 1 && tn_task.S_TYPE == TaskName.托盘_空托上线) {
                        prodLineDevice.AgvPlacingPallet = 1;
                    }
                }
                LogHelper.Info(JsonConvert.SerializeObject(prodLineDevice, Formatting.Indented));
                return gzResult;
            }
            catch (Exception ex) {
                LogHelper.Info($"发生了异常:{ex.Message}");
                return gzResult;
            }
        }
        public static void UpdateOutboundTaskState(int spotStateCode) {
            var db = new SqlHelper<object>().GetInstance();
            var detail = db.Queryable<TN_Outbound_Detail>()
                .First(d => d.N_B_STATE == 2);
            if (detail == null) {
                LogHelper.Info("出库--AGV取货--查询Detail:当前没有 执行中 的Detail表!");
                return;
            }
            using (var tran = db.Ado.UseTran()) {
                detail.N_B_STATE = spotStateCode;
                if (db.Updateable<TN_Outbound_Detail>(detail).UpdateColumns(it => it.N_B_STATE).ExecuteCommand() <= 0) {
                    tran.RollbackTran();
                    LogHelper.Info("出库--AGV取货:修改Detail状态 N_B_STATE 为 3任务执行完成 失败!");
                    return;
                }
                var finishedCount = db.Queryable<TN_Outbound_Detail>().Count(d => d.S_OO_NO == detail.S_OO_NO && d.N_B_STATE == 3);
                var allCount = db.Queryable<TN_Outbound_Detail>().Count(d => d.S_OO_NO == detail.S_OO_NO);
                LogHelper.Info($"出库--AGV取货--统计任务已完成:{finishedCount} / {allCount}");
                if (finishedCount == allCount) { // 当前出库单下的所有明细单,任务都已经完成
                    if (db.Updateable<TN_Outbound_Order>().SetColumns(it => it.N_B_STATE == 3)
                        .Where(it => it.S_NO == detail.S_OO_NO)
                        .ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        LogHelper.Info("出库--AGV取货--所有任务完成时:修改Order状态 N_B_STATE 为 3任务执行完成 失败!");
                        return;
                    }
                }
                tran.CommitTran();
            }
        }
        public static void UpdateCheckTaskState(int spotStateCode) {
            var db = new SqlHelper<object>().GetInstance();
            var detail = db.Queryable<TN_Check_Detail>()
                .First(d => d.N_B_STATE == 2);
            if (detail == null) {
                LogHelper.Info("抽检--AGV任务完成--查询Detail:当前没有 执行中 的Detail表!");
                return;
            }
            using (var tran = db.Ado.UseTran()) {
                detail.N_B_STATE = spotStateCode;
                if (db.Updateable<TN_Check_Detail>(detail).UpdateColumns(it => it.N_B_STATE).ExecuteCommand() <= 0) {
                    tran.RollbackTran();
                    LogHelper.Info("抽检--AGV任务完成:修改Detail状态 N_B_STATE 为 3任务执行完成 失败!");
                    return;
                }
                var finishedCount = db.Queryable<TN_Check_Detail>().Count(d => d.S_NO == detail.S_NO && d.N_B_STATE == 3);
                var allCount = db.Queryable<TN_Check_Detail>().Count(d => d.S_NO == detail.S_NO);
                LogHelper.Info($"抽检--AGV任务完成:统计任务已完成:{finishedCount} / {allCount}");
                if (finishedCount == allCount) { // 当前order下的detail,任务都已经完成
                    if (db.Updateable<TN_Check_Order>().SetColumns(it => it.N_B_STATE == 3)
                        .Where(it => it.S_NO == detail.S_NO)
                        .ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        LogHelper.Info("抽检--AGV任务完成--所有任务完成时:修改Order状态 N_B_STATE 为 3任务执行完成 失败!");
                        return;
                    }
                }
                tran.CommitTran();
            }
        }
        public static void UpdateShiftTaskState(int spotStateCode) {
            var db = new SqlHelper<object>().GetInstance();
            var detail = db.Queryable<TN_Shift_Detail>()
                .First(d => d.N_B_STATE == 2);
            if (detail == null) {
                LogHelper.Info("移库--AGV任务完成--查询Detail:当前没有 执行中 的Detail表!");
                return;
            }
            detail.N_B_STATE = spotStateCode;
            var finishedCount = db.Queryable<TN_Shift_Detail>().Count(d => d.S_NO == detail.S_NO && d.N_B_STATE == 3);
            var allCount = db.Queryable<TN_Shift_Detail>().Count(d => d.S_NO == detail.S_NO);
            LogHelper.Info($"移库--AGV任务完成:统计任务已完成:{finishedCount} / {allCount}");
            using (var tran = db.Ado.UseTran()) {
                if (db.Updateable<TN_Shift_Detail>(detail).UpdateColumns(it => it.N_B_STATE).ExecuteCommand() <= 0) {
                    tran.RollbackTran();
                    LogHelper.Info("移库--AGV任务完成:修改Detail状态 N_B_STATE 为 3任务执行完成 失败!");
                    return;
                }
                if (finishedCount == allCount) { // 当前order下的detail,任务都已经完成
                    if (db.Updateable<TN_Shift_Order>().SetColumns(it => it.N_B_STATE == 3)
                        .Where(it => it.S_NO == detail.S_NO)
                        .ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        LogHelper.Info("移库--AGV任务完成--所有任务完成时:修改Order状态 N_B_STATE 为 3任务执行完成 失败!");
                        return;
                    }
                }
                tran.CommitTran();
            }
        }
    }
}
core/WMSCore.cs
File was renamed from ServiceCore/DebugCore.cs
@@ -4,8 +4,8 @@
using System.Text;
using System.Threading.Tasks;
namespace HH.WCS.Mobox3.DSZSH.ServiceCore {
    public class DebugCore {
namespace HH.WCS.Mobox3.DSZSH.core {
    public class WMSCore {
    }
}
device/ModbusHelper.cs
File was renamed from Devices/ModbusHelper.cs
@@ -5,7 +5,7 @@
using EasyModbus;
namespace HH.WCS.Mobox3.DSZSH.Helpers {
namespace HH.WCS.Mobox3.DSZSH.device {
    /// <summary>
    /// modbus tcp 用第三方的包
    /// </summary>
device/OpcUaHelper.cs
device/PlcHelper.cs
File was renamed from Devices/PlcHelper.cs
@@ -1,9 +1,10 @@
using HH.WCS.Mobox3.DSZSH.Helpers;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HH.WCS.Mobox3.DSZSH.process;
namespace HH.WCS.Mobox3.DSZSH.device
{
@@ -15,7 +16,6 @@
        }
        internal static bool SendHex(string ip, string msg) {
            return TcpServer.TcpServerSend(ip, Hex2Bytes(msg));
        }
        internal static void SendAscii(string ip, string msg) {
            TcpServer.TcpServerSend(ip, Encoding.ASCII.GetBytes(msg));
device/ProductionLineDevice.cs
File was renamed from Devices/ProductionLineDevice.cs
@@ -1,10 +1,8 @@
using System;
using HH.WCS.Mobox3.DSZSH.Helpers;
using Newtonsoft.Json;
namespace HH.WCS.Mobox3.DSZSH.Devices {
namespace HH.WCS.Mobox3.DSZSH.device {
    /// <summary>
    /// 输送线PLC设备
    /// </summary>
device/S7Helper.cs
File was renamed from Devices/S7Helper.cs
@@ -1,4 +1,4 @@
using HH.WCS.Mobox3.DSZSH.Controllers;
using HH.WCS.Mobox3.DSZSH.api;
using Newtonsoft.Json.Linq;
using S7.Net;
using S7.Net.Types;
device/TcpClient.cs
device/TcpServer.cs
File was renamed from Devices/TcpServer.cs
@@ -1,5 +1,4 @@
using HH.WCS.Mobox3.DSZSH.Dispatch;
using HH.WCS.Mobox3.DSZSH.AppStart;
using HH.WCS.Mobox3.DSZSH.dispatch;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
process/DeviceProcess.cs
File was renamed from Helpers/DeviceProcess.cs
@@ -1,14 +1,12 @@
using HH.WCS.Mobox3.DSZSH.device;
using HH.WCS.Mobox3.DSZSH.Dispatch;
using HH.WCS.Mobox3.DSZSH.AppStart;
using HH.WCS.Mobox3.DSZSH.Helper;
using HH.WCS.Mobox3.DSZSH.dispatch;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace HH.WCS.Mobox3.DSZSH.Helpers
namespace HH.WCS.Mobox3.DSZSH.process
{
    /// <summary>
    /// 设备信号处理,主要是tcp信号,我们做server被动接收信号来处理,根据项目定制的
process/TaskProcess.cs
New file
@@ -0,0 +1,196 @@
using Newtonsoft.Json;
using SqlSugar;
using System.Collections.Generic;
using System.Linq;
using System;
using HH.WCS.Mobox3.DSZSH.dispatch;
using HH.WCS.Mobox3.DSZSH.models;
using HH.WCS.Mobox3.DSZSH.wms;
using HH.WCS.Mobox3.DSZSH;
namespace HH.WCS.Mobox3.DSZSH.process {
    internal class TaskProcess {
        #region 任务相关
        //--------------------------------------------------任务相关--------------------------------------------------
        /// <summary>
        /// 取货卸货完成,缓存位状态更新
        /// </summary>
        /// <param name="mst"></param>
        /// <param name="load"></param>
        internal static void CacheBitUpdate(TN_Task mst, bool load) {
            //var trayCarryCount = mst.N_CNTR_COUNT > 0 ? mst.N_CNTR_COUNT : 1;
            if (load) {
                Console.WriteLine($"任务{mst.S_CODE} 货位{mst.S_START_LOC}取货完成,起点解绑容器{mst.S_CNTR_CODE}");
                LogHelper.Info($"任务{mst.S_CODE} 货位{mst.S_START_LOC}取货完成,起点解绑容器{mst.S_CNTR_CODE}");
                LocationHelper.UnBindingLoc(mst.S_START_LOC, mst.S_CNTR_CODE.Split(',').ToList());
            }
            else {
                Console.WriteLine($"任务{mst.S_CODE} 货位{mst.S_END_LOC}卸货完成,终点绑定容器{mst.S_CNTR_CODE}");
                LogHelper.Info($"任务{mst.S_CODE} 货位{mst.S_END_LOC}卸货完成,终点绑定容器{mst.S_CNTR_CODE}");
                LocationHelper.BindingLoc(mst.S_END_LOC, mst.S_CNTR_CODE.Split(',').ToList());
            }
        }
        /// <summary>
        /// 任务取消,缓存位状态更新
        /// </summary>
        /// <param name="mst"></param>
        internal static void CacheBitCancelUpdate(TN_Task mst) {
            //任务取消,取货完成前的,起点的loadingCount和终点unLoadingCount都清除,取货完成的只处理终点
            if (WCSHelper.CheckActionRecordExist(mst.S_CODE, 4)) {
                //根据客户现场要求,如果取货完成任务失败人工拉到终点,我们就当卸货完成处理;如果是人工拉走到其它区域,我们就解锁终点,删除托盘。
                //终点绑定
                CacheBitUpdate(mst, false);
                LocationHelper.UnLockLoc(mst.S_END_LOC);
            }
            else {
                //起点终点解锁
                LocationHelper.UnLockLoc(mst.S_START_LOC);
                LocationHelper.UnLockLoc(mst.S_END_LOC);
            }
        }
        /// <summary>
        /// 任务状态更新处理
        /// </summary>
        /// <param name="mst"></param>
        /// <param name="state"></param>
        internal static void OperateStatus(TN_Task mst, int state) {
            if (state == 4) {
                CacheBitUpdate(mst, true);
            }
            if (state == 6)//卸货完成
            {
                CacheBitUpdate(mst, false);
            }
            if (state == 7) {
                CacheBitCancelUpdate(mst);
            }
        }
        /// <summary>
        /// 安全请求
        /// </summary>
        /// <param name="no"></param>
        /// <param name="state"></param>
        /// <param name="forkliftNo"></param>
        /// <param name="extData"></param>
        internal static void OperateReq(string no, int state, string forkliftNo, string extData = "") {
            if (state == 1101) {
                //请求取货,
            }
            if (state == 1102) {
                //请求卸货,
                //根据终点判断,是cb02的入口,判断内存中状态(要状态时间),允许卸货,通知agv改参数
                var dic = new Dictionary<string, string>();
                //< Req >< Order No = 'TN2302020002' ParamNo = '18' Param1 = '12' /></ Req >
                dic.Add("No", no);
                dic.Add("ParamNo", "8");
                dic.Add("Param1", "1");
                NDC.ChangeOrder(dic);
                //改完参数车子就会自己卸货
            }
            if (state == 1103) {
                //大铁框叉走以后通知,我们要通知输送线
            }
        }
        private static object locLocker = new object();
        /// <summary>
        /// 推送任务
        /// </summary>
        /// <param name="mst"></param>
        internal static bool SendTask(TN_Task mst) {
            var result = false;
            var start = "0"; var end = "0";
            var taskType = mst.S_TYPE.Trim();
            if (mst.N_B_STATE == 0) {
                if (mst.N_SCHEDULE_TYPE == 1)//通过NDC,hosttoagv调度设备
                {
                    start = LocationHelper.GetAgvSite(mst.S_START_LOC);
                    end = LocationHelper.GetAgvSite(mst.S_END_LOC);
                    //if (mst.S_TYPE == "空托下线堆叠") {
                    //    end = LocationHelper.GetAgvSite(mst.S_END_LOC, true);
                    //}
                    LogHelper.Info($"NDC推送任务 {mst.S_CODE};" + "start=" + start + "end= " + end);
                    var startLoc = LocationHelper.GetLoc(mst.S_START_LOC);
                    var endLoc = LocationHelper.GetLoc(mst.S_END_LOC);
                    var dic = new List<param>();
                    dic.Add(new param() { name = "IKey", value = "IKey" });
                    dic.Add(new param() { name = "From", value = start.ToString() });
                    dic.Add(new param() { name = "To", value = end.ToString() });
                    dic.Add(new param() { name = "FUNC", value = startLoc.N_LAYER.ToString() });
                    dic.Add(new param() { name = "Ctype", value = "0" });
                    //if (mst.S_TYPE == "余料下线入库" || mst.S_TYPE == "人工拆盘入库") {
                    //    dic.Add(new param() { name = "DATA", value = "1024" });
                    //}
                    //else {
                    //    dic.Add(new param() { name = "DATA", value = "0" });
                    //}
                    var res = NDCApi.AddOrderNew(1, 1, mst.S_CODE, dic);//添加新命令
                    if (res != null && (res.err_code == 0 || res.err_code == 50009)) {
                        //推送成功,修改任务优先级
                        mst.N_B_STATE = 1;
                        mst.S_B_STATE = TN_Task.GetStateStr(1);
                        WCSHelper.UpdateStatus(mst);//更新任务状态
                        result = true;
                        LogHelper.Info($"NDC推送任务成功 {mst.S_CODE}start= {mst.S_START_LOC} + end = {mst.S_END_LOC}");
                    }
                    else {
                        LogHelper.Info($"NDC推送任务失败 {mst.S_CODE};Res:" + JsonConvert.SerializeObject(res));
                    }
                }
                else if (mst.N_SCHEDULE_TYPE == 5)//通过杭奥调度设备
                {
                    //调第三方接口
                    var model = new HanAo.TaskInfoModel {
                        requestPk = mst.S_CODE,
                        frmPos = mst.S_START_LOC,
                        toPos = mst.S_END_LOC,
                        trkType = mst.S_OP_NAME == "入库" ? "1" : "2",
                        contNo = mst.S_CNTR_CODE
                    };
                    if (HanAo.CreateOrder(model)) {
                        mst.N_B_STATE = 1;
                        WCSHelper.UpdateStatus(mst);
                        LogHelper.Info($"杭奥推送任务成功 {mst.S_CODE};" + "start=" + model.frmPos + "end= " + model.toPos);
                    }
                    else {
                        LogHelper.Info($"杭奥推送任务失败 {mst.S_CODE};" + JsonConvert.SerializeObject(model));
                    }
                }
                else if (mst.N_SCHEDULE_TYPE == 3) //通过国自调度设备
                {
                    var code = GZRobot.CreateOrder(mst.S_CODE, mst.N_PRIORITY, JsonConvert.SerializeObject(new { src = mst.S_START_LOC, dst = mst.S_END_LOC }), "p2p");
                    if (code > 0) {
                        //更新任务状态
                        mst.N_B_STATE = 1;
                        mst.S_EQ_TASK_CODE = code.ToString();
                        WCSHelper.UpdateStatus(mst);
                        WCSHelper.UpdateEQNo(mst);
                        LogHelper.Info($"国自推送任务成功 {mst.S_CODE};" + "start=" + mst.S_START_LOC + "end= " + mst.S_END_LOC);
                    }
                    else {
                        LogHelper.Info($"国自推送任务失败 {mst.S_CODE};" + JsonConvert.SerializeObject(mst));
                    }
                }
            }
            return result;
        }
        #endregion
    }
}
util/HttpHelper.cs
File was renamed from Helpers/HttpHelper.cs
@@ -5,7 +5,7 @@
using System.Net;
using System.Text;
namespace HH.WCS.Mobox3.DSZSH.AppStart {
namespace HH.WCS.Mobox3.DSZSH.util {
    public class HttpHelper {
        public string WebPost(string url, string postData, string cotentType = "application/json")
        {
util/LogHelper.cs
File was renamed from Helpers/LogHelper.cs
@@ -13,7 +13,6 @@
    {
        public static Dictionary<string, ILogger> loggers = new Dictionary<string, ILogger>();
        #region 标准方法(Debug/Info/Error)
        public static void Debug(string message, string name = "") {
            ILogger logger = null;
            if (loggers.Keys.Contains(name)) {
@@ -70,7 +69,6 @@
                logger.Error($"{message}{ex.StackTrace}");
            }
        } 
        #endregion
        #region 自定义方法
        public static void InfoEx(Exception ex) {
util/Settings.cs
New file
@@ -0,0 +1,158 @@
using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace HH.WCS.Mobox3.DSZSH {
    public class Settings
    {
        public static string WebApiUrl { get; set; }
        public static string NdcApiUrl { get; set; }
        public static string SqlServer { get; set; }
        public static string TcpServerIp { get; set; }
        public static int TcpServerPort { get; set; }
        public static List<Config.Area> Areas { get; set; }
        public static List<Config.Task> Tasks { get; set; }
        public static List<Config.ProductionLine> ProductionLines { get; set; }
        /// <summary>
        /// 库区字典(加载后就不变)
        /// </summary>
        public static Dictionary<string, List<string>> AreaMap { get; set; } = new Dictionary<string, List<string>>();
        /// <summary>
        /// 任务字典(加载后就不变)
        /// </summary>
        public static Dictionary<string, Config.Task> TaskMap { get; set; } = new Dictionary<string, Config.Task>();
        public static void Init() {
            // 加载配置文件
            LoadJson();
            // 针对 Areas 进行转换:将 Config 的 List 加载到 Dict 中
            LoadAreas();
            // 针对 Tasks 进行转换
            LoadTasks();
        }
        private static void LoadJson() {
            LogHelper.Info("加载配置文件信息 开始");
            // JSON 文件路径
            string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "./config/config.json");
            try {
                // 读取 JSON 文件内容
                string jsonContent = File.ReadAllText(filePath);
                // 反序列化为 Config 对象
                var root = JsonConvert.DeserializeObject<Config.Root>(jsonContent);
                WebApiUrl = root.WebApiUrl;
                NdcApiUrl = root.NdcApiUrl;
                SqlServer = root.SqlServer;
                TcpServerIp = root.TcpServerIp;
                TcpServerPort = root.TcpServerPort;
                Areas = root.Areas;
                Tasks = root.Tasks;
                ProductionLines = root.ProductionLines;
            }
            catch (FileNotFoundException) {
                LogHelper.Info("JSON 文件未找到");
            }
            catch (JsonException ex) {
                LogHelper.Info($"JSON 解析错误: {ex.Message}");
            }
            catch (Exception ex) {
                LogHelper.Info($"发生错误: {ex.Message}");
            }
            LogHelper.Info("加载配置文件信息 完成");
        }
        private static void LoadAreas() {
            foreach (var area in Areas) {
                AreaMap.Add(area.Name, area.Codes);
            }
        }
        private static void LoadTasks() {
            foreach (var task in Tasks) {
                TaskMap.Add(task.Name, task);
            }
        }
    }
    public class Config {
        // Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse);
        public class Area {
            public string Name { get; set; }
            public List<string> Codes { get; set; }
        }
        public class ProductionLine {
            public string Id { get; set; }
            public string Name { get; set; }
            public string PlcIp { get; set; }
            public int PlcPort { get; set; }
            public int SlaveId { get; set; }
            public List<string> OnLoc { get; set; }
            public List<string> OffLoc { get; set; }
        }
        public class Root {
            public string WebApiUrl { get; set; }
            public string NdcApiUrl { get; set; }
            public string SqlServer { get; set; }
            public string TcpServerIp { get; set; }
            public int TcpServerPort { get; set; }
            public List<Area> Areas { get; set; }
            public List<Task> Tasks { get; set; }
            public List<ProductionLine> ProductionLines { get; set; }
        }
        public class Task {
            public string Name { get; set; }
            public List<string> StartAreas { get; set; }
            public List<string> EndAreas { get; set; }
        }
    }
    public class AreaName {
        public const string 包装区 = "包装区";
        public const string 操作区 = "操作区";
        public const string 空托存放区 = "空托存放区";
        public const string 货架区 = "货架区";
        public const string 空箱存放区 = "空箱存放区";
        public const string 满托存放区 = "满托存放区";
        public const string 满箱存放区 = "满箱存放区";
        public const string 人工_AGV接驳区 = "人工-AGV接驳区";
        public const string 空托盘接驳区 = "空托盘接驳区";
        public const string 空箱接驳区 = "空箱接驳区";
    }
    public class TaskName {
        public const string 好运箱_满箱下线入库 = "好运箱-满箱下线入库";
        public const string 好运箱_空箱上线 = "好运箱-空箱上线";
        public const string 好运箱_空箱入库 = "好运箱-空箱入库";
        public const string 好运箱_空箱绑定 = "好运箱-空箱绑定";
        public const string 成品胶出库 = "成品胶出库";
        public const string 托盘_满托下线入库 = "托盘-满托下线入库";
        public const string 托盘_空托上线 = "托盘-空托上线";
        public const string 托盘_空托入库 = "托盘-空托入库";
        public const string 托盘_空托绑定 = "托盘-空托绑定";
        public const string 抽检_不合格移库 = "抽检-不合格移库";
        public const string 抽检_出库 = "抽检-出库";
        public const string 抽检_合格回库 = "抽检-合格回库";
        public const string 移库 = "移库";
        public const string 尾箱回库 = "尾箱回库";
    }
}
util/SqlHelper.cs
New file
@@ -0,0 +1,63 @@
using System;
using System.Linq;
using SqlSugar;
namespace HH.WCS.Mobox3.DSZSH.util {
    //https://www.donet5.com/Home/Doc
    public class SqlHelper<T> where T : class, new() {
        /// <summary>
        /// 如果用Oracle数据需要包Oracle.ManagedDataAccess/21.15.0,环境netframework 4.62,太新了4.8有的服务器安装不上去
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public SqlSugarClient GetInstance(string url = "") {
            //创建数据库对象
            SqlSugarClient db = new SqlSugarClient(new ConnectionConfig() {
                ConnectionString = string.IsNullOrEmpty(url) ? Settings.SqlServer : url,
                //ConnectionString = @"Data Source=192.168.1.198\sql2008;Initial Catalog=OIMobox;User ID=sa;Password=sa@2015",
                DbType = DbType.SqlServer,
                //ConnectionString = @"Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=OIMobox)));User Id=system;Password=Am123123;",
                //DbType = DbType.Oracle,
                IsAutoCloseConnection = true,
                InitKeyType = InitKeyType.Attribute//从特性读取主键自增信息
            });
            //监控所有超过1秒的Sql
            db.Aop.OnLogExecuted = (sql, p) => {
                //执行时间超过1秒
                if (db.Ado.SqlExecutionTime.TotalSeconds > 1) {
                    Console.WriteLine(sql + "\r\n" + db.Utilities.SerializeObject(p.ToDictionary(it => it.ParameterName, it => it.Value)));
                    //代码CS文件名
                    var fileName = db.Ado.SqlStackTrace.FirstFileName;
                    //代码行数
                    var fileLine = db.Ado.SqlStackTrace.FirstLine;
                    //方法名
                    var FirstMethodName = db.Ado.SqlStackTrace.FirstMethodName;
                }
                //相当于EF的 PrintToMiniProfiler
            };
            //每次设置数值时都去除前导后导空格
            db.Aop.DataExecuted = (value, entity) => {
                entity.EntityColumnInfos.ToList().ForEach(a => {
                    var pvalue = entity.GetValue(a.PropertyName);
                    if (pvalue != null && pvalue.GetType() == typeof(String)) {
                        entity.SetValue(a.PropertyName, pvalue.ToString().Trim());
                    }
                });
            };
            //据转换 (ExecuteCommand才会拦截,查询不行)
            //db.Aop.DataExecuting = (value, entity) => {
            //    //var val=entity.EntityColumnInfo
            //    Console.WriteLine(entity.EntityName);
            //};
            return db;
        }
    }
}
wms/ContainerHelper.cs
File was renamed from Helpers/ContainerHelper.cs
@@ -2,12 +2,12 @@
using System.Collections.Generic;
using System.Linq;
using HH.WCS.Mobox3.DSZSH.Helpers;
using HH.WCS.Mobox3.DSZSH.Models;
using HH.WCS.Mobox3.DSZSH.models;
using HH.WCS.Mobox3.DSZSH.util;
using Newtonsoft.Json;
namespace HH.WCS.Mobox3.DSZSH.Helpers {
namespace HH.WCS.Mobox3.DSZSH.wms {
    /// <summary>
    /// 容器帮助类(包含容器-货品关系的处理)
    /// </summary>
@@ -21,7 +21,7 @@
        /// <returns></returns>
        public static string BindingCG(string cnt, List<string> cGs)
        {
            var db = DbHelper.GetDbClient();
            var db = new SqlHelper<object>().GetInstance();
            var logs = $"容器:{cnt},货品:{JsonConvert.SerializeObject(cGs)}";
            try
            {
@@ -86,7 +86,7 @@
        /// <returns></returns>
        public static string UnBindingCG(string cnt, List<string> cGs)
        {
            var db = DbHelper.GetDbClient();
            var db = new SqlHelper<object>().GetInstance();
            var logs = $"容器:{cnt},货品:{JsonConvert.SerializeObject(cGs)}";
            try
            {
wms/LocationHelper.cs
File was renamed from Helpers/LocationHelper.cs
@@ -2,15 +2,14 @@
using System.Collections.Generic;
using System.Linq;
using HH.WCS.Mobox3.DSZSH.Models;
using HH.WCS.Mobox3.DSZSH.models;
using HH.WCS.Mobox3.DSZSH.util;
using Newtonsoft.Json;
using SqlSugar;
using static HH.WCS.Mobox3.DSZSH.Dtos.Response.MoboxResponse;
namespace HH.WCS.Mobox3.DSZSH.Helpers {
namespace HH.WCS.Mobox3.DSZSH.wms {
    /// <summary>
    /// 货位帮助类(包含货位-容器关系的处理)
    /// </summary>
@@ -56,7 +55,7 @@
        /// <returns></returns>
        internal static List<TN_Location> GetAllLocList()
        {
            var db = DbHelper.GetDbClient();
            var db = new SqlHelper<object>().GetInstance();
            return db.Queryable<TN_Location>().ToList();
        }
@@ -123,8 +122,31 @@
        internal static TN_Location GetLoc(string code)
        {
            var db = DbHelper.GetDbClient();
            var db = new SqlHelper<object>().GetInstance();
            return db.Queryable<TN_Location>().Where(a => a.S_CODE.Trim() == code).First();
        }
        /// <summary>
        /// 入库锁定终点,出库锁定起点
        /// 你创建任务锁定货位的时候,把锁的来源就是任务号也写上去(加锁的方法加个参数,可空的参数),解锁的时候把来源置空。
        /// </summary>
        /// <param name="loc"></param>
        /// <param name="lockState">1:入库锁、2:出库锁、2:其它锁</param>
        /// <param name="lockSource">锁的来源=任务号</param>
        /// <returns></returns>
        public static bool LockLoc(ref TN_Location loc, int lockState, string lockSource = "") {
            if (loc == null || loc.N_LOCK_STATE != 0) {
                return false;
            }
            if (loc != null && loc.N_LOCK_STATE == 0) {
                loc.N_LOCK_STATE = lockState;
                loc.S_LOCK_STATE = TN_Location.GetLockStateStr(lockState);
                loc.S_LOCK_OP = lockSource;
                loc.T_MODIFY = System.DateTime.Now;
            }
            return true;
        }
        /// <summary>
@@ -140,7 +162,7 @@
        public static bool LockLoc(string loc, int lockState, string lockSource = "")
        {
            var res = false;
            var db = DbHelper.GetDbClient();
            var db = new SqlHelper<object>().GetInstance();
            var model = db.Queryable<TN_Location>().Where(a => a.S_CODE == loc).First();
            LogHelper.Info($"锁货位{loc},状态{lockState},信息" + JsonConvert.SerializeObject(model));
            if (model != null && model.N_LOCK_STATE == 0)
@@ -165,7 +187,7 @@
        {
            LogHelper.Info("UnLockLoc:" + loc);
            var res = false;
            var db = DbHelper.GetDbClient();
            var db = new SqlHelper<object>().GetInstance();
            var model = db.Queryable<TN_Location>().Where(a => a.S_CODE == loc).First();
            if (model != null)
            {
@@ -191,7 +213,7 @@
        /// <returns></returns>
        public static string UnBindingLoc(string loc, List<string> cntrs)
        {
            var db = DbHelper.GetDbClient();
            var db = new SqlHelper<object>().GetInstance();
            var logs = $"货位:{loc},容器:{JsonConvert.SerializeObject(cntrs)}";
            try
            {
@@ -264,7 +286,7 @@
        /// <returns></returns>
        public static string BindingLoc(string loc, List<string> cntrs)
        {
            var db = DbHelper.GetDbClient();
            var db = new SqlHelper<object>().GetInstance();
            var logs = $"货位:{loc},容器:{JsonConvert.SerializeObject(cntrs)}";
            try
            {
wms/SYSHelper.cs
File was renamed from Helpers/SysHelper.cs
@@ -1,19 +1,19 @@
using HH.WCS.Mobox3.DSZSH.Helpers;
using HH.WCS.Mobox3.DSZSH.util;
using SqlSugar;
namespace HH.WCS.Mobox3.DSZSH.Helper {
namespace HH.WCS.Mobox3.DSZSH.wms {
    /// <summary>
    /// 用于生成序列号,并管理序列号前缀的工具类
    /// </summary>
    internal class SysHelper
    internal class SYSHelper
    {
        //使用静态 locker 对象实现线程同步
        private static object locker = new object();
        internal static int GetSerialNumber(string snType, string prefix) {
            int result = 0;
            lock (locker) {
                var db = DbHelper.GetDbClient();
                var db = new SqlHelper<object>().GetInstance();
                var sId = db.Queryable<OI_SYS_MAXID>().Where(a => a.CN_S_TYPE.Trim() == snType && a.CN_S_PRE.Trim() == prefix).First();
                if (sId != null) {
                    sId.CN_N_MAX++;
wms/WCSHelper.cs
New file
@@ -0,0 +1,224 @@
using HH.WCS.Mobox3.DSZSH.models;
using HH.WCS.Mobox3.DSZSH.util;
using HH.WCS.Mobox3.DSZSH;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HH.WCS.Mobox3.DSZSH.wms {
    internal class WCSHelper {
        internal static string GenerateTaskNo() {
            var id = SYSHelper.GetSerialNumber("任务号", "TN");
            var date = DateTime.Now.ToString("yyMMdd");
            return $"TN{date}{id.ToString().PadLeft(4, '0')}";
        }
        internal static bool UpdateStatus(TN_Task task, string status) {
            var res = false;
            var db = new SqlHelper<TN_Task>().GetInstance();
            task.S_B_STATE = status;
            res = db.Updateable(task).UpdateColumns(it => new { it.S_B_STATE }).ExecuteCommand() > 0;
            return res;
        }
        internal static bool UpdateStatus(TN_Task task) {
            var res = false;
            var db = new SqlHelper<TN_Task>().GetInstance();
            task.S_B_STATE = TN_Task.GetStateStr(task.N_B_STATE);
            task.T_MODIFY = DateTime.Now;
            db.Updateable(task).UpdateColumns(it => new { it.N_B_STATE, it.S_B_STATE, it.T_MODIFY }).ExecuteCommand();
            return res;
        }
        internal static bool UpdateEQNo(TN_Task task) {
            var res = false;
            var db = new SqlHelper<TN_Task>().GetInstance();
            task.T_MODIFY = DateTime.Now;
            db.Updateable(task).UpdateColumns(it => new { it.S_EQ_TASK_CODE, it.T_MODIFY }).ExecuteCommand();
            return res;
        }
        internal static TN_Task GetTask(string no) {
            var db = new SqlHelper<TN_Task>().GetInstance();
            var task = db.Queryable<TN_Task>().Where(a => a.S_CODE == no).First();
            return task;
        }
        public static TN_Task BuildTask(TN_Location startLoc, TN_Location endLoc, string cntId, string type) {
            TN_Task TN_Task = new TN_Task() {
                S_CODE = GenerateTaskNo(),
                S_START_AREA = startLoc.S_AREA_CODE,
                S_END_AREA = endLoc.S_AREA_CODE,
                S_START_LOC = startLoc.S_CODE,
                S_END_LOC = endLoc.S_CODE,
                S_TYPE = type,
                N_PRIORITY = 3, // 初始优先级默认为:3
                N_SCHEDULE_TYPE = 3, // 国自
                N_B_STATE = 0,
                S_CNTR_CODE = cntId,
            };
            return TN_Task;
        }
        public static TN_Task BuildTask(TN_Loc_Container locCntrRel, TN_Location endLoc, string cntId, string type) {
            var fromLoc = LocationHelper.GetLocation(locCntrRel.S_LOC_CODE);
            TN_Task TN_Task = new TN_Task() {
                S_CODE = GenerateTaskNo(),
                S_START_AREA = fromLoc.S_AREA_CODE,
                S_END_AREA = endLoc.S_AREA_CODE,
                S_START_LOC = fromLoc.S_CODE,
                S_END_LOC = endLoc.S_CODE,
                S_TYPE = type,
                N_PRIORITY = 3, // 初始优先级默认为:3
                N_SCHEDULE_TYPE = 3, // 国自
                N_B_STATE = 0,
                S_CNTR_CODE = cntId,
            };
            return TN_Task;
        }
        /// <summary>
        /// 创建搬送任务
        /// </summary>
        /// <param name="no">编号</param>
        /// <param name="from">起点</param>
        /// <param name="to">终点</param>
        /// <param name="taskType">任务类型</param>
        /// <param name="pri">优先级</param>
        /// <param name="cntrInfo">容器编码</param>
        /// <returns></returns>
        internal static bool CreateTask(string from, string to, string taskType, int pri, string cntrInfo) {
            var fromLoc = LocationHelper.GetLocation(from);
            var endLoc = LocationHelper.GetLocation(to);
            TN_Task TN_Task = new TN_Task() {
                S_CODE = GenerateTaskNo(),
                S_START_AREA = fromLoc.S_AREA_CODE,
                S_END_AREA = endLoc.S_AREA_CODE,
                S_START_LOC = from,
                S_END_LOC = to,
                S_TYPE = taskType,
                N_PRIORITY = pri,
                N_SCHEDULE_TYPE = 1,
                N_B_STATE = 0,
                S_CNTR_CODE = cntrInfo,
            };
            var log = JsonConvert.SerializeObject(TN_Task);
            var db = new SqlHelper<TN_Task>().GetInstance();
            var res = db.Insertable(TN_Task).ExecuteCommand() > 0;
            if (res) {
                LogHelper.Info($"插入任务成功,{log}");
            }
            else {
                LogHelper.Info($"插入任务失败,{log}");
            }
            return res;
        }
        public static bool CreateTask(List<CreateTasks> modes) {
            if (modes != null && modes.Count > 0) {
                List<TN_Task> tN_Tasks = new List<TN_Task>();
                foreach (var item in modes) {
                    var fromLoc = LocationHelper.GetLocation(item.from);
                    var endLoc = LocationHelper.GetLocation(item.to);
                    tN_Tasks.Add(new TN_Task() {
                        S_CODE = GenerateTaskNo(),
                        S_START_AREA = fromLoc.S_AREA_CODE,
                        S_END_AREA = endLoc.S_AREA_CODE,
                        S_START_LOC = item.from,
                        S_END_LOC = item.to,
                        S_TYPE = item.taskType,
                        N_PRIORITY = item.pri,
                        N_SCHEDULE_TYPE = 1,
                        N_B_STATE = 0,
                        S_CNTR_CODE = item.cntrInfo,
                    });
                }
                var log = JsonConvert.SerializeObject(tN_Tasks);
                var db = new SqlHelper<object>().GetInstance();
                var res = db.Insertable<TN_Task>(tN_Tasks).ExecuteCommand() > 0;
                if (res) {
                    LogHelper.Info($"插入任务成功,{log}");
                }
                else {
                    LogHelper.Info($"插入任务失败,{log}");
                }
                return res;
            }
            return false;
        }
        internal static bool CheckActionRecordExist(string no, int code) {
            var db = new SqlHelper<TN_Task_Action>().GetInstance();
            return db.Queryable<TN_Task_Action>().Count(a => a.S_TASK_CODE == no && a.N_ACTION_CODE == code) > 0;
        }
        internal static void Begin(TN_Task task, string forklift_no) {
            var db = new SqlHelper<TN_Task>().GetInstance();
            if (task != null) {
                if (task.N_B_STATE == 1) {
                    task.N_B_STATE = 2;
                    task.S_B_STATE = TN_Task.GetStateStr(task.N_B_STATE);
                    task.T_START_TIME = System.DateTime.Now;
                    task.S_EQ_NO = forklift_no;
                    db.Updateable(task).UpdateColumns(it => new { it.N_B_STATE, it.S_B_STATE, it.T_START_TIME, it.S_EQ_NO }).ExecuteCommand();
                }
            }
        }
        internal static void End(TN_Task task) {
            var db = new SqlHelper<TN_Task>().GetInstance();
            if (task != null) {
                task.N_B_STATE = 3;
                task.S_B_STATE = TN_Task.GetStateStr(task.N_B_STATE);
                task.T_END_TIME = DateTime.Now;
                db.Updateable(task).UpdateColumns(it => new { it.N_B_STATE, it.S_B_STATE, it.T_END_TIME }).ExecuteCommand();
            }
        }
        internal static void Fail(TN_Task task) {
            var db = new SqlHelper<TN_Task>().GetInstance();
            if (task != null) {
                //判断有没有取货完成,没有就变成失败。有取货完成默认完成了(跟据项目而定,有些项目人工拉走了也没有放到终点)。
                task.N_B_STATE = 4;
                task.S_B_STATE = TN_Task.GetStateStr(task.N_B_STATE);
                db.Updateable(task).UpdateColumns(it => new { it.N_B_STATE, it.S_B_STATE }).ExecuteCommand();
            }
        }
        internal static bool AddActionRecord(string no, int state, string forkliftNo, string extData) {
            var db = new SqlHelper<TN_Task_Action>().GetInstance();
            var action = new TN_Task_Action() {
                N_ACTION_CODE = state,
                S_TASK_CODE = no,
                S_EQ_CODE = forkliftNo,
                S_EQ_TYPE = "agv",
                S_DATA = extData
            };
            return db.Insertable(action).ExecuteCommand() > 0;
        }
        internal static List<TN_Task> GetWaitingTaskList() {
            var db = new SqlHelper<object>().GetInstance();
            return db.Queryable<TN_Task>().Where(a => a.N_B_STATE == 0 && (a.S_B_STATE == "等待" || a.S_B_STATE == "待推送")).ToList();
        }
    }
    public class CreateTasks {
        public string from { set; get; }
        public string to { set; get; }
        public string taskType { set; get; }
        public int pri { set; get; }
        public string cntrInfo { set; get; }
    }
}
wms/WMSHelper.cs
copy from ServiceCore/DebugCore.cs copy to wms/WMSHelper.cs
File was copied from ServiceCore/DebugCore.cs
@@ -4,8 +4,8 @@
using System.Text;
using System.Threading.Tasks;
namespace HH.WCS.Mobox3.DSZSH.ServiceCore {
    public class DebugCore {
namespace HH.WCS.Mobox3.DSZSH.wms {
    public class WMSHelper {
    }
}