| | |
| | | |
| | | 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; |
| | |
| | | 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; |
| | | |
| | |
| | | private readonly ITaskExecuteDetailRepository _taskExecuteDetailRepository; |
| | | private readonly IMapper _mapper; |
| | | private readonly HttpClientHelper _httpClientHelper; |
| | | private readonly IRobotTaskService _robotTaskService; |
| | | |
| | | private Dictionary<string, OrderByType> _taskOrderBy = new() |
| | | { |
| | |
| | | |
| | | 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> |
| | |
| | | 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) |
| | |
| | | // 更新任务数据 |
| | | 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.自动删除); |
| | | } |
| | |
| | | } |
| | | 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); |
| | | } |
| | | } |
| | | |
| | |
| | | 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), |
| | |
| | | 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系统获取入库目标地址 |
| | |
| | | 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; |
| | |
| | | |
| | | 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"; |
| | | |
| | |
| | | { |
| | | 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; } |
| | | } |
| | | } |