wanshenmean
2 天以前 3406bbd34469982efeefe164c207dcb5c4a0dac2
fix: 修复多个功能模块中的问题和优化逻辑

修复WMS基础URL配置错误
修复SiemensDBDataType中DataType_ByteArray的类型转换问题
修复RedisConnectionManager中无可用终结点时的异常处理
修复RobotTaskService中GetRobotTaskTotalNum方法的WMS接口调用
优化ConveyorLineTargetAddressSelector中的目标地址选择逻辑
优化Quartz任务调度中IntervalSecond为0或负值的处理
优化RobotWorkflowOrchestrator中的换盘任务处理逻辑
优化TaskService_Inbound中的库存状态更新
优化ConveyorLineDispatchHandler中的任务下发逻辑
优化RobotSimpleCommandHandler中的批量拆盘和组盘确认逻辑
优化StackerCraneTaskSelector中的出库任务选择逻辑
优化CommonConveyorLineNewJob中的手动入库任务处理
已修改23个文件
308 ■■■■ 文件已修改
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Common/HttpEnum/BaseAPI.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Communicator/Siemens/SiemensDBDataType.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Communicator/Siemens/SiemensS7Communicator.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_ITaskInfoService/IRobotTaskService.cs 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/QuartzNet/SchedulerCenterServer.cs 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Connection/RedisConnectionManager.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Controllers/Task/RobotTaskController.cs 63 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/appsettings.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/RobotTaskService.cs 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/CommonConveyorLineNewJob.cs 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineTargetAddressSelector.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Messaging.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/CommonStackerCraneJob.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneTaskSelector.cs 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_Common/Constants/OutboundTimeConstants.cs 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_Common/Constants/TaskAddressConstants.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Inbound.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/appsettings.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Common/HttpEnum/BaseAPI.cs
@@ -11,7 +11,7 @@
        /// <summary>
        /// WMS接口基础URL
        /// </summary>
        public const string WMSBaseUrl = "http://localhost:9291/api/";
        public const string WMSBaseUrl = "http://192.168.60.30:9291/api/";
        /// <summary>
        /// WCS接口基础URL
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Communicator/Siemens/SiemensDBDataType.cs
@@ -112,6 +112,7 @@
                DataType_Char => TypeCode.Char,
                DataType_UInt => TypeCode.UInt16,
                DataType_UDInt => TypeCode.UInt32,
                DataType_ByteArray => TypeCode.SByte,
                _ => throw new CommunicationException($"数据类型错误:【{dataType}】", CommunicationErrorType.TypeError),
            };
        }
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Communicator/Siemens/SiemensS7Communicator.cs
@@ -358,6 +358,9 @@
                            return Encoding.Default.GetString((byte[])GetContent(plc.Read(address, length), address)).ToArray();
                        return (char)GetContent(plc.ReadByte(address), address);
                    case TypeCode.SByte:
                        return Encoding.Default.GetString((byte[])GetContent(plc.Read(address, 20), address)).ToArray();
                    default:
                        throw new CommunicationException(string.Format(CommunicationExceptionMessage.DataTypeErrorException, typeCode.ToString(), address), CommunicationErrorType.TypeError);
                }
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_ITaskInfoService/IRobotTaskService.cs
@@ -83,6 +83,14 @@
        WebResponseContent GetWMSRobotTask(Dt_Task task);
        /// <summary>
        ///
        /// </summary>
        /// <param name="taskType"></param>
        /// <param name="palletCode"></param>
        /// <returns></returns>
        int GetRobotTaskTotalNum(int taskType, string? palletCode);
        /// <summary>
        /// 在本地直接创建机械手任务,不调用WMS接口
        /// </summary>
        /// <param name="task">出库任务</param>
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/QuartzNet/SchedulerCenterServer.cs
@@ -459,11 +459,14 @@
        /// <returns></returns>
        private ITrigger CreateSimpleTrigger(DispatchInfoDTO sysSchedule)
        {
            // Quartz要求间隔至少1秒,防止数据库中IntervalSecond为0或负值导致ArgumentOutOfRangeException
            var intervalSeconds = sysSchedule.IntervalSecond <= 0 ? 1 : sysSchedule.IntervalSecond;
            ITrigger trigger = TriggerBuilder.Create()
            .WithIdentity(sysSchedule.Id.ToString(), sysSchedule.JobGroup)
            .StartAt(sysSchedule.BeginTime.GetValueOrDefault())
            .WithSimpleSchedule(x => x
                .WithIntervalInSeconds(sysSchedule.IntervalSecond)
                .WithIntervalInSeconds(intervalSeconds)
                .RepeatForever()
            )
            .EndAt(sysSchedule.EndTime.GetValueOrDefault())
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Connection/RedisConnectionManager.cs
@@ -82,6 +82,8 @@
        public IServer GetServer()
        {
            var endpoints = _connection.Value.GetEndPoints();
            if (endpoints == null || endpoints.Length == 0)
                throw new InvalidOperationException("Redis没有可用的终结点,请检查连接配置");
            return _connection.Value.GetServer(endpoints[0]);
        }
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Controllers/Task/RobotTaskController.cs
@@ -1,32 +1,35 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.BaseController;
using WIDESEAWCS_Core.Enums;
using WIDESEAWCS_DTO.TaskInfo;
using WIDESEAWCS_ISystemServices;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
namespace WIDESEAWCS_Server.Controllers.Task
{
    [Route("api/RobotTask")]
    [ApiController]
    public class RobotTaskController : ApiBaseController<IRobotTaskService, Dt_RobotTask>
    {
        public RobotTaskController(IRobotTaskService service) : base(service)
        {
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.BaseController;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
namespace WIDESEAWCS_Server.Controllers.Task
{
    [Route("api/RobotTask")]
    [ApiController]
    public class RobotTaskController : ApiBaseController<IRobotTaskService, Dt_RobotTask>
    {
        public RobotTaskController(IRobotTaskService service) : base(service)
        {
        }
        [HttpGet, HttpPost, Route("DeleteRobotTask"), AllowAnonymous]
        public WebResponseContent DeleteRobotTask(int id)
        {
            if (Service.DeleteRobotTask(id)){
        [HttpGet, HttpPost, Route("DeleteRobotTask"), AllowAnonymous]
        public WebResponseContent DeleteRobotTask(int id)
        {
            if (Service.DeleteRobotTask(id))
            {
                return WebResponseContent.Instance.OK();
            }
            return WebResponseContent.Instance.Error();
        }
    }
}
            }
            return WebResponseContent.Instance.Error();
        }
        [HttpGet, HttpPost, Route("GetRobotTaskTotalNum"), AllowAnonymous]
        public int GetRobotTaskTotalNum( int taskType, string? palletCode)
        {
            return Service.GetRobotTaskTotalNum(taskType, palletCode);
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/appsettings.json
@@ -34,7 +34,7 @@
  //5.PostgreSQL
  "DBType": "SqlServer",
  //连接字符串
  "ConnectionString": "Data Source=.;Initial Catalog=WIDESEAWCS_ShanMei;User ID=sa;Password=P@ssw0rd;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
  "ConnectionString": "Data Source=192.168.60.30;Initial Catalog=WIDESEAWCS_ShanMei;User ID=sa;Password=P@ssw0rd;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
  //"ConnectionString": "Data Source=.;Initial Catalog=WIDESEAWCS_ShanMei;User ID=sa;Password=123456;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
  //跨域
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/RobotTaskService.cs
@@ -20,10 +20,12 @@
using MapsterMapper;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Serilog;
using SqlSugar;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using WIDESEA_Core;
using WIDESEAWCS_Common;
using WIDESEAWCS_Common.HttpEnum;
@@ -73,6 +75,7 @@
            _taskExecuteDetailService = taskExecuteDetailService;
            _logger = logger;
        }
        public override WebResponseContent DeleteData(object[] keys)
        {
            List<int> taskKeys = new List<int>();
@@ -83,8 +86,8 @@
            List<Dt_RobotTask> tasks = BaseDal.QueryData(x => taskKeys.Contains(x.RobotTaskId));
            BaseDal.DeleteAndMoveIntoHty(tasks, OperateTypeEnum.人工删除);
            return WebResponseContent.Instance.OK($"成功删除{tasks.Count}条数据");
        }
        public bool DeleteRobotTask(int id)
        {
            Dt_RobotTask task = BaseDal.QueryFirst(x => x.RobotTaskId == id);
@@ -133,7 +136,7 @@
        public Dt_RobotTask? QueryRobotCraneTask(string deviceCode)
        {
            return BaseDal.QueryFirst(x => x.RobotRoadway == deviceCode && x.RobotTaskState != (int)TaskRobotStatusEnum.RobotExecuting, TaskOrderBy);
            return BaseDal.QueryFirst(x => x.RobotRoadway == deviceCode, TaskOrderBy);
        }
        public Dt_RobotTask? QueryRobotCraneExecutingTask(string deviceCode)
@@ -341,7 +344,7 @@
        /// 获取机械手任务总数量。
        /// 组盘任务固定48,换盘和拆盘任务通过托盘号查询WMS库存明细数量。
        /// </summary>
        private int GetRobotTaskTotalNum(int taskType, string? palletCode)
        public int GetRobotTaskTotalNum(int taskType, string? palletCode)
        {
            if (taskType == (int)RobotTaskTypeEnum.GroupPallet)
                return 48;
@@ -351,8 +354,10 @@
            try
            {
                QuartzLogHelper.LogInfo(_logger, $"开始调用WMS接口获取库存明细数量,托盘号:【{palletCode}】", "RobotTaskService");
                string url = $"{BaseAPI.WMSBaseUrl}Stock/GetStockDetailCount?palletCode={Uri.EscapeDataString(palletCode)}";
                var result = _httpClientHelper.Get(url);
                QuartzLogHelper.LogInfo(_logger, $"调用WMS获取库存明细数量接口,请求URL:【{url}】,响应数据:【{result.Content}】,耗时:{result.Duration}ms", "RobotTaskService");
                if (!result.IsSuccess || string.IsNullOrEmpty(result.Content))
                    return 1;
@@ -360,8 +365,8 @@
                if (response == null || !response.Status)
                    return 1;
                var detailCount = response.Data?.GetType().GetProperty("DetailCount")?.GetValue(response.Data);
                return detailCount is int count and > 0 ? count : 1;
                var detailCount = (response.Data as JObject)?["detailCount"]?.Value<int>();
                return detailCount.HasValue && detailCount.Value > 0 ? detailCount.Value : 1;
            }
            catch
            {
@@ -466,8 +471,10 @@
                    CommonConveyorLine conveyorLine = (CommonConveyorLine)device;
                    DeviceProDTO? devicePro = conveyorLine.DeviceProDTOs.FirstOrDefault(x => x.DeviceProParamName == nameof(ConveyorLineDBNameNew.Barcode) && x.DeviceChildCode == sourceLineNo);
                    //conveyorLine.Communicator.Read(devicePro.DeviceProAddress, 20);
                    //ConveyorLineTaskCommandNew command = conveyorLine.ReadCustomer<ConveyorLineTaskCommandNew>(sourceLineNo);  // 测试用
                    var barcode = conveyorLine.GetValue<ConveyorLineDBNameNew, string>(ConveyorLineDBNameNew.Barcode, sourceLineNo);
                    var bytes = conveyorLine.Communicator.Read(devicePro.DeviceProAddress, 20);
                    var barcode = Encoding.Default.GetString(bytes).Trim();
                    stock.SourcePalletNo = string.IsNullOrEmpty(barcode) ? string.Empty : barcode;
                }
            }
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/CommonConveyorLineNewJob.cs
@@ -1,16 +1,11 @@
using MapsterMapper;
using Masuit.Tools;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Quartz;
using Serilog;
using SqlSugar;
using WIDESEA_Core;
using WIDESEAWCS_Common.HttpEnum;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_DTO.TaskInfo;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob;
@@ -228,7 +223,11 @@
                            {
                                // 如果 WCS_ACK 为 1,先清除(表示处理过上一次请求)
                                if (command.WCS_ACK == 1)
                                    conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)0, childDeviceCode);
                                {
                                    conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (byte)0, childDeviceCode);
                                    //Thread.Sleep(300);
                                    //conveyorLine.SetValue(ConveyorLineDBNameNew.Target, (short)0, childDeviceCode);
                                }
                                // 处理手动入库任务(起点为线体点位的任务)
                                try
@@ -247,17 +246,36 @@
                                }
                                continue;
                            }
                            else
                            {
                                if (childDeviceCode == "2103" || childDeviceCode == "2101")
                                {
                                    try
                                    {
                                        var task = _taskService.QueryManualInboundTask(childDeviceCode);
                                        if (task != null)
                                        {
                                            QuartzLogHelper.LogInfo(_logger, $"获取到输送线开始任务,任务号:{task.TaskNum},状态: {task.TaskStatus},当前地址:{conveyorLine.DeviceCode}", conveyorLine.DeviceCode);
                                            var handler = new ManualInboundTaskHandler(_taskService, _logger);
                                            handler.WriteTaskToPlc(conveyorLine, childDeviceCode, task);
                                        }
                                    }
                                    catch (Exception ex)
                                    {
                                        QuartzLogHelper.LogError(_logger, ex, "处理手动入库任务异常", $"处理手动入库任务异常: {ex.Message}", "CommonConveyorLineNewJob");
                                    }
                                }
                            }
                            // ========== 处理无托盘条码的情况 ==========
                            // 无托盘条码时,请求出库任务
                            if (command.Barcode.IsNullOrEmpty() || command.Barcode.Replace("\0", "") == "")
                            // 如果 PLC_STB 为 1,但没有任务号,可能是新任务的开始,先请求出库任务(适用于无条码的情况)
                            if (command.TaskNo == 1000)
                            {
                                _conveyorLineDispatch.RequestOutbound(conveyorLine, command, childDeviceCode);
                                continue;
                            }
                            // ========== 处理已有任务号的情况 ==========
                            if (command.TaskNo > 0 && !command.Barcode.IsNullOrEmpty())
                            if (command.TaskNo > 0)
                            {
                                // 查询正在执行的任务
                                Dt_Task task = _taskService.QueryExecutingConveyorLineTask(command.TaskNo, childDeviceCode);
@@ -299,48 +317,6 @@
                                        conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, childDeviceCode);
                                    }
                                }
                                //else if (!command.Barcode.IsNullOrEmpty() && (childDeviceCode == "11001" || childDeviceCode == "11010"))
                                //{
                                //    var isWcsTask = _taskService.Db.Queryable<Dt_Task>().Any(x => x.PalletCode == command.Barcode && (x.TaskStatus == (int)TaskOutStatusEnum.OutNew || x.TaskStatus == (int)TaskInStatusEnum.InNew));
                                //    var isRobotTask = _robotTaskService.Db.Queryable<Dt_RobotTask>().Any(x => x.RobotTargetAddressPalletCode == command.Barcode);
                                //    if (isWcsTask || isRobotTask)
                                //    {
                                //        continue;
                                //    }
                                //    // 调用 WMS 创建空托盘入库任务
                                //    string configKey = nameof(ConfigKey.CreateTaskInboundAsync);
                                //    string requestParam = new CreateTaskDto()
                                //    {
                                //        PalletCode = command.Barcode,
                                //        SourceAddress = childDeviceCode,
                                //        TargetAddress = "GWSC1",  // 目标地址
                                //        Roadway = "GWSC1",             // 巷道
                                //        WarehouseId = 1,                   // 仓库 ID
                                //        PalletType = 1,                             // 托盘类型(默认为1)
                                //        TaskType = TaskTypeEnum.InEmpty.GetHashCode()                         // 任务类型(入库/空托盘入库)
                                //    }.Serialize();
                                //    DateTime startTime = DateTime.Now;
                                //    var responseResult = _httpClientHelper.Post<WebResponseContent>(configKey, requestParam);
                                //    if (responseResult.IsSuccess && responseResult.Data.Status)
                                //    {
                                //        QuartzLogHelper.LogInfo(_logger, $"调用WMS接口成功,接口:【{configKey}】,请求参数:【{requestParam}】,响应数据:【{responseResult.Data?.Data}】,耗时:{(DateTime.Now - startTime).TotalMilliseconds}ms", conveyorLine.DeviceCode);
                                //        var wmsTask = JsonConvert.DeserializeObject<WMSTaskDTO>(responseResult?.Data?.Data?.ToString());
                                //        List<WMSTaskDTO> taskDTOs = new List<WMSTaskDTO> { wmsTask };
                                //        if (wmsTask == null) continue;
                                //        if (_taskService.ReceiveWMSTask(taskDTOs).Status)
                                //        {
                                //            conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, childDeviceCode);
                                //        }
                                //    }
                                //    else
                                //    {
                                //        QuartzLogHelper.LogError(_logger, $"调用WMS接口失败,接口:【{configKey}】,请求参数:【{requestParam}】,错误信息:【{responseResult.Data?.Message}】", conveyorLine.DeviceCode);
                                //    }
                                //}
                            }
                        }
                        catch (Exception innerEx)
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs
@@ -257,21 +257,21 @@
            // 确定目标地址:空托盘任务使用 "2201",其他任务使用 NextAddress
            var isEmptyTask = task.TaskType == (int)TaskOutboundTypeEnum.OutEmpty;
            var targetAddress = task.CurrentAddress == "2217" ? (isEmptyTask ? "2201" : task.NextAddress) : task.NextAddress;
            var targetAddress = task.CurrentAddress == "2217" ? (isEmptyTask ? task.TargetAddress : task.NextAddress) : task.NextAddress;
            // 处理特殊地址 2217,需要调用目标地址选择器
            if (task.CurrentAddress == "2217" && !_targetAddressSelector.HandleOutboundNextAddress(conveyorLine, targetAddress, childDeviceCode))
            {
                return Task.CompletedTask; ;
            }
            //if (task.CurrentAddress == "2217" && !_targetAddressSelector.HandleOutboundNextAddress(conveyorLine, targetAddress, childDeviceCode))
            //{
            //    return Task.CompletedTask; ;
            //}
            // 设置任务号、托盘条码、目标地址、WCS_ACK
            var isTaskNoSet = conveyorLine.SetValue(ConveyorLineDBNameNew.TaskNo, task.TaskNum, childDeviceCode);
            Thread.Sleep(100); // 确保 PLC 能正确读取任务号后再写入条码
            Thread.Sleep(300); // 确保 PLC 能正确读取任务号后再写入条码
            var isPalletSet = conveyorLine.SetValue(ConveyorLineDBNameNew.Barcode, task.PalletCode, childDeviceCode);
            Thread.Sleep(100); // 确保 PLC 能正确读取任务号后再写入条码
            Thread.Sleep(300); // 确保 PLC 能正确读取任务号后再写入条码
            bool isTargetSet = conveyorLine.SetValue(ConveyorLineDBNameNew.Target, targetAddress, childDeviceCode);
            //if (targetAddress == "2217" && !isEmptyTask)
            //{
