wanshenmean
2026-03-18 da5d613a85d97ecd826e343eae6d901806120a05
fix: 修复输送线目标地址选择器并完善机械手任务地址映射
已修改2个文件
302 ■■■■ 文件已修改
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs 277 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineTargetAddressSelector.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs
@@ -19,7 +19,9 @@
using MapsterMapper;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Text;
using SqlSugar;
using System.Diagnostics.CodeAnalysis;
using WIDESEA_Core;
@@ -36,6 +38,8 @@
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob;
using WIDESEAWCS_QuartzJob.DeviceBase;
using WIDESEAWCS_QuartzJob.DTO;
using WIDESEAWCS_QuartzJob.Models;
using WIDESEAWCS_QuartzJob.Service;
@@ -48,6 +52,7 @@
        private readonly ITaskExecuteDetailRepository _taskExecuteDetailRepository;
        private readonly IMapper _mapper;
        private readonly HttpClientHelper _httpClientHelper;
        private readonly IRobotTaskService _robotTaskService;
        private Dictionary<string, OrderByType> _taskOrderBy = new()
            {
@@ -66,13 +71,14 @@
        public List<int> TaskRobotTypes => typeof(TaskOtherTypeEnum).GetEnumIndexList();
        public TaskService(ITaskRepository BaseDal, IRouterService routerService, ITaskExecuteDetailService taskExecuteDetailService, ITaskExecuteDetailRepository taskExecuteDetailRepository, IMapper mapper, HttpClientHelper httpClientHelper) : base(BaseDal)
        public TaskService(ITaskRepository BaseDal, IRouterService routerService, ITaskExecuteDetailService taskExecuteDetailService, ITaskExecuteDetailRepository taskExecuteDetailRepository, IMapper mapper, HttpClientHelper httpClientHelper, IRobotTaskService robotTaskService) : base(BaseDal)
        {
            _routerService = routerService;
            _taskExecuteDetailService = taskExecuteDetailService;
            _taskExecuteDetailRepository = taskExecuteDetailRepository;
            _mapper = mapper;
            _httpClientHelper = httpClientHelper;
            _robotTaskService = robotTaskService;
        }
        /// <summary>
@@ -96,7 +102,13 @@
                    task.Creater = "WMS";
                    if (task.TaskType.GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup)
                    {
                        var taskType = 100;
                        int taskType = 0;
                        if (task.TaskType == (int)TaskOutboundTypeEnum.OutEmpty)
                        {
                            taskType = 100;
                        }
                        else
                            taskType = task.TaskType;
                        Dt_Router router = _routerService.QueryNextRoute(item.Roadway, item.TargetAddress, taskType);
                        //暂不考虑多路径
                        if (router != null)
@@ -370,7 +382,7 @@
            // 更新任务数据
            task.ModifyDate = DateTime.Now;
            task.Modifier = "System";
            if(task.TaskStatus == (int)TaskOutStatusEnum.Line_OutFinish)
            if (task.TaskStatus == (int)TaskOutStatusEnum.Line_OutFinish)
            {
                BaseDal.DeleteAndMoveIntoHty(task, OperateTypeEnum.自动删除);
            }
@@ -408,12 +420,14 @@
            }
            else if (task.TaskStatus == (int)TaskOutStatusEnum.Line_OutFinish && task.TaskType == (int)TaskOutboundTypeEnum.OutEmpty)
            {
                return GetWMSRobotTask(task);
                return _robotTaskService.GetWMSRobotTask(task);
            }
            else
            {
                // return UpdateWMSTaskStatus(task);
                return WebResponseContent.Instance.OK();
                if (task.TaskStatus == (int)TaskOutStatusEnum.Line_OutExecuting)
                    return WebResponseContent.Instance.OK();
                else
                    return UpdateWMSTaskStatus(task);
            }
        }
