| | |
| | | using SqlSugar; |
| | | using System.DirectoryServices.Protocols; |
| | | using System.Text.Json; |
| | | using WIDESEA_Common.Constants; |
| | | using WIDESEA_Common.LocationEnum; |
| | | using WIDESEA_Common.StockEnum; |
| | | using WIDESEA_Common.TaskEnum; |
| | |
| | | using WIDESEA_Core.Enums; |
| | | using WIDESEA_Core.Helper; |
| | | using WIDESEA_DTO.GradingMachine; |
| | | using WIDESEA_DTO.MES; |
| | | using WIDESEA_DTO.Stock; |
| | | using WIDESEA_DTO.Task; |
| | | using WIDESEA_IBasicService; |
| | | using WIDESEA_IRecordService; |
| | | using WIDESEA_IStockService; |
| | | using WIDESEA_ITaskInfoService; |
| | | using WIDESEA_Model.Models; |
| | |
| | | private readonly HttpClientHelper _httpClientHelper; |
| | | private readonly IConfiguration _configuration; |
| | | private readonly RoundRobinService _roundRobinService; |
| | | private readonly IMesService _mesService; |
| | | private readonly ITask_HtyService _task_HtyService; |
| | | private readonly IStockInfo_HtyService _stockInfo_HtyService; |
| | | private readonly IUnitOfWorkManage _unitOfWorkManage; |
| | | private readonly IRecordService _recordService; |
| | | |
| | | public IRepository<Dt_Task> Repository => BaseDal; |
| | | |
| | |
| | | HttpClientHelper httpClientHelper, |
| | | IConfiguration configuration, |
| | | RoundRobinService roundRobinService, |
| | | IUnitOfWorkManage unitOfWorkManage) : base(BaseDal) |
| | | IMesService mesService, |
| | | ITask_HtyService task_HtyService, |
| | | IStockInfo_HtyService stockInfo_HtyService, |
| | | IUnitOfWorkManage unitOfWorkManage, |
| | | IRecordService recordService) : base(BaseDal) |
| | | { |
| | | _mapper = mapper; |
| | | _stockInfoService = stockInfoService; |
| | |
| | | _httpClientHelper = httpClientHelper; |
| | | _configuration = configuration; |
| | | _roundRobinService = roundRobinService; |
| | | _mesService = mesService; |
| | | _task_HtyService = task_HtyService; |
| | | _stockInfo_HtyService = stockInfo_HtyService; |
| | | _unitOfWorkManage = unitOfWorkManage; |
| | | } |
| | | |
| | | #region WCS逻辑处理 |
| | | |
| | | /// <summary> |
| | | /// 创建任务(组盘入库任务、空托盘回库任务) |
| | | /// </summary> |
| | | public async Task<WebResponseContent> CreateTaskInboundAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | WebResponseContent content = await GetTaskByPalletCodeAsync(taskDto.PalletCode); |
| | | if (content.Status) |
| | | { |
| | | return content; |
| | | } |
| | | |
| | | if (string.IsNullOrWhiteSpace(taskDto.PalletCode) || |
| | | string.IsNullOrWhiteSpace(taskDto.Roadway)) |
| | | { |
| | | return WebResponseContent.Instance.Error("无效的任务详情"); |
| | | } |
| | | |
| | | if (taskDto.TaskType != TaskTypeEnum.Inbound && taskDto.TaskType != TaskTypeEnum.InEmpty) |
| | | { |
| | | return WebResponseContent.Instance.Error("无效的任务详情"); |
| | | } |
| | | |
| | | // 使用 switch 表达式映射任务类型 |
| | | int taskInboundType = taskDto.TaskType switch |
| | | { |
| | | TaskTypeEnum.Inbound => TaskInboundTypeEnum.Inbound.GetHashCode(), |
| | | TaskTypeEnum.InEmpty => TaskInboundTypeEnum.InEmpty.GetHashCode(), |
| | | _ => 0 // 理论上不会走到这里,因为已经验证过了 |
| | | }; |
| | | |
| | | var task = new Dt_Task |
| | | { |
| | | TaskNum = await BaseDal.GetTaskNo(), |
| | | PalletCode = taskDto.PalletCode, |
| | | PalletType = taskDto.PalletType, |
| | | Roadway = taskDto.Roadway, |
| | | TaskType = taskInboundType, |
| | | TaskStatus = TaskInStatusEnum.InNew.GetHashCode(), |
| | | SourceAddress = taskDto.SourceAddress, |
| | | TargetAddress = taskDto.TargetAddress, |
| | | CurrentAddress = taskDto.SourceAddress, |
| | | NextAddress = taskDto.TargetAddress, |
| | | WarehouseId = taskDto.WarehouseId, |
| | | Grade = 1, |
| | | Creater = "system" |
| | | }; |
| | | |
| | | var result = await Repository.AddDataAsync(task) > 0; |
| | | if (!result) return WebResponseContent.Instance.Error("任务创建失败"); |
| | | |
| | | var wmstaskDto = _mapper.Map<WMSTaskDTO>(task); |
| | | return WebResponseContent.Instance.OK("任务创建成功", wmstaskDto); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"任务创建失败: {ex.Message}"); |
| | | } |
| | | _recordService = recordService; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 根据指定的任务详情异步创建新的出库任务 |
| | | /// 查找托盘是否存在未完成任务。 |
| | | /// </summary> |
| | | public async Task<WebResponseContent> CreateTaskOutboundAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | var stockResult = await _stockInfoService.GetStockInfoAsync(taskDto.WarehouseId); |
| | | if (stockResult == null || !stockResult.Any()) |
| | | return WebResponseContent.Instance.Error("未找到库存信息"); |
| | | |
| | | var taskList = stockResult.Select(item => new Dt_Task |
| | | { |
| | | WarehouseId = item.WarehouseId, |
| | | PalletCode = item.PalletCode, |
| | | PalletType = item.PalletType, |
| | | SourceAddress = item.LocationCode, |
| | | TargetAddress = taskDto.TargetAddress, |
| | | Roadway = item.LocationDetails.RoadwayNo, |
| | | TaskType = TaskTypeEnum.Outbound.GetHashCode(), |
| | | TaskStatus = TaskStatusEnum.New.GetHashCode(), |
| | | Grade = 1, |
| | | TaskNum = 0, |
| | | CurrentAddress = item.LocationCode, |
| | | NextAddress = taskDto.TargetAddress, |
| | | Creater = "system", |
| | | }).ToList(); |
| | | |
| | | var result = await BaseDal.AddDataAsync(taskList) > 0; |
| | | var wmstaskDto = result ? _mapper.Map<WMSTaskDTO>(taskList) : null; |
| | | return WebResponseContent.Instance.OK(result ? "任务创建成功" : "任务创建失败", wmstaskDto ?? new object()); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"任务创建失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取可入库货位 |
| | | /// </summary> |
| | | public async Task<WebResponseContent> GetTasksLocationAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode); |
| | | if (task == null) return WebResponseContent.Instance.Error("未找到对应的任务"); |
| | | |
| | | var locationInfo = await _locationInfoService.GetLocationInfo(task.Roadway); |
| | | if (locationInfo == null) return WebResponseContent.Instance.Error("未找到对应的货位"); |
| | | |
| | | return await ExecuteWithinTransactionAsync(async () => |
| | | { |
| | | locationInfo.LocationStatus = LocationStatusEnum.FreeLock.GetHashCode(); |
| | | task.CurrentAddress = task.SourceAddress; |
| | | task.NextAddress = locationInfo.LocationCode; |
| | | task.TargetAddress = locationInfo.LocationCode; |
| | | task.TaskStatus = TaskInStatusEnum.Line_InFinish.GetHashCode(); |
| | | |
| | | var updateTaskResult = await BaseDal.UpdateDataAsync(task); |
| | | var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(locationInfo); |
| | | if (!updateTaskResult || !updateLocationResult) |
| | | { |
| | | return WebResponseContent.Instance.Error("任务更新失败"); |
| | | } |
| | | |
| | | return WebResponseContent.Instance.OK("任务更新成功", locationInfo.LocationCode); |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"获取任务失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 入库任务完成:添加库存,修改货位状态,删除任务数据,添加历史任务数据 |
| | | /// </summary> |
| | | public async Task<WebResponseContent> InboundFinishTaskAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode); |
| | | if (task == null) return WebResponseContent.Instance.Error("未找到对应的任务"); |
| | | |
| | | var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.TargetAddress); |
| | | if (location == null) return WebResponseContent.Instance.Error("未找到对应的货位"); |
| | | |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode); |
| | | if (stockInfo == null) return WebResponseContent.Instance.Error("未找到对应库存信息"); |
| | | |
| | | // 判断是不是极卷库任务 |
| | | //if (taskDto.WarehouseId == (int)WarehouseEnum.FJ1 || taskDto.WarehouseId == (int)WarehouseEnum.ZJ1) |
| | | //{ |
| | | // return WebResponseContent.Instance.OK(); |
| | | //} |
| | | |
| | | return await ExecuteWithinTransactionAsync(async () => |
| | | { |
| | | stockInfo.LocationCode = location.LocationCode; |
| | | stockInfo.LocationId = location.Id; |
| | | stockInfo.OutboundDate = task.Roadway switch |
| | | { |
| | | var r when r.Contains("GW") => DateTime.Now.AddHours(2), |
| | | var r when r.Contains("CW") => DateTime.Now.AddHours(1), |
| | | _ => DateTime.Now |
| | | }; |
| | | stockInfo.StockStatus = StockStatusEmun.入库完成.GetHashCode(); |
| | | |
| | | location.LocationStatus = LocationStatusEnum.InStock.GetHashCode(); |
| | | |
| | | var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location); |
| | | var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo); |
| | | if (!updateLocationResult || !updateStockResult) |
| | | return WebResponseContent.Instance.Error("任务完成失败"); |
| | | return await CompleteTaskAsync(task); |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"完成任务失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 出库任务完成 :修改库存,修改货位状态,删除任务数据,添加历史任务数据 |
| | | /// </summary> |
| | | public async Task<WebResponseContent> OutboundFinishTaskAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode); |
| | | if (task == null) return WebResponseContent.Instance.Error("未找到对应的任务"); |
| | | |
| | | var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.SourceAddress); |
| | | if (location == null) return WebResponseContent.Instance.Error("未找到对应的货位"); |
| | | |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode); |
| | | if (stockInfo == null) return WebResponseContent.Instance.Error("未找到对应库存信息"); |
| | | |
| | | // 判断是不是极卷库任务 |
| | | if (taskDto.WarehouseId == (int)WarehouseEnum.FJ1 || taskDto.WarehouseId == (int)WarehouseEnum.ZJ1) |
| | | { |
| | | OutTaskCompleteDto outTaskCompleteDto = new OutTaskCompleteDto() |
| | | { |
| | | TaskId = task.OrderNo, |
| | | DevId = task.TargetAddress, |
| | | ReqTime = DateTime.Now.ToString() |
| | | }; |
| | | return await OutTaskComplete(outTaskCompleteDto); |
| | | } |
| | | |
| | | return await ExecuteWithinTransactionAsync(async () => |
| | | { |
| | | stockInfo.LocationId = 0; |
| | | stockInfo.LocationCode = null; |
| | | stockInfo.OutboundDate = DateTime.Now; |
| | | |
| | | location.LocationStatus = LocationStatusEnum.Free.GetHashCode(); |
| | | |
| | | var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location); |
| | | var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo); |
| | | if (!updateLocationResult || !updateStockResult) |
| | | return WebResponseContent.Instance.Error("任务完成失败"); |
| | | return await CompleteTaskAsync(task); |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"完成任务失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 移库任务完成:修改库存位置与状态,修改源/目标货位状态,删除任务数据 |
| | | /// </summary> |
| | | public async Task<WebResponseContent> RelocationFinishTaskAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | var task = await BaseDal.QueryFirstAsync(s => |
| | | s.PalletCode == taskDto.PalletCode && |
| | | s.TaskType == TaskRelocationTypeEnum.Relocation.GetHashCode()); |
| | | if (task == null) return WebResponseContent.Instance.Error("未找到对应的移库任务"); |
| | | |
| | | var sourceLocation = await _locationInfoService.GetLocationInfo(task.Roadway, task.SourceAddress); |
| | | if (sourceLocation == null) return WebResponseContent.Instance.Error("未找到移库源货位"); |
| | | |
| | | var targetLocation = await _locationInfoService.GetLocationInfo(task.Roadway, task.TargetAddress); |
| | | if (targetLocation == null) return WebResponseContent.Instance.Error("未找到移库目标货位"); |
| | | |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode); |
| | | if (stockInfo == null) return WebResponseContent.Instance.Error("未找到对应库存信息"); |
| | | |
| | | return await ExecuteWithinTransactionAsync(async () => |
| | | { |
| | | stockInfo.LocationCode = targetLocation.LocationCode; |
| | | stockInfo.LocationId = targetLocation.Id; |
| | | stockInfo.StockStatus = StockStatusEmun.入库完成.GetHashCode(); |
| | | |
| | | sourceLocation.LocationStatus = LocationStatusEnum.Free.GetHashCode(); |
| | | targetLocation.LocationStatus = LocationStatusEnum.InStock.GetHashCode(); |
| | | |
| | | var updateSourceResult = await _locationInfoService.UpdateLocationInfoAsync(sourceLocation); |
| | | var updateTargetResult = await _locationInfoService.UpdateLocationInfoAsync(targetLocation); |
| | | var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo); |
| | | |
| | | if (!updateSourceResult || !updateTargetResult || !updateStockResult) |
| | | return WebResponseContent.Instance.Error("移库任务完成失败"); |
| | | |
| | | return await CompleteTaskAsync(task); |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"完成任务失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 创建空托盘入库任务 |
| | | /// </summary> |
| | | /// <param name="taskDto"></param> |
| | | /// <returns></returns> |
| | | public async Task<WebResponseContent> CreateTaskInboundTrayAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | WebResponseContent content = await GetTaskByPalletCodeAsync(taskDto.PalletCode); |
| | | if (content.Status) |
| | | { |
| | | return content; |
| | | } |
| | | |
| | | //var tasks = await BaseDal.QueryAsync(s => s.PalletCode == palletCode); |
| | | //if (tasks == null || !tasks.Any()) |
| | | // return WebResponseContent.Instance.Error("未找到对应的任务"); |
| | | //var taskDtos = _mapper.Map<List<WMSTaskDTO>>(tasks); |
| | | return WebResponseContent.Instance.OK("查询成功"/*, taskDtos*/); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"查询任务失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 创建空托盘出库任务 |
| | | /// </summary> |
| | | /// <param name="taskDto"></param> |
| | | /// <returns></returns> |
| | | public async Task<WebResponseContent> GetOutBoundTrayTaskAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | |
| | | var stockInfo = await _stockInfoService.Repository.QueryDataNavFirstAsync(x => x.LocationDetails.WarehouseId == taskDto.WarehouseId && x.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode() && x.StockStatus == StockStatusEmun.空托盘库存.GetHashCode()); |
| | | if (stockInfo == null) |
| | | return WebResponseContent.Instance.Error("未找到对应的库存信息"); |
| | | |
| | | var task = new Dt_Task() |
| | | { |
| | | WarehouseId = stockInfo.LocationDetails.WarehouseId, |
| | | PalletCode = stockInfo.PalletCode, |
| | | PalletType = stockInfo.PalletType, |
| | | SourceAddress = stockInfo.LocationCode, |
| | | CurrentAddress = stockInfo.LocationCode, |
| | | NextAddress = taskDto.TargetAddress, |
| | | TargetAddress = taskDto.TargetAddress, |
| | | Roadway = stockInfo.LocationDetails.RoadwayNo, |
| | | TaskType = TaskOutboundTypeEnum.OutEmpty.GetHashCode(), |
| | | TaskStatus = TaskStatusEnum.New.GetHashCode(), |
| | | Grade = 1, |
| | | TaskNum = await BaseDal.GetTaskNo(), |
| | | Creater = "system", |
| | | }; |
| | | var taskDtos = task.Adapt<WMSTaskDTO>(); |
| | | |
| | | var addResult = await BaseDal.AddDataAsync(task) > 0; |
| | | if (!addResult) |
| | | return WebResponseContent.Instance.Error("任务创建失败"); |
| | | return WebResponseContent.Instance.OK("任务创建成功", taskDtos); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"查询任务失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 修改任务状态(根据任务ID修改为指定状态) |
| | | /// </summary> |
| | | /// <param name="taskId"></param> |
| | | /// <param name="newStatus"></param> |
| | | /// <returns></returns> |
| | | public async Task<WebResponseContent> UpdateTaskByStatusAsync(int taskId, int newStatus) |
| | | { |
| | | try |
| | | { |
| | | var tasks = await BaseDal.QueryFirstAsync(s => s.TaskNum == taskId); |
| | | if (tasks == null) |
| | | return WebResponseContent.Instance.Error("未找到对应的任务"); |
| | | |
| | | tasks.TaskStatus = newStatus; |
| | | await BaseDal.UpdateDataAsync(tasks); |
| | | |
| | | return WebResponseContent.Instance.OK("修改成功", tasks); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"修改失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// 查找托盘是否有任务 |
| | | /// </summary> |
| | | /// <param name="palletCode"></param> |
| | | /// <returns></returns> |
| | | private async Task<WebResponseContent> GetTaskByPalletCodeAsync(string palletCode) |
| | | { |
| | | try |
| | |
| | | var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == palletCode); |
| | | if (task == null) |
| | | return WebResponseContent.Instance.Error("未找到对应的任务"); |
| | | |
| | | var taskDto = _mapper.Map<WMSTaskDTO>(task); |
| | | return WebResponseContent.Instance.OK("查询成功", taskDto); |
| | | } |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 完成任务后统一处理(删除任务数据) |
| | | /// 保存任务历史。 |
| | | /// </summary> |
| | | private async Task<WebResponseContent> CompleteTaskAsync(Dt_Task task) |
| | | private async Task<WebResponseContent> SaveTaskHistoryAsync(Dt_Task task, string operateType) |
| | | { |
| | | var deleteTaskResult = await BaseDal.DeleteDataAsync(task); |
| | | if (!deleteTaskResult) return WebResponseContent.Instance.Error("任务完成失败"); |
| | | |
| | | // 保留历史对象构建逻辑,后续可接入历史表落库 |
| | | var historyTask = _mapper.Map<Dt_Task_Hty>(task); |
| | | historyTask.InsertTime = DateTime.Now; |
| | | historyTask.OperateType = operateType; |
| | | |
| | | return WebResponseContent.Instance.OK("任务完成"); |
| | | var saved = await _task_HtyService.Repository.AddDataAsync(historyTask) > 0; |
| | | return saved |
| | | ? WebResponseContent.Instance.OK() |
| | | : WebResponseContent.Instance.Error("任务历史保存失败"); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 根据巷道确定目标地址(支持多出库口轮询) |
| | | /// 保存库存历史。 |
| | | /// </summary> |
| | | private async Task<WebResponseContent> SaveStockHistoryAsync(Dt_StockInfo stockInfo, string operateType) |
| | | { |
| | | var historyStock = _mapper.Map<Dt_StockInfo_Hty>(stockInfo); |
| | | historyStock.InsertTime = DateTime.Now; |
| | | historyStock.OperateType = operateType; |
| | | |
| | | var saved = await _stockInfo_HtyService.Repository.AddDataAsync(historyStock) > 0; |
| | | return saved |
| | | ? WebResponseContent.Instance.OK() |
| | | : WebResponseContent.Instance.Error("库存历史保存失败"); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 完成任务后统一处理。 |
| | | /// </summary> |
| | | private async Task<WebResponseContent> CompleteTaskAsync(Dt_Task task, string operateType = "") |
| | | { |
| | | var deleteTaskResult = await BaseDal.DeleteDataAsync(task); |
| | | if (!deleteTaskResult) |
| | | return WebResponseContent.Instance.Error("任务完成失败"); |
| | | |
| | | return await SaveTaskHistoryAsync(task, operateType); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 根据巷道确定目标地址,支持多出库口轮询。 |
| | | /// </summary> |
| | | private string DetermineTargetAddress(string roadway, Dictionary<string, List<string>> addressMap) |
| | | { |
| | | if (string.IsNullOrWhiteSpace(roadway)) |
| | | return "10080"; |
| | | return TaskAddressConstants.DEFAULT_ADDRESS; |
| | | |
| | | // 查找匹配的巷道前缀 |
| | | string matchedPrefix = null; |
| | | string? matchedPrefix = null; |
| | | foreach (var kvp in addressMap) |
| | | { |
| | | if (roadway.Contains(kvp.Key)) |
| | |
| | | } |
| | | |
| | | if (matchedPrefix == null) |
| | | return "10080"; |
| | | return TaskAddressConstants.DEFAULT_ADDRESS; |
| | | |
| | | var addresses = addressMap[matchedPrefix]; |
| | | if (addresses == null || addresses.Count == 0) |
| | | return "10080"; |
| | | if (!addressMap.TryGetValue(matchedPrefix, out var addresses) || addresses == null || addresses.Count == 0) |
| | | return TaskAddressConstants.DEFAULT_ADDRESS; |
| | | |
| | | // 单个地址,直接返回 |
| | | if (addresses.Count == 1) |
| | | return addresses[0]; |
| | | |
| | | // 多个地址,使用轮询服务 |
| | | return _roundRobinService.GetNextAddress(matchedPrefix, addresses); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 自动创建出库任务 - 查询到期库存并创建任务 |
| | | /// 根据库存 Remark 确定目标地址(GW_1→[11001,11010]轮询,GW_2→CWSC1,CW_1→22001)。 |
| | | /// </summary> |
| | | public async Task<WebResponseContent> CreateAutoOutboundTasksAsync() |
| | | private string DetermineTargetAddressByRemark(string remark, string roadway, Dictionary<string, List<string>> addressMap) |
| | | { |
| | | try |
| | | // 根据 Remark 确定目标地址 |
| | | if (!string.IsNullOrWhiteSpace(remark)) |
| | | { |
| | | // 1. 查询到期库存 |
| | | var expiredStocks = await _stockInfoService.Repository |
| | | .QueryDataNavAsync(s => s.OutboundDate <= DateTime.Now |
| | | && s.StockStatus == StockStatusEmun.入库完成.GetHashCode()); |
| | | |
| | | if (expiredStocks == null || !expiredStocks.Any()) |
| | | return remark switch |
| | | { |
| | | return WebResponseContent.Instance.OK("无到期库存需要处理"); |
| | | } |
| | | |
| | | // 过滤有位置且位置有库存的记录 |
| | | expiredStocks = expiredStocks |
| | | .Where(s => s.LocationDetails != null |
| | | && s.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode()) |
| | | .ToList(); |
| | | |
| | | if (!expiredStocks.Any()) |
| | | { |
| | | return WebResponseContent.Instance.OK("无符合条件的到期库存"); |
| | | } |
| | | |
| | | // 2. 检查已存在的任务 |
| | | var palletCodes = expiredStocks.Select(s => s.PalletCode).ToList(); |
| | | var existingTasks = await Repository.QueryDataAsync(t => |
| | | palletCodes.Contains(t.PalletCode) |
| | | && (t.TaskStatus == TaskStatusEnum.New.GetHashCode() |
| | | || t.TaskStatus == TaskStatusEnum.SC_Executing.GetHashCode() |
| | | || t.TaskStatus == TaskInStatusEnum.InNew.GetHashCode())); |
| | | |
| | | var processedPallets = existingTasks.Select(t => t.PalletCode).ToHashSet(); |
| | | |
| | | // 3. 筛选需要处理的库存 |
| | | var stocksToProcess = expiredStocks |
| | | .Where(s => !processedPallets.Contains(s.PalletCode)) |
| | | .ToList(); |
| | | |
| | | if (!stocksToProcess.Any()) |
| | | { |
| | | return WebResponseContent.Instance.OK("所有到期库存已存在任务"); |
| | | } |
| | | |
| | | // 4. 获取配置的目标地址映射 |
| | | var targetAddressMap = _configuration.GetSection("AutoOutboundTask:TargetAddresses") |
| | | .Get<Dictionary<string, List<string>>>() |
| | | ?? new Dictionary<string, List<string>>(); |
| | | |
| | | // 5. 批量创建任务 |
| | | var taskList = new List<Dt_Task>(); |
| | | foreach (var stock in stocksToProcess) |
| | | { |
| | | // 根据巷道确定目标地址 |
| | | var targetAddress = DetermineTargetAddress( |
| | | stock.LocationDetails?.RoadwayNo ?? "", |
| | | targetAddressMap); |
| | | |
| | | var task = new Dt_Task |
| | | { |
| | | WarehouseId = stock.WarehouseId, |
| | | PalletCode = stock.PalletCode, |
| | | PalletType = stock.PalletType, |
| | | SourceAddress = stock.LocationCode, |
| | | CurrentAddress = stock.LocationCode, |
| | | NextAddress = targetAddress, |
| | | TargetAddress = targetAddress, |
| | | Roadway = stock.LocationDetails?.RoadwayNo ?? "", |
| | | TaskType = TaskTypeEnum.Outbound.GetHashCode(), |
| | | TaskStatus = TaskStatusEnum.New.GetHashCode(), |
| | | Grade = 1, |
| | | TaskNum = await BaseDal.GetTaskNo(), |
| | | Creater = "system_auto" |
| | | }; |
| | | taskList.Add(task); |
| | | } |
| | | |
| | | var transactionResult = await ExecuteWithinTransactionAsync(async () => |
| | | { |
| | | var addResult = await BaseDal.AddDataAsync(taskList) > 0; |
| | | if (!addResult) |
| | | { |
| | | return WebResponseContent.Instance.Error($"批量创建任务失败,共 {taskList.Count} 个任务"); |
| | | } |
| | | |
| | | // 任务创建成功后,同步锁定库存和货位状态,避免重复分配 |
| | | var stocksToUpdate = stocksToProcess |
| | | .Select(s => |
| | | { |
| | | s.StockStatus = StockStatusEmun.出库锁定.GetHashCode(); |
| | | return s; |
| | | }) |
| | | .ToList(); |
| | | |
| | | var updateStockResult = await _stockInfoService.Repository.UpdateDataAsync(stocksToUpdate); |
| | | if (!updateStockResult) |
| | | { |
| | | return WebResponseContent.Instance.Error($"任务创建成功,但库存状态更新失败,共 {stocksToUpdate.Count} 条"); |
| | | } |
| | | |
| | | var locationsToUpdate = stocksToProcess |
| | | .Where(s => s.LocationDetails != null) |
| | | .GroupBy(s => s.LocationDetails.Id) |
| | | .Select(g => |
| | | { |
| | | var location = g.First().LocationDetails; |
| | | location.LocationStatus = LocationStatusEnum.InStockLock.GetHashCode(); |
| | | return location; |
| | | }) |
| | | .ToList(); |
| | | |
| | | if (locationsToUpdate.Any()) |
| | | { |
| | | var updateLocationResult = await _locationInfoService.Repository.UpdateDataAsync(locationsToUpdate); |
| | | if (!updateLocationResult) |
| | | { |
| | | return WebResponseContent.Instance.Error($"任务创建成功,但货位状态更新失败,共 {locationsToUpdate.Count} 条"); |
| | | } |
| | | } |
| | | |
| | | return WebResponseContent.Instance.OK(); |
| | | }); |
| | | if (!transactionResult.Status) |
| | | { |
| | | return transactionResult; |
| | | } |
| | | |
| | | // 6. 通知 WCS(异步,不影响主流程) |
| | | _ = Task.Run(() => |
| | | { |
| | | try |
| | | { |
| | | var wmstaskDtos = _mapper.Map<List<WMSTaskDTO>>(taskList); |
| | | _httpClientHelper.Post<WebResponseContent>( |
| | | "http://localhost:9292/api/Task/ReceiveTask", |
| | | wmstaskDtos.ToJson()); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | // WCS 通知失败不影响任务创建,记录日志即可 |
| | | Console.WriteLine($"WCS 批量通知失败,任务数量: {taskList.Count}, 错误: {ex.Message}"); |
| | | } |
| | | }); |
| | | |
| | | return WebResponseContent.Instance.OK($"成功创建 {taskList.Count} 个出库任务", taskList.Count); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"自动创建出库任务失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 创建机械手组盘任务 |
| | | /// </summary> |
| | | public async Task<WebResponseContent> CreateRobotGroupPalletTaskAsync(StockDTO stock) |
| | | { |
| | | return await CreateRobotPalletTaskAsync( |
| | | stock, |
| | | "组盘", |
| | | RobotTaskTypeEnum.GroupPallet, |
| | | s => string.IsNullOrWhiteSpace(s.TargetPalletNo) ? s.SourcePalletNo : s.TargetPalletNo, |
| | | requireStockWithoutLocation: false); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 创建机械手换盘任务 |
| | | /// </summary> |
| | | public async Task<WebResponseContent> CreateRobotChangePalletTaskAsync(StockDTO stock) |
| | | { |
| | | return await CreateRobotPalletTaskAsync( |
| | | stock, |
| | | "换盘", |
| | | RobotTaskTypeEnum.ChangePallet, |
| | | s => s.SourcePalletNo, |
| | | requireStockWithoutLocation: true, |
| | | stockPalletCodeSelector: s => s.SourcePalletNo); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 创建机械手拆盘任务 |
| | | /// </summary> |
| | | public async Task<WebResponseContent> CreateRobotSplitPalletTaskAsync(StockDTO stock) |
| | | { |
| | | return await CreateRobotPalletTaskAsync( |
| | | stock, |
| | | "拆盘", |
| | | RobotTaskTypeEnum.SplitPallet, |
| | | s => s.SourcePalletNo, |
| | | requireStockWithoutLocation: true, |
| | | stockPalletCodeSelector: s => s.SourcePalletNo); |
| | | } |
| | | |
| | | private async Task<WebResponseContent> CreateRobotPalletTaskAsync( |
| | | StockDTO stock, |
| | | string taskName, |
| | | RobotTaskTypeEnum taskType, |
| | | Func<StockDTO, string?> palletCodeSelector, |
| | | bool requireStockWithoutLocation, |
| | | Func<StockDTO, string?>? stockPalletCodeSelector = null) |
| | | { |
| | | try |
| | | { |
| | | if (stock == null) |
| | | return WebResponseContent.Instance.Error("任务参数不能为空"); |
| | | |
| | | var palletCode = palletCodeSelector(stock)?.Trim(); |
| | | if (string.IsNullOrWhiteSpace(palletCode)) |
| | | return WebResponseContent.Instance.Error("托盘号不能为空"); |
| | | |
| | | var sourceLineNo = stock.SourceLineNo?.Trim(); |
| | | var targetLineNo = stock.TargetLineNo?.Trim(); |
| | | if (string.IsNullOrWhiteSpace(sourceLineNo) || string.IsNullOrWhiteSpace(targetLineNo)) |
| | | return WebResponseContent.Instance.Error("来源线体编号和目标线体编号不能为空"); |
| | | |
| | | var existingTask = await BaseDal.QueryFirstAsync(t => |
| | | t.PalletCode == palletCode && |
| | | (t.TaskStatus == TaskRobotStatusEnum.RobotNew.GetHashCode() |
| | | || t.TaskStatus == TaskRobotStatusEnum.RobotExecuting.GetHashCode() |
| | | || t.TaskStatus == TaskRobotStatusEnum.RobotPickFinish.GetHashCode() |
| | | || t.TaskStatus == TaskRobotStatusEnum.RobotPutFinish.GetHashCode() |
| | | || t.TaskStatus == TaskRobotStatusEnum.RobotPending.GetHashCode())); |
| | | if (existingTask != null) |
| | | return WebResponseContent.Instance.Error($"托盘[{palletCode}]已存在未完成任务"); |
| | | |
| | | Dt_StockInfo? stockInfo = null; |
| | | if (requireStockWithoutLocation) |
| | | { |
| | | var stockPalletCode = (stockPalletCodeSelector ?? palletCodeSelector).Invoke(stock)?.Trim(); |
| | | if (string.IsNullOrWhiteSpace(stockPalletCode)) |
| | | return WebResponseContent.Instance.Error("源托盘号不能为空"); |
| | | |
| | | stockInfo = await _stockInfoService.GetStockInfoAsync(stockPalletCode); |
| | | if (stockInfo == null) |
| | | return WebResponseContent.Instance.Error($"托盘[{stockPalletCode}]库存不存在"); |
| | | |
| | | if (stockInfo.LocationId > 0 || !string.IsNullOrWhiteSpace(stockInfo.LocationCode)) |
| | | return WebResponseContent.Instance.Error($"托盘[{stockPalletCode}]库存已绑定货位,不能创建机械手{taskName}任务"); |
| | | } |
| | | |
| | | var task = new Dt_Task |
| | | { |
| | | TaskNum = await BaseDal.GetTaskNo(), |
| | | PalletCode = palletCode, |
| | | PalletType = stockInfo?.PalletType ?? 0, |
| | | Roadway = stock.Roadway, |
| | | TaskType = taskType.GetHashCode(), |
| | | TaskStatus = TaskRobotStatusEnum.RobotNew.GetHashCode(), |
| | | SourceAddress = sourceLineNo, |
| | | TargetAddress = targetLineNo, |
| | | CurrentAddress = sourceLineNo, |
| | | NextAddress = targetLineNo, |
| | | WarehouseId = stockInfo?.WarehouseId ?? 1, |
| | | Grade = 1, |
| | | Remark = $"机械手{taskName}", |
| | | Creater = "system" |
| | | StockRemarkConstants.GW1 => _roundRobinService.GetNextAddress(StockRemarkConstants.GW1, TaskAddressConstants.GW1_ADDRESSES.ToList()), |
| | | StockRemarkConstants.GW2 => TaskAddressConstants.GW2_ADDRESS, |
| | | StockRemarkConstants.CW1 => TaskAddressConstants.CW1_ADDRESS, |
| | | _ => DetermineTargetAddress(roadway, addressMap) |
| | | }; |
| | | |
| | | var result = await Repository.AddDataAsync(task) > 0; |
| | | if (!result) |
| | | return WebResponseContent.Instance.Error($"机械手{taskName}任务创建失败"); |
| | | |
| | | var wmstaskDto = _mapper.Map<WMSTaskDTO>(task); |
| | | return WebResponseContent.Instance.OK($"机械手{taskName}任务创建成功", wmstaskDto); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"机械手{taskName}任务创建失败: {ex.Message}"); |
| | | } |
| | | |
| | | // Remark 为空时,回退到巷道配置 |
| | | return DetermineTargetAddress(roadway, addressMap); |
| | | } |
| | | |
| | | #endregion WCS逻辑处理 |
| | | |
| | | #region 分容柜接口 |
| | | |
| | | /// <summary> |
| | | /// 堆垛机取放货完成后物流通知化成分容柜完成信号 |
| | | /// </summary> |
| | | public async Task<WebResponseContent> InOrOutCompletedAsync(GradingMachineInputDto input) |
| | | { |
| | | WebResponseContent content = new WebResponseContent(); |
| | | if (string.IsNullOrWhiteSpace(input.PalletCode) || string.IsNullOrWhiteSpace(input.LocationCode)) |
| | | { |
| | | return content.Error($"托盘号或者货位编号不能为空"); |
| | | } |
| | | |
| | | try |
| | | { |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(input.PalletCode, input.LocationCode); |
| | | |
| | | int locationStatus; |
| | | if (stockInfo == null) |
| | | { |
| | | var location = await _locationInfoService.GetLocationInfoAsync(input.LocationCode); |
| | | locationStatus = location?.LocationStatus ?? 0; |
| | | } |
| | | else |
| | | { |
| | | locationStatus = stockInfo.LocationDetails?.LocationStatus ?? 0; |
| | | } |
| | | |
| | | OutputDto outPutDto = new OutputDto() |
| | | { |
| | | LocationCode = input.LocationCode, |
| | | PalletCode = input.PalletCode, |
| | | IsNormalProcedure = 1, |
| | | LocationStatus = locationStatus |
| | | }; |
| | | return content.OK(data: outPutDto); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | content.Error(ex.Message); |
| | | } |
| | | |
| | | return content; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 化成分容柜定时向物流更新分容柜状态信息 |
| | | /// </summary> |
| | | /// <param name="input"></param> |
| | | /// <returns></returns> |
| | | public async Task<WebResponseContent> SendLocationStatusAsync(GradingMachineInputDto input) |
| | | { |
| | | WebResponseContent content = new WebResponseContent(); |
| | | if (string.IsNullOrWhiteSpace(input.LocationCode)) |
| | | { |
| | | return content.Error($"货位编号不能为空"); |
| | | } |
| | | |
| | | try |
| | | { |
| | | var result = await _locationInfoService.Db.Updateable<Dt_LocationInfo>() |
| | | .SetColumns(s => new Dt_LocationInfo |
| | | { |
| | | LocationStatus = input.LocationStatus |
| | | }).Where(s => s.LocationCode == input.LocationCode).ExecuteCommandAsync() > 0; |
| | | |
| | | if (result) |
| | | { |
| | | content.OK("更新成功"); |
| | | } |
| | | else |
| | | { |
| | | content.Error("更新失败"); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | content.Error(ex.Message); |
| | | } |
| | | return content; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 分容柜工作完成后调用此接口通知物流出库 |
| | | /// </summary> |
| | | /// <param name="input"></param> |
| | | /// <returns></returns> |
| | | public async Task<WebResponseContent> RequestOutboundAsync(GradingMachineInputDto input) |
| | | { |
| | | WebResponseContent content = new WebResponseContent(); |
| | | if (string.IsNullOrWhiteSpace(input.LocationCode) || string.IsNullOrWhiteSpace(input.PalletCode)) |
| | | { |
| | | return content.Error($"托盘号或者货位编号不能为空"); |
| | | } |
| | | try |
| | | { |
| | | var stock = await _stockInfoService.GetStockInfoAsync(input.PalletCode, input.LocationCode); |
| | | if (stock == null) |
| | | { |
| | | content.Error("未找到对应的托盘"); |
| | | } |
| | | else |
| | | { |
| | | var taskList = new Dt_Task |
| | | { |
| | | WarehouseId = stock.WarehouseId, |
| | | PalletCode = stock.PalletCode, |
| | | PalletType = stock.PalletType, |
| | | SourceAddress = stock.LocationCode, |
| | | CurrentAddress = stock.LocationCode, |
| | | NextAddress = "10080", |
| | | TargetAddress = "10081", |
| | | Roadway = stock.LocationDetails.RoadwayNo, |
| | | TaskType = TaskTypeEnum.Outbound.GetHashCode(), |
| | | TaskStatus = TaskStatusEnum.New.GetHashCode(), |
| | | Grade = 1, |
| | | TaskNum = await BaseDal.GetTaskNo(), |
| | | Creater = "system", |
| | | }; |
| | | |
| | | var result = await BaseDal.AddDataAsync(taskList) > 0; |
| | | var wmstaskDto = result ? _mapper.Map<WMSTaskDTO>(taskList) : null; |
| | | |
| | | var httpResponse = _httpClientHelper.Post<WebResponseContent>("http://logistics-service/api/logistics/notifyoutbound", JsonSerializer.Serialize(wmstaskDto)).Data; |
| | | if (result && httpResponse != null) |
| | | { |
| | | content.OK("出库请求成功"); |
| | | } |
| | | else |
| | | { |
| | | content.Error("出库请求失败"); |
| | | } |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | content.Error(ex.Message); |
| | | } |
| | | return content; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 入库完成分容调用获取托盘上每个通道电芯 |
| | | /// </summary> |
| | | /// <param name="input"></param> |
| | | /// <returns></returns> |
| | | public async Task<WebResponseContent> GetPalletCodeCellAsync(GradingMachineInputDto input) |
| | | { |
| | | WebResponseContent content = new WebResponseContent(); |
| | | if (string.IsNullOrWhiteSpace(input.PalletCode) || string.IsNullOrWhiteSpace(input.LocationCode)) |
| | | { |
| | | return content.Error($"托盘号或者货位编号不能为空"); |
| | | } |
| | | try |
| | | { |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(input.PalletCode, input.LocationCode); |
| | | if (stockInfo == null) |
| | | { |
| | | return content.Error("未找到对应的托盘"); |
| | | } |
| | | var outPutDtos = stockInfo.Details.Select(x => new OutputDto() |
| | | { |
| | | LocationCode = input.LocationCode, |
| | | PalletCode = input.PalletCode, |
| | | IsNormalProcedure = 1, |
| | | LocationStatus = stockInfo.LocationDetails.LocationStatus, |
| | | CellCode = x.SerialNumber, |
| | | Channel = x.InboundOrderRowNo.ToString() |
| | | }).ToList(); |
| | | return content.OK(data: outPutDtos); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return content.Error(ex.Message); |
| | | } |
| | | } |
| | | |
| | | #endregion 分容柜接口 |
| | | |
| | | |
| | | #region 极卷库任务模块 |
| | | public string AGV_OutTaskComplete = WIDESEA_Core.Helper.AppSettings.Configuration["AGV_OutTaskComplete"]; // 上报AGV出库输送线完成 |
| | | public string WCS_ReceiveTask = WIDESEA_Core.Helper.AppSettings.Configuration["WCS_ReceiveTask"]; // WMS输送线任务下发 |
| | | /// <summary> |
| | | /// 出入库申请 |
| | | /// </summary> |
| | | /// <param name="applyInOutDto">请求参数</param> |
| | | /// <returns></returns> |
| | | public async Task<AGVResponse> ApplyInOutAsync(ApplyInOutDto applyInOutDto) |
| | | { |
| | | AGVResponse aGVResponse = new AGVResponse(); |
| | | try |
| | | { |
| | | // 参数验证 |
| | | if (applyInOutDto == null) return aGVResponse.Error("请求参数不能为空"); |
| | | if (string.IsNullOrWhiteSpace(applyInOutDto.TrayNumber)) return aGVResponse.Error("托盘号不能为空"); |
| | | if (string.IsNullOrWhiteSpace(applyInOutDto.TaskId)) return aGVResponse.Error("任务号不能为空"); |
| | | if (string.IsNullOrWhiteSpace(applyInOutDto.MaterialType)) return aGVResponse.Error("物料类型不能为空"); |
| | | if (string.IsNullOrWhiteSpace(applyInOutDto.MaterialName)) return aGVResponse.Error("物料描述不能为空"); |
| | | if (string.IsNullOrWhiteSpace(applyInOutDto.ReqTime)) return aGVResponse.Error("请求时间不能为空"); |
| | | if (applyInOutDto.Floor != 1 && applyInOutDto.Floor != 2) return aGVResponse.Error($"楼层段错误,必须为1(模切段)或2(卷绕段),当前值:{applyInOutDto.Floor}"); |
| | | if (applyInOutDto.YinYang != 1 && applyInOutDto.YinYang != 2) return aGVResponse.Error($"阴阳极错误,必须为1(阴极)或2(阳极),当前值:{applyInOutDto.YinYang}"); |
| | | if (applyInOutDto.InOut != 1 && applyInOutDto.InOut != 2) return aGVResponse.Error($"出入库类型错误,必须为1(入库)或2(出库),当前值:{applyInOutDto.InOut}"); |
| | | if (applyInOutDto.InOut == 1) // 入库 |
| | | { |
| | | if (applyInOutDto.Width == null || applyInOutDto.Width <= 0) return aGVResponse.Error("入库时宽度不能为空且必须大于0"); |
| | | if (string.IsNullOrWhiteSpace(applyInOutDto.Group)) return aGVResponse.Error("入库时整托组别不能为空"); |
| | | } |
| | | // 检查任务是否已存在 |
| | | var existingTask = await BaseDal.QueryFirstAsync(x => x.PalletCode == applyInOutDto.TrayNumber); |
| | | if (existingTask != null) return aGVResponse.Error($"WMS已有当前任务,不可重复下发,任务号:{applyInOutDto.TaskId}"); |
| | | // 创建任务 |
| | | Dt_Task task = new Dt_Task |
| | | { |
| | | OrderNo = applyInOutDto.TaskId, // 使用AGV的任务号 |
| | | PalletCode = applyInOutDto.TrayNumber, |
| | | PalletType = applyInOutDto.Floor, |
| | | //WarehouseId = applyInOutDto.YinYang, |
| | | Grade = 1, |
| | | Creater = "AGV", |
| | | CreateDate = DateTime.Now, |
| | | Remark = $"物料类型:{applyInOutDto.MaterialType},物料描述:{applyInOutDto.MaterialName}" |
| | | }; |
| | | // 根据阴极/阳极设置巷道和站台 |
| | | if (applyInOutDto.YinYang == 1) // 阴极 |
| | | { |
| | | // 货物到达后分配货物位 |
| | | task.Roadway = WarehouseEnum.FJ1.ToString(); |
| | | task.WarehouseId = (int)WarehouseEnum.FJ1; |
| | | task.SourceAddress = applyInOutDto.InOut == 1 ? "D10010" : "D10020"; // 入库口/出库口 |
| | | task.NextAddress = "D10080"; // 阴极公共出库口 |
| | | task.TargetAddress = "阴极卷库"; |
| | | } |
| | | else if (applyInOutDto.YinYang == 2) // 阳极 |
| | | { |
| | | task.Roadway = WarehouseEnum.ZJ1.ToString(); |
| | | task.WarehouseId = (int)WarehouseEnum.ZJ1; |
| | | task.SourceAddress = applyInOutDto.InOut == 1 ? "D10100" : "D10090"; // 入库口/出库口 |
| | | task.NextAddress = "D10160"; // 阳极公共出库口 |
| | | task.TargetAddress = "正极卷库"; |
| | | } |
| | | // 根据出入库类型设置目标地址 |
| | | if (applyInOutDto.InOut == 1) // 入库 |
| | | { |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(applyInOutDto.TrayNumber); |
| | | if (stockInfo != null) return aGVResponse.Error($"当前托盘{applyInOutDto.TrayNumber}已经入库了"); |
| | | |
| | | task.TaskType = (int)TaskInboundTypeEnum.Inbound; |
| | | task.TaskStatus = (int)TaskInStatusEnum.InNew; |
| | | task.CurrentAddress = task.SourceAddress; // 当前在入库口 |
| | | // 保存任务 |
| | | var result = await BaseDal.AddDataAsync(task); |
| | | } |
| | | else // 出库 |
| | | { |
| | | // 判断是否移库 |
| | | task.TaskType = (int)TaskOutboundTypeEnum.Outbound; |
| | | task.TaskStatus = (int)TaskOutStatusEnum.OutNew; |
| | | task.CurrentAddress = task.SourceAddress; // 当前在入库口 |
| | | // 查找库存 |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(applyInOutDto.TrayNumber); |
| | | if (stockInfo == null) return aGVResponse.Error($"未找到托盘{applyInOutDto.TrayNumber}的库存信息"); |
| | | // 验证库存所属是否正确 |
| | | if (stockInfo.WarehouseId != applyInOutDto.YinYang) return aGVResponse.Error($"托盘{applyInOutDto.TrayNumber}不属于当前{(applyInOutDto.YinYang == 1 ? "阴极" : "阳极")}"); |
| | | if (stockInfo.StockStatus != (int)StockStatusEmun.入库完成) return aGVResponse.Error($"托盘{applyInOutDto.TrayNumber}正在移动中,请稍后!"); |
| | | task.SourceAddress = stockInfo.LocationCode; // 源地址为货位 |
| | | task.CurrentAddress = stockInfo.LocationCode; // 当前位置在货位 |
| | | task.TargetAddress = applyInOutDto.YinYang == 1 ? "D10020" : "D10090"; // 目标地址为出库口 |
| | | |
| | | // 保存任务 |
| | | var result = await BaseDal.AddDataAsync(task); |
| | | // 判断是否移库 |
| | | //var transferTask = await _locationInfoService.TransferCheckAsync(result); |
| | | } |
| | | |
| | | // 构建响应数据 |
| | | AGVDataDto aGVDataDto = new AGVDataDto |
| | | { |
| | | DevId = applyInOutDto.InOut == 1 ? task.SourceAddress : task.TargetAddress, |
| | | TrayNumber = task.PalletCode, |
| | | Group = applyInOutDto.Group, |
| | | Width = applyInOutDto.Width ?? 0, |
| | | LabelNumber = applyInOutDto.LabelNumber, |
| | | ProductNo = applyInOutDto.ProductNo, |
| | | ProductName = applyInOutDto.ProductName, |
| | | Quantity = applyInOutDto.Quantity, |
| | | UomCode = applyInOutDto.UomCode, |
| | | ProductType = applyInOutDto.ProductType, |
| | | Equipment = applyInOutDto.Equipment, |
| | | ProductionDate = applyInOutDto.ProductionDate, |
| | | LowerLimitTime = applyInOutDto.LowerLimitTime, |
| | | WarningTime = applyInOutDto.WarningTime, |
| | | OverdueTime = applyInOutDto.OverdueTime |
| | | }; |
| | | |
| | | return aGVResponse.OK(aGVDataDto); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return aGVResponse.Error($"WMS任务创建接口错误: {ex.Message}"); |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// 手动出库完成反馈给AGV |
| | | /// </summary> |
| | | /// <param name="outTaskCompleteDto">请求参数</param> |
| | | /// <returns></returns> |
| | | public async Task<WebResponseContent> OutTaskComplete(OutTaskCompleteDto outTaskCompleteDto) |
| | | { |
| | | WebResponseContent webResponse = new WebResponseContent(); |
| | | try |
| | | { |
| | | if (outTaskCompleteDto == null) return webResponse.Error("请求参数不能为空"); |
| | | if (string.IsNullOrWhiteSpace(outTaskCompleteDto.TaskId)) return webResponse.Error("任务号不能为空"); |
| | | if (string.IsNullOrWhiteSpace(outTaskCompleteDto.DevId)) return webResponse.Error("出库口编号不能为空"); |
| | | var task = await BaseDal.QueryFirstAsync(x => x.OrderNo == outTaskCompleteDto.TaskId); |
| | | if (task == null) return webResponse.Error("未找到任务信息"); |
| | | outTaskCompleteDto.ReqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); |
| | | var httpResponse = _httpClientHelper.Post<AGVResponse>(AGV_OutTaskComplete, JsonSerializer.Serialize(outTaskCompleteDto)).Data; |
| | | // 判断远程接口返回是否成功 |
| | | if (httpResponse != null && httpResponse.Data != null) |
| | | { |
| | | AGVResponse agvResponse = httpResponse; |
| | | // 根据code字段判断,code为true表示成功 |
| | | if (agvResponse.Code == true) |
| | | { |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(task.PalletCode); |
| | | if (stockInfo == null) return webResponse.Error($"未找到托盘{task.PalletCode}的库存信息"); |
| | | var locationInfo = await _locationInfoService.GetLocationInfoAsync(stockInfo.LocationCode); |
| | | if (locationInfo == null) return webResponse.Error($"未找到托盘{stockInfo.LocationCode}的货位信息"); |
| | | if (stockInfo.StockStatus != (int)StockStatusEmun.出库锁定 || locationInfo.LocationStatus != (int)LocationStatusEnum.InStockLock) |
| | | { |
| | | return webResponse.Error($"当前库存{stockInfo.StockStatus}或者货位{locationInfo.LocationStatus}状态信息错误"); |
| | | } |
| | | locationInfo.LocationStatus = (int)LocationStatusEnum.Free; |
| | | _unitOfWorkManage.BeginTran(); |
| | | BaseDal.DeleteData(task); |
| | | _locationInfoService.UpdateData(locationInfo); |
| | | _stockInfoService.DeleteData(stockInfo); |
| | | _unitOfWorkManage.CommitTran(); |
| | | return webResponse.OK(agvResponse.Msg); |
| | | } |
| | | else |
| | | { |
| | | // 失败:返回AGV返回的错误信息 |
| | | return webResponse.Error(string.IsNullOrWhiteSpace(agvResponse.Msg) |
| | | ? "AGV接口调用失败" |
| | | : agvResponse.Msg); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // HTTP请求本身失败 |
| | | return webResponse.Error(httpResponse?.Msg ?? "AGV接口调用异常"); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return webResponse.Error($"WMS任务完成接口错误:{ex.Message}"); |
| | | } |
| | | } |
| | | ///// <summary> |
| | | ///// 任务完成接口 |
| | | ///// </summary> |
| | | ///// <param name="wCSTask"></param> |
| | | ///// <returns></returns> |
| | | //public async Task<WebResponseContent> TaskCompleted(WCSTaskDTO wCSTask) |
| | | //{ |
| | | // WebResponseContent webResponse = new WebResponseContent(); |
| | | // try |
| | | // { |
| | | // Dt_Task task = await BaseDal.QueryFirstAsync(x => x.TaskId == wCSTask.TaskNum && x.PalletCode == wCSTask.PalletCode); |
| | | // if (task == null) return webResponse.Error("未找到任务信息"); |
| | | // // 出库完成反馈 |
| | | // OutTaskCompleteDto outTaskCompleteDto = new OutTaskCompleteDto() |
| | | // { |
| | | // TaskId = task.OrderNo, |
| | | // DevId = task.TargetAddress, |
| | | // ReqTime = DateTime.Now.ToString() |
| | | // }; |
| | | // return await OutTaskComplete(outTaskCompleteDto); |
| | | // } |
| | | // catch (Exception ex) |
| | | // { |
| | | // return webResponse.Error($"WMS任务完成接口错误:{ex.Message}"); |
| | | // } |
| | | //} |
| | | /// <summary> |
| | | /// 输送线申请进入 |
| | | /// </summary> |
| | | /// <param name="applyEnterDto">请求参数</param> |
| | | /// <returns></returns> |
| | | public async Task<AGVResponse> ApplyEnterAsync(ApplyEnterDto applyEnterDto) |
| | | { |
| | | AGVResponse aGVResponse = new AGVResponse(); |
| | | try |
| | | { |
| | | // 参数验证 |
| | | if (applyEnterDto == null) return aGVResponse.Error("请求参数不能为空"); |
| | | if (string.IsNullOrWhiteSpace(applyEnterDto.DevId)) return aGVResponse.Error("设备编号不能为空"); |
| | | if (string.IsNullOrWhiteSpace(applyEnterDto.TaskId)) return aGVResponse.Error("任务号不能为空"); |
| | | if (applyEnterDto.InOut != 1 && applyEnterDto.InOut != 2) return aGVResponse.Error($"出入库类型错误,必须为1(入库)或2(出库),当前值:{applyEnterDto.InOut}"); |
| | | |
| | | // 查询任务是否存在 |
| | | var task = await BaseDal.QueryFirstAsync(x => x.OrderNo == applyEnterDto.TaskId); |
| | | if (task == null) return aGVResponse.Error($"未找到任务信息,任务号:{applyEnterDto.TaskId}"); |
| | | if (applyEnterDto.InOut == 1 && task.TaskType == (int)TaskInboundTypeEnum.Inbound && task.TaskStatus == (int)TaskInStatusEnum.InNew) |
| | | { |
| | | aGVResponse.OK(); |
| | | } |
| | | else if(applyEnterDto.InOut == 2 && task.TaskType == (int)TaskOutboundTypeEnum.Outbound && task.TaskStatus == (int)TaskStatusEnum.Line_Finish) |
| | | { |
| | | aGVResponse.OK(); |
| | | } |
| | | return aGVResponse.Error($"输送线{applyEnterDto.DevId}当前繁忙,请稍后重试"); |
| | | |
| | | // Dt_Task.SourceAddress询问wcs站台1-阴极入口叫D10010和D10020;2-阳极D10090和D10100是否有货物 |
| | | //var httpResponse = _httpClientHelper.Post<WebResponseContent>("http://127.0.0.1:9999/api/Task/ApplyInOut", JsonSerializer.Serialize(task)).Data; |
| | | //if (httpResponse != null && httpResponse.Status == true) |
| | | //{ |
| | | // _unitOfWorkManage.BeginTran(); |
| | | // task.TaskStatus = (int)TaskStatusEnum.AGV_Executing; |
| | | // await BaseDal.UpdateDataAsync(task); |
| | | // _unitOfWorkManage.CommitTran(); |
| | | // return aGVResponse.OK(); |
| | | //} |
| | | //else |
| | | //{ |
| | | // return aGVResponse.Error($"站台{task.SourceAddress}已经载货"); |
| | | //} |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | //_unitOfWorkManage.RollbackTran(); |
| | | return aGVResponse.Error($"WMS输送线申请接口错误:{ex.Message}"); |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// 取放货完成 |
| | | /// </summary> |
| | | /// <param name="taskCompleteDto">请求参数</param> |
| | | /// <returns></returns> |
| | | public async Task<AGVResponse> TaskCompleteAsync(TaskCompleteDto taskCompleteDto) |
| | | { |
| | | AGVResponse aGVResponse = new AGVResponse(); |
| | | try |
| | | { |
| | | if (taskCompleteDto == null) return aGVResponse.Error("请求参数不能为空"); |
| | | if (string.IsNullOrWhiteSpace(taskCompleteDto.TaskId)) return aGVResponse.Error("任务号不能为空"); |
| | | if (string.IsNullOrWhiteSpace(taskCompleteDto.DevId)) return aGVResponse.Error("设备编号不能为空"); |
| | | if (taskCompleteDto.InOut != 1 && taskCompleteDto.InOut != 2) return aGVResponse.Error($"出入库类型错误,必须为1(入库)或2(出库),当前值:{taskCompleteDto.InOut}"); |
| | | var task = await BaseDal.QueryFirstAsync(x => x.OrderNo == taskCompleteDto.TaskId); |
| | | if (task == null) return aGVResponse.Error($"未找到任务信息,任务号:{taskCompleteDto.TaskId}"); |
| | | if (taskCompleteDto.InOut == 2) |
| | | { |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(task.PalletCode); |
| | | if (stockInfo == null) return aGVResponse.Error($"未找到托盘{task.PalletCode}的库存信息"); |
| | | var locationInfo = await _locationInfoService.GetLocationInfoAsync(stockInfo.LocationCode); |
| | | if (locationInfo == null) return aGVResponse.Error($"未找到托盘{stockInfo.LocationCode}的货位信息"); |
| | | if (stockInfo.StockStatus != (int)StockStatusEmun.出库锁定 || locationInfo.LocationStatus != (int)LocationStatusEnum.InStockLock) |
| | | { |
| | | return aGVResponse.Error($"当前库存{stockInfo.StockStatus}或者货位{locationInfo.LocationStatus}状态信息错误"); |
| | | } |
| | | locationInfo.LocationStatus = (int)LocationStatusEnum.Free; |
| | | task.TaskStatus = (int)TaskOutStatusEnum.OutFinish; |
| | | _unitOfWorkManage.BeginTran(); |
| | | _stockInfoService.DeleteData(stockInfo); |
| | | await _locationInfoService.UpdateLocationInfoAsync(locationInfo); |
| | | BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成); |
| | | _unitOfWorkManage.CommitTran(); |
| | | } |
| | | else |
| | | { |
| | | //查找可用货位 |
| | | var availableLocation = await _locationInfoService.GetLocationInfo(task.Roadway); |
| | | |
| | | if (availableLocation == null) return aGVResponse.Error("无可用的入库货位"); |
| | | |
| | | task.TargetAddress = availableLocation.LocationCode; |
| | | // 通知WCS入库 |
| | | var wcsTaskDto = _mapper.Map<WCSTaskDTO>(task); |
| | | var httpResponse = _httpClientHelper.Post<WebResponseContent>(WCS_ReceiveTask, JsonSerializer.Serialize(wcsTaskDto)); |
| | | if (httpResponse == null) return aGVResponse.Error("下发WCS失败"); |
| | | |
| | | task.TaskStatus = (int)TaskStatusEnum.Line_Executing; |
| | | task.Dispatchertime = DateTime.Now; |
| | | var locationInfo = await _locationInfoService.GetLocationInfoAsync(task.TargetAddress); |
| | | if (locationInfo == null) return aGVResponse.Error($"未找到托盘{task.TargetAddress}的货位信息"); |
| | | if (locationInfo.LocationStatus != (int)LocationStatusEnum.InStock) |
| | | { |
| | | return aGVResponse.Error($"当前货位{locationInfo.LocationStatus}状态信息错误"); |
| | | } |
| | | Dt_StockInfo dt_Stock = new Dt_StockInfo() |
| | | { |
| | | PalletCode = task.PalletCode, |
| | | StockStatus = (int)StockStatusEmun.入库确认, |
| | | LocationCode = locationInfo.LocationCode, |
| | | WarehouseId = task.WarehouseId, |
| | | Creater = "AGV", |
| | | CreateDate = DateTime.Now |
| | | }; |
| | | locationInfo.LocationStatus = (int)LocationStatusEnum.FreeLock; |
| | | _unitOfWorkManage.BeginTran(); |
| | | BaseDal.UpdateData(task); |
| | | _locationInfoService.UpdateData(locationInfo); |
| | | _stockInfoService.AddData(dt_Stock); |
| | | _unitOfWorkManage.CommitTran(); |
| | | } |
| | | return aGVResponse.OK(); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return aGVResponse.Error($"WMS取放货完成接口错误:{ex.Message}"); |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// 任务取消 |
| | | /// </summary> |
| | | /// <param name="taskCancelDto">请求参数</param> |
| | | /// <returns></returns> |
| | | public async Task<AGVResponse> TaskCancelAsync(TaskCancelDto taskCancelDto) |
| | | { |
| | | AGVResponse aGVResponse = new AGVResponse(); |
| | | try |
| | | { |
| | | if (taskCancelDto == null) return aGVResponse.Error("请求参数不能为空"); |
| | | if (string.IsNullOrWhiteSpace(taskCancelDto.TaskId)) return aGVResponse.Error("任务号不能为空"); |
| | | |
| | | var task = await BaseDal.QueryFirstAsync(x => x.OrderNo == taskCancelDto.TaskId); |
| | | // 没有任务强制取消 |
| | | if (task == null) return aGVResponse.OK(); |
| | | |
| | | if (task.TaskStatus == (int)TaskInStatusEnum.InNew) |
| | | { |
| | | task.TaskStatus = (int)TaskInStatusEnum.InCancel; |
| | | // 入库任务直接删除 |
| | | _unitOfWorkManage.BeginTran(); |
| | | BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成); |
| | | _unitOfWorkManage.CommitTran(); |
| | | return aGVResponse.OK(); |
| | | } |
| | | else if(task.TaskStatus == (int)TaskOutStatusEnum.OutNew) |
| | | { |
| | | // 出库任务恢复库存 |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(task.PalletCode); |
| | | if (stockInfo == null) return aGVResponse.Error($"未找到托盘{task.PalletCode}的库存信息"); |
| | | var locationInfo = await _locationInfoService.GetLocationInfoAsync(stockInfo.LocationCode); |
| | | if (locationInfo == null) return aGVResponse.Error($"未找到托盘{stockInfo.LocationCode}的货位信息"); |
| | | if (stockInfo.StockStatus != (int)StockStatusEmun.出库锁定 || locationInfo.LocationStatus != (int)LocationStatusEnum.InStockLock) |
| | | { |
| | | return aGVResponse.Error($"当前库存{stockInfo.StockStatus}或者货位{locationInfo.LocationStatus}状态信息错误"); |
| | | } |
| | | stockInfo.StockStatus = (int)StockStatusEmun.入库完成; |
| | | locationInfo.LocationStatus = (int)LocationStatusEnum.InStock; |
| | | task.TaskStatus = (int)TaskOutStatusEnum.OutCancel; |
| | | _unitOfWorkManage.BeginTran(); |
| | | _locationInfoService.UpdateData(locationInfo); |
| | | _stockInfoService.UpdateData(stockInfo); |
| | | BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成); |
| | | _unitOfWorkManage.CommitTran(); |
| | | return aGVResponse.OK(); |
| | | } |
| | | return aGVResponse.Error("任务已经在执行中,不可取消"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return aGVResponse.Error($"WMS任务取消接口错误:{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | #endregion 极卷库任务模块 |
| | | } |
| | | } |
| | | } |