@@ -281,16 +281,16 @@
            //}
            //if (!isTargetSet || !isTaskNoSet || !isPalletSet)
            //{
            //    QuartzLogHelper.LogError(_logger, $"RequestOutbound:下发出库任务失败,任务号: {task.TaskNum},子设备: {childDeviceCode}", conveyorLine.DeviceCode);
            //    QuartzLogHelper.LogError(_logger, $"RequestOutbound:下发出库任务失败,任务号: {task.TaskNum},子设备: {childDeviceCode}", conveyorLine.DeviceCode);
            //    return Task.CompletedTask;
            //}
            bool isWmsResult = false;
            // 更新任务状态或位置
            if (isEmptyTask && task.NextAddress == "2217")
            if (isEmptyTask && (task.TargetAddress == "2103" || task.TargetAddress == "2101"))
            {
                task.TaskStatus = task.TaskStatus.GetNextNotCompletedStatus<TaskOutStatusEnum>();
                task.NextAddress = "2201";
                task.NextAddress = "2103";
                isWmsResult = _taskService.Repository.UpdateData(task);
            }
            else
@@ -298,7 +298,7 @@
                isWmsResult = _taskService.UpdateTaskStatusToNext(task).Status;
            }
            if(!isWmsResult)
            if (!isWmsResult)
            {
                QuartzLogHelper.LogError(_logger, $"RequestOutbound:更新任务状态失败,任务号: {task.TaskNum},子设备: {childDeviceCode}", conveyorLine.DeviceCode);
                return Task.CompletedTask;
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineTargetAddressSelector.cs
@@ -68,8 +68,11 @@
            var cvState = conveyorLine.GetValue<ConveyorLineDBNameNew, byte>(ConveyorLineDBNameNew.CV_State, nextAddress);
            bool isAvailable = cvState == 2;
            WriteDebug(conveyorLine, "出库下一地址状态", childDeviceCode, $"CV_State={cvState},可用={isAvailable}");
            if (isAvailable)
            {
                WriteDebug(conveyorLine, "出库下一地址可用,写入目标地址", childDeviceCode, nextAddress);
                Thread.Sleep(300); // 短暂等待,确保设备状态稳定后再写入目标地址
                return conveyorLine.SetValue(ConveyorLineDBNameNew.Target, Convert.ToInt16(nextAddress), childDeviceCode);
            }
            return false;
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs
@@ -627,6 +627,12 @@
            // 解析 WMS 返回的任务信息
            WMSTaskDTO taskDTO = JsonConvert.DeserializeObject<WMSTaskDTO>(result.Data.Data.ToJson() ?? string.Empty) ?? new WMSTaskDTO();
            var task = await _taskService.Repository.QueryFirstAsync(x => x.PalletCode == taskDTO.PalletCode);
            if(task != null)
            {
                await _taskService.Repository.DeleteDataAsync(task);
            }
            // 调用任务服务接收 WMS 任务
            var content = _taskService.ReceiveWMSTask(new List<WMSTaskDTO> { taskDTO });
            if (!content.Status)
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs
@@ -197,7 +197,7 @@
                            // 调用批量拆盘确认接口
                            var sourcePallet = state.CurrentTask.RobotSourceAddressPalletCode;
                            var confirmResult = _taskProcessor.PostSplitPalletConfirmAsync(sourcePallet, state.RobotCrane?.DeviceName);
                            if (!confirmResult.IsSuccess)
                            if (!confirmResult.IsSuccess && !confirmResult.Data.Status)
                            {
                                QuartzLogHelper.LogError(_logger, $"批量拆盘确认失败: {confirmResult.ErrorMessage}", state.RobotCrane?.DeviceName ?? "Unknown");
                                return false;
@@ -314,7 +314,7 @@
                            // 调用批量组盘确认接口
                            var targetPallet = state.CurrentTask.RobotTargetAddressPalletCode;
                            var confirmResult = _taskProcessor.PostGroupPalletConfirmAsync(targetPallet, state.RobotCrane?.DeviceName);
                            if (!confirmResult.IsSuccess)
                            if (!confirmResult.IsSuccess && !confirmResult.Data.Status)
                            {
                                QuartzLogHelper.LogError(_logger, $"批量组盘确认失败: {confirmResult.ErrorMessage}", state.RobotCrane?.DeviceName ?? "Unknown");
                                return false;
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs
@@ -396,10 +396,13 @@
                // 目标数量为48:直接走原有逻辑,不进入批次模式
                if (targetNormalCount + currentCompletedCount == targetTotal)
                {
                    QuartzLogHelper.LogInfo(_logger, $"HandlePutFinishedStateAsync:目标数量已达48,直接下发取货指令,任务号: {task.RobotTaskNum}", stateForUpdate?.RobotCrane?.DeviceName ?? ipAddress);
                    await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate);
                    return;
                }
                QuartzLogHelper.LogDebug(_logger,$"HandlePutFinishedStateAsync:换盘任务目标数量: {targetNormalCount},当前已完成数量: {currentCompletedCount},流向: {(isFlowA ? "A" : "B")},任务号: {task.RobotTaskNum}", stateForUpdate?.RobotCrane?.DeviceName ?? ipAddress);
                // 初始化批次模式
                if (stateForUpdate.ChangePalletPhase == 0)
                {
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Messaging.cs
@@ -3,6 +3,7 @@
using System.Text.Json;
using System.IO;
using WIDESEAWCS_Model.Models;
using Serilog.Core;
namespace WIDESEAWCS_Tasks.SocketServer
{
@@ -64,6 +65,7 @@
                        //{
                        //    if (_clientLastMessage.TryGetValue(clientId, out var prev) && message == prev)
                        //    {
                        //        QuartzLogHelper.LogInfo(Logger.None, $"来自客户端 {clientId} 的重复消息,内容: {message}", clientId);
                        //        continue;
                        //    }
                        //    _clientLastMessage[clientId] = message;
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/CommonStackerCraneJob.cs
@@ -121,36 +121,6 @@
        }
        /// <summary>
        /// 加载配置文件(优先级:配置文件 > 默认配置)
        /// </summary>
        /// <remarks>
        /// 从应用程序目录下的 StackerCraneJob/stackercrane-command-config.json 读取配置。
        /// 如果文件不存在或解析失败,使用默认配置。
        /// </remarks>
        /// <returns>堆垛机命令配置</returns>
        private static StackerCraneCommandConfig LoadConfig()
        {
            try
            {
                // 构造配置文件路径
                string configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "StackerCraneJob", "stackercrane-command-config.json");
                if (File.Exists(configPath))
                {
                    // 读取并解析 JSON 配置
                    string json = File.ReadAllText(configPath);
                    return System.Text.Json.JsonSerializer.Deserialize<StackerCraneCommandConfig>(json) ?? new StackerCraneCommandConfig();
                }
            }
            catch (Exception ex)
            {
                // 配置加载失败,使用默认配置
                Console.WriteLine($"配置加载失败: {ex.Message},使用默认配置");
            }
            return new StackerCraneCommandConfig();
        }
        /// <summary>
        /// Quartz Job 的执行入口
        /// </summary>
        /// <remarks>
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneTaskSelector.cs
@@ -1,7 +1,6 @@
using Newtonsoft.Json;
using Serilog;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using WIDESEA_Core;
using WIDESEAWCS_Common.Constants;
using WIDESEAWCS_Common.HttpEnum;
@@ -10,7 +9,6 @@
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob;
using WIDESEAWCS_QuartzJob.ConveyorLine.Enum;
using WIDESEAWCS_QuartzJob.Models;
using WIDESEAWCS_QuartzJob.Service;
@@ -208,7 +206,7 @@
        {
            // 先进行本地站台检查(PLC 读取,快速),避免不必要的 WMS HTTP 调用
            if (outboundTask.TaskType != (int)TaskOutboundTypeEnum.OutEmpty && outboundTask.Roadway != "GWSC1" && outboundTask.TargetAddress != "CWSC1")
            if (outboundTask.TaskType != (int)TaskOutboundTypeEnum.OutEmpty && outboundTask.TargetAddress != "CWSC1")
            {
                // 判断 TargetAddress 输送线站台是否空闲
                if (!IsTargetAddressConveyorStationAvailable(outboundTask))
@@ -223,18 +221,18 @@
                return null;
            }
            if (outboundTask.TargetAddress != "CWSC1")
            //if (outboundTask.TargetAddress != "CWSC1")
            //{
            // 检查是否有正在执行的输送线任务去往同一 TargetAddress
            if (_taskService.HasExecutingTaskToTarget(outboundTask.Roadway, outboundTask.TargetAddress))
            {
                // 检查是否有正在执行的输送线任务去往同一 TargetAddress
                if (_taskService.HasExecutingTaskToTarget(outboundTask.Roadway, outboundTask.TargetAddress))
                {
                    QuartzLogHelper.LogInfo(_logger, "TrySelectOutboundTask:TargetAddress: {TargetAddress} 已有正在执行的输送线任务,任务号: {TaskNum}",
                        $"TrySelectOutboundTask:TargetAddress: {outboundTask.TargetAddress} 已有正在执行的输送线任务", outboundTask.Roadway, outboundTask.TargetAddress, outboundTask.TaskNum);
                    return null;
                }
                QuartzLogHelper.LogInfo(_logger, "TrySelectOutboundTask:TargetAddress: {TargetAddress} 已有正在执行的输送线任务,任务号: {TaskNum}",
                    $"TrySelectOutboundTask:TargetAddress: {outboundTask.TargetAddress} 已有正在执行的输送线任务", outboundTask.Roadway, outboundTask.TargetAddress, outboundTask.TaskNum);
                return null;
            }
            //}
            if(outboundTask.Roadway != "GWSC1")
            if (outboundTask.Roadway != "GWSC1")
            {
                return outboundTask;
            }
Code/WMS/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs
@@ -159,9 +159,12 @@
                    && x.LocationType == locationType)
                .CountAsync();
            // 空闲货位不足最低保留数量时返回null,避免将巷道分配耗尽
            const int minFreeLocationThreshold = 5;
            if (freeCount < minFreeLocationThreshold) return null;
            if(roadwayNo != "CWSC1")
            {
                // 空闲货位不足最低保留数量时返回null,避免将巷道分配耗尽
                const int minFreeLocationThreshold = 1;
                if (freeCount < minFreeLocationThreshold) return null;
            }
            // 数据库端排序取第一条(只传输单行数据)
            return await BaseDal.Db.Queryable<Dt_LocationInfo>()
Code/WMS/WIDESEA_WMSServer/WIDESEA_Common/Constants/OutboundTimeConstants.cs
@@ -29,16 +29,16 @@
        /// <summary>
        /// GW_1首放入库时效(24小时)
        /// </summary>
        public const int OUTBOUND_HOURS_GW1_FIRST = 24;
        public const double OUTBOUND_HOURS_GW1_FIRST = 24;
        /// <summary>
        /// GW_1二放入库时效(24小时)
        /// GW_1二放入库时效(5分钟)
        /// </summary>
        public const int OUTBOUND_HOURS_GW1_SECOND = 24;
        public const double OUTBOUND_HOURS_GW1_SECOND = 0.05;
        /// <summary>
        /// CW_1出库时效(12小时)
        /// CW_1出库时效(3小时)
        /// </summary>
        public const int OUTBOUND_HOURS_CW1 = 12;
        public const double OUTBOUND_HOURS_CW1 = 3;
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_Common/Constants/TaskAddressConstants.cs
@@ -13,7 +13,7 @@
        /// <summary>
        /// 高温1号出库地址列表(轮询)
        /// </summary>
        public static readonly string[] GW1_ADDRESSES = { "11001" };
        public static readonly string[] GW1_ADDRESSES = { "11010" };
        /// <summary>
        /// 高温2号出库地址
@@ -28,7 +28,7 @@
        /// <summary>
        /// 分容库出库地址
        /// </summary>
        public const string GRADING_OUTBOUND_ADDRESS = "2103";
        public const string GRADING_OUTBOUND_ADDRESS = "2101";
        /// <summary>
        /// 分容库出库地址
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Inbound.cs
@@ -141,8 +141,10 @@
                            Creater = StockConstants.SYSTEM_USER,
                            Details = null,
                            LocationCode = location.LocationCode,
                            LocationId = location.Id
                            LocationId = location.Id,
                            OutboundDate = DateTime.Now
                        };
                        location.LocationStatus = LocationStatusEnum.InStock.GetHashCode();
                        var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
                        var updateStockResult = await _stockInfoService.Repository.AddDataAsync(stockInfo);
                        return await CompleteTaskAsync(task, "入库完成");
@@ -167,6 +169,8 @@
                        stockInfo.StockStatus = StockStatusEmun.入库完成.GetHashCode();
                        stockInfo.CreateDate = DateTime.Now;
                        location.LocationStatus = LocationStatusEnum.InStock.GetHashCode();
                        var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/appsettings.json
@@ -34,7 +34,7 @@
  "MainDB": "DB_WIDESEA", //当前项目的主库,所对应的连接字符串的Enabled必须为true
  //连接字符串
  //"ConnectionString": "HTI6FB1H05Krd07mNm9yBCNhofW6edA5zLs9TY~MNthRYW3kn0qKbMIsGp~3yyPDF1YZUCPBQx8U0Jfk4PH~ajNFXVIwlH85M3F~v_qKYQ3CeAz3q1mLVDn8O5uWt1~3Ut2V3KRkEwYHvW2oMDN~QIDXPxDgXN0R2oTIhc9dNu7QNaLEknblqmHhjaNSSpERdDVZIgHnMKejU_SL49tralBkZmDNi0hmkbL~837j1NWe37u9fJKmv91QPb~16JsuI9uu0EvNZ06g6PuZfOSAeFH9GMMIZiketdcJG3tHelo=",
  "ConnectionString": "Data Source=.;Initial Catalog=WIDESEAWMS_ShanMei;User ID=sa;Password=P@ssw0rd;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
  "ConnectionString": "Data Source=192.168.60.30;Initial Catalog=WIDESEAWMS_ShanMei;User ID=sa;Password=P@ssw0rd;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
  //"ConnectionString": "Data Source=.;Initial Catalog=WIDESEAWMS_ShanMei;User ID=sa;Password=123456;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
  //"ConnectionString": "Data Source=10.30.4.92;Initial Catalog=WMS_TC;User ID=sa;Password=duo123456;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
  //旧WMS数据库连接