@@ -506,8 +520,8 @@
        private WebResponseContent GetWMSOutboundTrayTask(Dt_Task task)
        {
            var targetAddress = task.TargetAddress;
            var warehouseId = MapWarehouseIdConfigKey(task.TargetAddress);
            string sourceLineNo = ResolveRobotRuleValue(targetAddress, "AddressSourceLineNoMap", task.TargetAddress);
            var warehouseId = _robotTaskService.MapWarehouseIdConfigKey(task.TargetAddress);
            string sourceLineNo = _robotTaskService.ResolveRobotRuleValue(targetAddress, "AddressSourceLineNoMap", task.TargetAddress);
            var result = _httpClientHelper.Post<WebResponseContent>(
                nameof(ConfigKey.GetOutBoundTrayTaskAsync),
@@ -516,153 +530,17 @@
            if (!result.IsSuccess || !result.Data.Status)
                return WebResponseContent.Instance.Error($"获取WMS系统空托盘出库任务失败,任务号:【{task.TaskNum}】,托盘号:【{task.PalletCode}】,错误信息:【{result.Data?.Message}】");
            var wMSTask = JsonConvert.DeserializeObject<WMSTaskDTO>(result.Data.Data.ToString());
            var wMSTask = JsonConvert.DeserializeObject<WMSTaskDTO>(result.Data.Data?.ToString() ?? string.Empty);
            var tasks = new List<WMSTaskDTO>();
            tasks.Add(wMSTask);
            var tasks = new List<WMSTaskDTO>
            {
                wMSTask
            };
            return ReceiveWMSTask(tasks);
        }
        /// <summary>
        /// 获取WMS系统机械手任务
        /// </summary>
        /// <param name="task"></param>
        /// <returns></returns>
        private WebResponseContent GetWMSRobotTask(Dt_Task task)
        {
            string configKey = ResolveRobotTaskConfigKey(task.TargetAddress);
            StockDTO stock = BuildRobotTaskStock(task, configKey);
            var result = _httpClientHelper.Post<WebResponseContent>(configKey, stock.ToJson());
            if (!result.IsSuccess || !result.Data.Status)
                return WebResponseContent.Instance.Error($"获取WMS系统机械手任务失败,任务号:【{task.TaskNum}】,托盘号:【{task.PalletCode}】,目标地址:【{task.TargetAddress}】,接口:【{configKey}】,错误信息:【{result.Data?.Message}】");
            var tasks = JsonConvert.DeserializeObject<List<WMSTaskDTO>>(result.Data.Data?.ToString() ?? string.Empty);
            if (tasks == null || tasks.Count == 0)
                return WebResponseContent.Instance.Error($"获取WMS系统机械手任务失败,任务号:【{task.TaskNum}】,托盘号:【{task.PalletCode}】,错误信息:【WMS未返回有效任务数据】");
            return ReceiveWMSTask(tasks);
        }
        /// <summary>
        /// 根据输送线目标地址解析机械手任务接口。
        /// 规则:
        /// 1. 从配置读取精确地址映射(AddressMap)
        /// 2. 未命中时默认换盘
        /// </summary>
        private string ResolveRobotTaskConfigKey(string? targetAddress)
        {
            string address = (targetAddress ?? string.Empty).Trim();
            if (string.IsNullOrWhiteSpace(address))
                return nameof(ConfigKey.CreateRobotChangePalletTask);
            var section = App.Configuration.GetSection("RobotTaskAddressRules");
            var addressMap = ReadRobotRuleMap(section.GetSection("AddressMap"));
            if (addressMap.TryGetValue(address, out string? exactTaskType))
                return MapRobotTaskTypeToConfigKey(exactTaskType);
            return nameof(ConfigKey.CreateRobotChangePalletTask);
        }
        private int MapWarehouseIdConfigKey(string? targetAddress)
        {
            return targetAddress switch
            {
                "11068" => 1,
                "11001" => 3,
                "11010" => 3,
                "10010" => 1,
                "10030" => 1,
                _ => 1
            };
        }
        /// <summary>
        /// 将配置任务类型转换为接口配置键。
        /// 支持值:Split/Group/Change(大小写不敏感)
        /// </summary>
        private string MapRobotTaskTypeToConfigKey(string? taskType)
        {
            string type = (taskType ?? string.Empty).Trim().ToLowerInvariant();
            return type switch
            {
                "split" => nameof(ConfigKey.CreateRobotSplitPalletTask),
                "group" => nameof(ConfigKey.CreateRobotGroupPalletTask),
                _ => nameof(ConfigKey.CreateRobotChangePalletTask)
            };
        }
        /// <summary>
        /// 根据接口类型构建机械手任务入参。
        /// </summary>
        private StockDTO BuildRobotTaskStock(Dt_Task task, string configKey)
        {
            string targetAddress = task.TargetAddress ?? string.Empty;
            string roadway = ResolveRobotRuleValue(targetAddress, "AddressRoadwayMap", task.Roadway);
            string sourceLineNo = ResolveRobotRuleValue(targetAddress, "AddressSourceLineNoMap", task.SourceAddress);
            var stock = new StockDTO
            {
                Roadway = roadway,
                SourceLineNo = sourceLineNo,
                TargetLineNo = task.TargetAddress,
                SourcePalletNo = task.PalletCode,
                TargetPalletNo = task.PalletCode
            };
            if (configKey == nameof(ConfigKey.CreateRobotSplitPalletTask))
            {
                stock.TargetPalletNo = string.Empty;
            }
            else if (configKey == nameof(ConfigKey.CreateRobotGroupPalletTask))
            {
                stock.SourcePalletNo = string.Empty;
            }
            else if (configKey == nameof(ConfigKey.CreateRobotChangePalletTask))
            {
                IDevice? device = Storage.Devices.FirstOrDefault(x => x.DeviceProDTOs.Any(d => d.DeviceChildCode == sourceLineNo));
                if (device != null)
                {
                    CommonConveyorLine conveyorLine = (CommonConveyorLine)device;
                    var barcode = conveyorLine.GetValue<ConveyorLineDBNameNew, string>(ConveyorLineDBNameNew.Barcode, sourceLineNo);
                    stock.SourcePalletNo = string.IsNullOrEmpty(barcode) ? string.Empty : barcode;
                }
            }
            return stock;
        }
        /// <summary>
        /// 根据目标地址按「精确 > 回退值」解析规则值。
        /// </summary>
        private string ResolveRobotRuleValue(string? targetAddress, string addressSectionName, string? fallback)
        {
            string address = (targetAddress ?? string.Empty).Trim();
            string defaultValue = fallback ?? string.Empty;
            if (string.IsNullOrWhiteSpace(address))
                return defaultValue;
            var section = App.Configuration.GetSection("RobotTaskAddressRules");
            var addressMap = ReadRobotRuleMap(section.GetSection(addressSectionName));
            if (addressMap.TryGetValue(address, out string? value))
                return value;
            return defaultValue;
        }
        /// <summary>
        /// 读取规则映射段。
        /// </summary>
        private Dictionary<string, string> ReadRobotRuleMap(IConfigurationSection section)
        {
            return section
                .GetChildren()
                .Where(x => !string.IsNullOrWhiteSpace(x.Key) && !string.IsNullOrWhiteSpace(x.Value))
                .ToDictionary(x => x.Key.Trim(), x => x.Value!.Trim());
        }
        /// <summary>
        /// 从WMS系统获取入库目标地址
@@ -744,7 +622,8 @@
                string oldCurrentPos = task.CurrentAddress;
                string oldNextPos = task.NextAddress;
                Dt_Router routers = _routerService.QueryNextRoute(task.CurrentAddress);
                Dt_Router routers = _routerService.QueryNextRoute(oldNextPos, task.TargetAddress);
                if (routers == null) throw new Exception($"未找到设备路由信息");
                task.CurrentAddress = task.NextAddress;
@@ -779,13 +658,20 @@
                if (task.TaskType.GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup && task.TaskStatus == (int)TaskOutStatusEnum.SC_OutExecuting)
                {
                    List<Dt_Router> routers = _routerService.QueryNextRoutes(task.NextAddress, task.TargetAddress);
                    if (!routers.Any()) return WebResponseContent.Instance.Error($"未找到设备路由信息");
                    int taskType = 0;
                    if (task.TaskType == (int)TaskOutboundTypeEnum.OutEmpty)
                    {
                        taskType = 100;
                    }
                    else
                        taskType = task.TaskType;
                    Dt_Router router = _routerService.QueryNextRoute(task.NextAddress, task.TargetAddress, taskType);
                    if (router == null) return WebResponseContent.Instance.Error($"未找到设备路由信息");
                    int nextStatus = task.TaskStatus.GetNextNotCompletedStatus<TaskOutStatusEnum>();
                    task.TaskStatus = nextStatus;
                    task.CurrentAddress = task.NextAddress;
                    task.NextAddress = routers.FirstOrDefault().ChildPosi;
                    task.NextAddress = router.ChildPosi;
                    task.ModifyDate = DateTime.Now;
                    task.Modifier = "System";
@@ -1005,4 +891,87 @@
    {
        Barcode
    }
    public class ConveyorLineTaskCommandNew : DeviceCommand
    {
        /// <summary>
        /// 任务号
        /// </summary>
        public short TaskNo { get; set; }
        /// <summary>
        /// 源位置 开始地址
        /// </summary>
        public short Source { get; set; }
        /// <summary>
        /// 目标位置
        /// </summary>
        public short Target { get; set; }
        /// <summary>
        /// 箱型
        /// </summary>
        public byte BoxType { get; set; }
        /// <summary>
        /// 输送线状态 设备空闲状态
        /// </summary>
        public byte CV_State { get; set; }
        /// <summary>
        /// 输送线错误代码
        /// </summary>
        public byte CV_ERRCode { get; set; }
        /// <summary>
        /// WCS就绪标志 WCS下发完成时,触发为1
        /// </summary>
        public byte WCS_STB { get; set; }
        /// <summary>
        /// WCS应答标志 WCS收到完成时,触发为1
        /// </summary>
        public byte WCS_ACK { get; set; }
        /// <summary>
        /// PLC就绪标志 完成任务时,触发为1
        /// </summary>
        public byte PLC_STB { get; set; }
        /// <summary>
        /// PLC应答标志 收到任务时,触发为1
        /// </summary>
        public byte PLC_ACK { get; set; }
        /// <summary>
        /// PLC请求标志 入库站台,到位写1
        /// </summary>
        public byte PLC_REQ { get; set; }
        /// <summary>
        /// WCS错误代码
        /// </summary>
        public byte WCS_ERRCode { get; set; }
        /// <summary>
        /// WCS特殊标志 (旋转标识、强制放行、循环、特殊申请、是否叠盘、是否堵塞)
        /// </summary>
        public byte WCS_Special { get; set; }
        /// <summary>
        /// 设备自动模式 手动1,自动2
        /// </summary>
        public byte Equ_Auto { get; set; }
        /// <summary>
        /// 尾板标志
        /// </summary>
        public byte Last_pallet { get; set; }
        /// <summary>
        /// 条码(22个字符)
        /// </summary>
        [DataLength(22)]
        public string Barcode { get; set; }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineTargetAddressSelector.cs
@@ -9,6 +9,11 @@
        private const string ConstraintMachineName = "拘束机";
        private const string PinMachineName = "插拔钉机";
        // 拘束机点位
        private static readonly List<string> ConstraintMachineCodes = new List<string> { "10180", "20090" };
        // 插拔钉机点位
        private static readonly List<string> PinMachineCodes = new List<string> { "10190", "20100" };
        public void HandleInboundNextAddress(CommonConveyorLine conveyorLine, string nextAddress, string childDeviceCode)
        {
            HandleDeviceRequest(conveyorLine, nextAddress, childDeviceCode, isUpper: true);
@@ -23,7 +28,7 @@
        {
            var devices = Storage.Devices;
            if (string.Equals(nextAddress, ConstraintMachineName, StringComparison.Ordinal))
            if (ConstraintMachineCodes.Contains(nextAddress))
            {
                ConstraintMachine? constraint = devices.OfType<ConstraintMachine>().FirstOrDefault(d => d.DeviceName == ConstraintMachineName);
                if (constraint == null)
@@ -35,11 +40,11 @@
                    conveyorLine,
                    childDeviceCode,
                    getMaterialRequest: () => isUpper
                        ? constraint.GetValue<ConstraintMachineDBName, bool>(ConstraintMachineDBName.MaterialRequestUpper)
                        : constraint.GetValue<ConstraintMachineDBName, bool>(ConstraintMachineDBName.MaterialRequestLower),
                        ? constraint.GetValue<ConstraintMachineDBName, short>(ConstraintMachineDBName.MaterialRequestUpper) != 0
                        : constraint.GetValue<ConstraintMachineDBName, short>(ConstraintMachineDBName.MaterialRequestLower) != 0,
                    getOutputRequest: () => isUpper
                        ? constraint.GetValue<ConstraintMachineDBName, bool>(ConstraintMachineDBName.OutputRequestUpper)
                        : constraint.GetValue<ConstraintMachineDBName, bool>(ConstraintMachineDBName.OutputRequestLower),
                        ? constraint.GetValue<ConstraintMachineDBName, short>(ConstraintMachineDBName.OutputRequestUpper) != 0
                        : constraint.GetValue<ConstraintMachineDBName, short>(ConstraintMachineDBName.OutputRequestLower) != 0,
                    setOutputReady: outputReq =>
                    {
                        if (isUpper)
@@ -52,7 +57,7 @@
                        }
                    });
            }
            else if (string.Equals(nextAddress, PinMachineName, StringComparison.Ordinal))
            else if (PinMachineCodes.Contains(nextAddress))
            {
                PinMachine? pinMachine = devices.OfType<PinMachine>().FirstOrDefault(d => d.DeviceName == PinMachineName);
                if (pinMachine == null)
@@ -64,11 +69,11 @@
                    conveyorLine,
                    childDeviceCode,
                    getMaterialRequest: () => isUpper
                        ? pinMachine.GetValue<PinMachineDBName, bool>(PinMachineDBName.MaterialRequestUpper)
                        : pinMachine.GetValue<PinMachineDBName, bool>(PinMachineDBName.MaterialRequestLower),
                        ? pinMachine.GetValue<PinMachineDBName, short>(PinMachineDBName.MaterialRequestUpper) != 0
                        : pinMachine.GetValue<PinMachineDBName, short>(PinMachineDBName.MaterialRequestLower) != 0,
                    getOutputRequest: () => isUpper
                        ? pinMachine.GetValue<PinMachineDBName, bool>(PinMachineDBName.OutputRequestUpper)
                        : pinMachine.GetValue<PinMachineDBName, bool>(PinMachineDBName.OutputRequestLower),
                        ? pinMachine.GetValue<PinMachineDBName, short>(PinMachineDBName.OutputRequestUpper) != 0
                        : pinMachine.GetValue<PinMachineDBName, short>(PinMachineDBName.OutputRequestLower) != 0,
                    setOutputReady: outputReq =>
                    {
                        if (isUpper)