| | |
| | | using Mapster; |
| | | using System.Text.Json; |
| | | using WIDESEA_Common.LocationEnum; |
| | | using WIDESEA_Common.StockEnum; |
| | | using WIDESEA_Common.TaskEnum; |
| | | using WIDESEA_Common.WareHouseEnum; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_Core.Enums; |
| | | using WIDESEA_Core.Helper; |
| | | using WIDESEA_DTO.Task; |
| | | using WIDESEA_Model.Models; |
| | | |
| | | namespace WIDESEA_TaskInfoService |
| | | { |
| | | public partial class TaskService |
| | | { |
| | | |
| | | public string AGV_OutTaskComplete = WIDESEA_Core.Helper.AppSettings.Configuration["AGV_OutTaskComplete"]; |
| | | public string WCS_ReceiveTask = WIDESEA_Core.Helper.AppSettings.Configuration["WCS_ReceiveTask"]; |
| | | |
| | | /// <summary> |
| | | /// 极卷库出入库申请 |
| | | /// </summary> |
| | | public async Task<AGVResponse> ApplyInOutAsync(ApplyInOutDto applyInOutDto) |
| | | { |
| | | AGVResponse response = new AGVResponse(); |
| | | |
| | | try |
| | | { |
| | | var validationMessage = ValidateApplyInOutRequest(applyInOutDto); |
| | | if (validationMessage != null) |
| | | return response.Error(validationMessage); |
| | | |
| | | var existingTask = await BaseDal.QueryFirstAsync(x => x.PalletCode == applyInOutDto.TrayNumber || x.OrderNo == applyInOutDto.TaskId); |
| | | if (existingTask != null) |
| | | return response.Error($"WMS已有当前任务,不可重复下发,任务号:{applyInOutDto.TaskId}"); |
| | | |
| | | var task = BuildAgvTask(applyInOutDto); |
| | | |
| | | if (applyInOutDto.InOut == 1) |
| | | { |
| | | var inboundResult = await CreateAgvInboundTaskAsync(task, applyInOutDto); |
| | | if (inboundResult != null) |
| | | return inboundResult; |
| | | } |
| | | else |
| | | { |
| | | var outboundResult = await CreateAgvOutboundTaskAsync(task, applyInOutDto); |
| | | if (outboundResult != null) |
| | | return outboundResult; |
| | | } |
| | | |
| | | return response.OK(BuildAgvDataDto(task, applyInOutDto)); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return response.Error($"WMS任务创建接口错误: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 手动出库完成反馈给AGV |
| | | /// </summary> |
| | | public async Task<WebResponseContent> OutTaskComplete(OutTaskCompleteDto outTaskCompleteDto) |
| | | { |
| | | WebResponseContent response = new WebResponseContent(); |
| | | |
| | | try |
| | | { |
| | | var validationMessage = ValidateOutTaskCompleteRequest(outTaskCompleteDto); |
| | | if (validationMessage != null) |
| | | return response.Error(validationMessage); |
| | | |
| | | var task = await BaseDal.QueryFirstAsync(x => x.OrderNo == outTaskCompleteDto.TaskId); |
| | | if (task == null) |
| | | return response.Error("未找到任务信息"); |
| | | |
| | | outTaskCompleteDto.ReqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); |
| | | var httpResponse = _httpClientHelper.Post<AGVResponse>(AGV_OutTaskComplete, outTaskCompleteDto.ToJson()).Data; |
| | | if (httpResponse == null || httpResponse.Data == null) |
| | | return response.Error(httpResponse?.Msg ?? "AGV接口调用异常"); |
| | | |
| | | if (!httpResponse.Code) |
| | | return response.Error(string.IsNullOrWhiteSpace(httpResponse.Msg) ? "AGV接口调用失败" : httpResponse.Msg); |
| | | |
| | | var syncResult = await CompleteLocalOutboundAfterAgvAckAsync(task); |
| | | return syncResult.Status ? response.OK(httpResponse.Msg) : syncResult; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return response.Error($"WMS任务完成接口错误:{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 输送线申请进入 |
| | | /// </summary> |
| | | public async Task<AGVResponse> ApplyEnterAsync(ApplyEnterDto applyEnterDto) |
| | | { |
| | | AGVResponse response = new AGVResponse(); |
| | | |
| | | try |
| | | { |
| | | var validationMessage = ValidateApplyEnterRequest(applyEnterDto); |
| | | if (validationMessage != null) |
| | | return response.Error(validationMessage); |
| | | |
| | | var task = await BaseDal.QueryFirstAsync(x => x.OrderNo == applyEnterDto.TaskId); |
| | | if (task == null) |
| | | return response.Error($"未找到任务信息,任务号:{applyEnterDto.TaskId}"); |
| | | |
| | | if (CanApplyEnter(task, applyEnterDto)) |
| | | return response.OK(); |
| | | |
| | | return response.Error($"输送线{applyEnterDto.DevId}当前繁忙,请稍后重试"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return response.Error($"WMS输送线申请接口错误:{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 取放货完成 |
| | | /// </summary> |
| | | public async Task<AGVResponse> TaskCompleteAsync(TaskCompleteDto taskCompleteDto) |
| | | { |
| | | AGVResponse response = new AGVResponse(); |
| | | |
| | | try |
| | | { |
| | | var validationMessage = ValidateTaskCompleteRequest(taskCompleteDto); |
| | | if (validationMessage != null) |
| | | return response.Error(validationMessage); |
| | | |
| | | var task = await BaseDal.QueryFirstAsync(x => x.OrderNo == taskCompleteDto.TaskId); |
| | | if (task == null) |
| | | return response.Error($"未找到任务信息,任务号:{taskCompleteDto.TaskId}"); |
| | | |
| | | return taskCompleteDto.InOut == 2 |
| | | ? await CompleteAgvOutboundTaskAsync(task) |
| | | : await CompleteAgvInboundTaskAsync(task); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return response.Error($"WMS取放货完成接口错误:{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 任务取消 |
| | | /// </summary> |
| | | public async Task<AGVResponse> TaskCancelAsync(TaskCancelDto taskCancelDto) |
| | | { |
| | | AGVResponse response = new AGVResponse(); |
| | | |
| | | try |
| | | { |
| | | var validationMessage = ValidateTaskCancelRequest(taskCancelDto); |
| | | if (validationMessage != null) |
| | | return response.Error(validationMessage); |
| | | |
| | | var task = await BaseDal.QueryFirstAsync(x => x.OrderNo == taskCancelDto.TaskId); |
| | | if (task == null) |
| | | return response.OK(); |
| | | |
| | | if (task.TaskStatus == (int)TaskInStatusEnum.InNew) |
| | | return CancelAgvInboundTask(task); |
| | | |
| | | if (task.TaskStatus == (int)TaskOutStatusEnum.OutNew) |
| | | return await CancelAgvOutboundTaskAsync(task); |
| | | |
| | | return response.Error("任务已经在执行中,不可取消"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return response.Error($"WMS任务取消接口错误:{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | #region 参数验证 |
| | | private static string? ValidateApplyInOutRequest(ApplyInOutDto dto) |
| | | { |
| | | if (dto == null) return "请求参数不能为空"; |
| | | if (string.IsNullOrWhiteSpace(dto.TrayNumber)) return "托盘号不能为空"; |
| | | if (string.IsNullOrWhiteSpace(dto.TaskId)) return "任务号不能为空"; |
| | | if (string.IsNullOrWhiteSpace(dto.MaterialType)) return "物料类型不能为空"; |
| | | if (string.IsNullOrWhiteSpace(dto.MaterialName)) return "物料描述不能为空"; |
| | | if (string.IsNullOrWhiteSpace(dto.ReqTime)) return "请求时间不能为空"; |
| | | if (dto.Floor != 1 && dto.Floor != 2) return $"楼层段错误,必须为1(模切段)或2(卷绕段),当前值:{dto.Floor}"; |
| | | if (dto.YinYang != 1 && dto.YinYang != 2) return $"阴阳极错误,必须为1(阴极)或2(阳极),当前值:{dto.YinYang}"; |
| | | if (dto.InOut != 1 && dto.InOut != 2) return $"出入库类型错误,必须为1(入库)或2(出库),当前值:{dto.InOut}"; |
| | | if (dto.InOut == 1 && (dto.Width == null || dto.Width <= 0)) return "入库时宽度不能为空且必须大于0"; |
| | | if (dto.InOut == 1 && string.IsNullOrWhiteSpace(dto.Group)) return "入库时整托组别不能为空"; |
| | | return null; |
| | | } |
| | | |
| | | private static string? ValidateOutTaskCompleteRequest(OutTaskCompleteDto dto) |
| | | { |
| | | if (dto == null) return "请求参数不能为空"; |
| | | if (string.IsNullOrWhiteSpace(dto.TaskId)) return "任务号不能为空"; |
| | | if (string.IsNullOrWhiteSpace(dto.DevId)) return "出库口编号不能为空"; |
| | | return null; |
| | | } |
| | | |
| | | private static string? ValidateApplyEnterRequest(ApplyEnterDto dto) |
| | | { |
| | | if (dto == null) return "请求参数不能为空"; |
| | | if (string.IsNullOrWhiteSpace(dto.DevId)) return "设备编号不能为空"; |
| | | if (string.IsNullOrWhiteSpace(dto.TaskId)) return "任务号不能为空"; |
| | | if (dto.InOut != 1 && dto.InOut != 2) return $"出入库类型错误,必须为1(入库)或2(出库),当前值:{dto.InOut}"; |
| | | return null; |
| | | } |
| | | |
| | | private static string? ValidateTaskCompleteRequest(TaskCompleteDto dto) |
| | | { |
| | | if (dto == null) return "请求参数不能为空"; |
| | | if (string.IsNullOrWhiteSpace(dto.TaskId)) return "任务号不能为空"; |
| | | if (string.IsNullOrWhiteSpace(dto.DevId)) return "设备编号不能为空"; |
| | | if (dto.InOut != 1 && dto.InOut != 2) return $"出入库类型错误,必须为1(入库)或2(出库),当前值:{dto.InOut}"; |
| | | return null; |
| | | } |
| | | |
| | | private static string? ValidateTaskCancelRequest(TaskCancelDto dto) |
| | | { |
| | | if (dto == null) return "请求参数不能为空"; |
| | | if (string.IsNullOrWhiteSpace(dto.TaskId)) return "任务号不能为空"; |
| | | return null; |
| | | } |
| | | #endregion 参数验证 |
| | | |
| | | #region 具体实现 |
| | | // 出入库共用创建任务 |
| | | private Dt_Task BuildAgvTask(ApplyInOutDto dto) |
| | | { |
| | | var task = new Dt_Task |
| | | { |
| | | OrderNo = dto.TaskId, |
| | | PalletCode = dto.TrayNumber, |
| | | PalletType = dto.Floor, |
| | | Grade = 1, |
| | | Creater = "AGV", |
| | | CreateDate = DateTime.Now, |
| | | Remark = $"物料类型:{dto.MaterialType},物料描述:{dto.MaterialName}" |
| | | }; |
| | | |
| | | if (dto.YinYang == 1) |
| | | { |
| | | task.Roadway = WarehouseEnum.FJ1.ToString(); |
| | | task.WarehouseId = (int)WarehouseEnum.FJ1; |
| | | task.SourceAddress = dto.InOut == 1 ? "D10010" : "D10020"; |
| | | task.NextAddress = "D10080"; |
| | | task.TargetAddress = "阴极卷库"; |
| | | } |
| | | else |
| | | { |
| | | task.Roadway = WarehouseEnum.ZJ1.ToString(); |
| | | task.WarehouseId = (int)WarehouseEnum.ZJ1; |
| | | task.SourceAddress = dto.InOut == 1 ? "D10100" : "D10090"; |
| | | task.NextAddress = "D10160"; |
| | | task.TargetAddress = "正极卷库"; |
| | | } |
| | | |
| | | return task; |
| | | } |
| | | |
| | | // 构建返回AGV出入库请求体 |
| | | private AGVDataDto BuildAgvDataDto(Dt_Task task, ApplyInOutDto dto) |
| | | { |
| | | return new AGVDataDto |
| | | { |
| | | DevId = dto.InOut == 1 ? task.SourceAddress : task.TargetAddress, |
| | | TrayNumber = task.PalletCode, |
| | | Group = dto.Group, |
| | | Width = dto.Width ?? 0, |
| | | LabelNumber = dto.LabelNumber, |
| | | ProductNo = dto.ProductNo, |
| | | ProductName = dto.ProductName, |
| | | Quantity = dto.Quantity, |
| | | UomCode = dto.UomCode, |
| | | ProductType = dto.ProductType, |
| | | Equipment = dto.Equipment, |
| | | ProductionDate = dto.ProductionDate, |
| | | LowerLimitTime = dto.LowerLimitTime, |
| | | WarningTime = dto.WarningTime, |
| | | OverdueTime = dto.OverdueTime |
| | | }; |
| | | } |
| | | |
| | | // 入库创建 |
| | | private async Task<AGVResponse?> CreateAgvInboundTaskAsync(Dt_Task task, ApplyInOutDto dto) |
| | | { |
| | | AGVResponse response = new AGVResponse(); |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(dto.TrayNumber); |
| | | if (stockInfo != null) |
| | | return response.Error($"当前托盘{dto.TrayNumber}已经入库了"); |
| | | |
| | | task.TaskType = (int)TaskInboundTypeEnum.Inbound; |
| | | task.TaskStatus = (int)TaskInStatusEnum.InNew; |
| | | task.CurrentAddress = task.SourceAddress; |
| | | |
| | | var addResult = await BaseDal.AddDataAsync(task) > 0; |
| | | return addResult ? null : response.Error("入库任务创建失败"); |
| | | } |
| | | |
| | | // 出库创建 |
| | | private async Task<AGVResponse?> CreateAgvOutboundTaskAsync(Dt_Task task, ApplyInOutDto dto) |
| | | { |
| | | AGVResponse response = new AGVResponse(); |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(dto.TrayNumber); |
| | | if (stockInfo == null) |
| | | return response.Error($"未找到托盘{dto.TrayNumber}的库存信息"); |
| | | |
| | | //if (stockInfo.WarehouseId != dto.YinYang) |
| | | // return response.Error($"托盘{dto.TrayNumber}不属于当前{(dto.YinYang == 1 ? "阴极" : "阳极")}"); |
| | | |
| | | if (stockInfo.StockStatus != (int)StockStatusEmun.入库完成) |
| | | return response.Error($"托盘{dto.TrayNumber}正在移动中,请稍后!"); |
| | | |
| | | var locationInfo = await _locationInfoService.GetLocationInfo(stockInfo.LocationCode); |
| | | if (locationInfo == null) |
| | | return response.Error($"未找到托盘{stockInfo.LocationCode}的货位信息"); |
| | | |
| | | if (locationInfo.LocationStatus != (int)LocationStatusEnum.InStock) |
| | | return response.Error($"当前货位{locationInfo.LocationStatus}状态信息错误"); |
| | | |
| | | task.TaskType = (int)TaskOutboundTypeEnum.Outbound; |
| | | task.TaskStatus = (int)TaskOutStatusEnum.OutNew; |
| | | task.SourceAddress = stockInfo.LocationCode; |
| | | task.CurrentAddress = stockInfo.LocationCode; |
| | | task.TargetAddress = dto.YinYang == 1 ? "D10020" : "D10090"; |
| | | |
| | | var wmsTaskDto = _mapper.Map<WMSTaskDTO>(task); |
| | | var taskList = new List<WMSTaskDTO> { wmsTaskDto }; |
| | | var requestBody = JsonSerializer.Serialize(taskList); |
| | | |
| | | var httpResponse = _httpClientHelper.Post<WebResponseContent>(WCS_ReceiveTask, requestBody); |
| | | if (httpResponse == null || httpResponse.Data == null || !httpResponse.Data.Status) |
| | | return response.Error(httpResponse?.Data?.Message ?? "下发WCS失败"); |
| | | |
| | | stockInfo.StockStatus = (int)StockStatusEmun.出库锁定; |
| | | locationInfo.LocationStatus = (int)LocationStatusEnum.InStockLock; |
| | | |
| | | _unitOfWorkManage.BeginTran(); |
| | | var addTaskResult = await BaseDal.AddDataAsync(task) > 0; |
| | | var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(locationInfo); |
| | | var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo); |
| | | if (!addTaskResult || !updateLocationResult || !updateStockResult) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return response.Error("出库任务创建失败"); |
| | | } |
| | | _unitOfWorkManage.CommitTran(); |
| | | |
| | | |
| | | return null; |
| | | } |
| | | |
| | | private async Task<WebResponseContent> CompleteLocalOutboundAfterAgvAckAsync(Dt_Task task) |
| | | { |
| | | task.TaskStatus = (int)TaskOutStatusEnum.Line_OutFinish; |
| | | |
| | | _unitOfWorkManage.BeginTran(); |
| | | var updateResult = BaseDal.UpdateData(task); |
| | | if (!updateResult) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return WebResponseContent.Instance.Error("AGV完成回传后,任务更新失败"); |
| | | } |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | return WebResponseContent.Instance.OK(); |
| | | } |
| | | |
| | | private bool CanApplyEnter(Dt_Task task, ApplyEnterDto dto) |
| | | { |
| | | if (dto.InOut == 1) |
| | | { |
| | | var hasExecutingOutTask = BaseDal.QueryFirst(x => x.TaskType == (int)TaskOutboundTypeEnum.Outbound |
| | | && x.TargetAddress == task.SourceAddress |
| | | && (x.TaskStatus == (int)TaskOutStatusEnum.SC_OutExecuting |
| | | || x.TaskStatus == (int)TaskOutStatusEnum.Line_OutExecuting)); |
| | | |
| | | // 如果没有正在执行的出库任务,则允许入库 |
| | | return hasExecutingOutTask == null; |
| | | } |
| | | else |
| | | { |
| | | return task.TaskType == (int)TaskOutboundTypeEnum.Outbound |
| | | && task.TaskStatus == (int)TaskStatusEnum.Line_Finish; |
| | | } |
| | | } |
| | | |
| | | // WCS入库完成 |
| | | private async Task<WebResponseContent> CompleteAgvInboundTaskAsync(CreateTaskDto taskDto) |
| | | { |
| | | WebResponseContent response = new WebResponseContent(); |
| | | var task = await BaseDal.QueryFirstAsync(x => x.PalletType == taskDto.PalletType); |
| | | if (task == null) |
| | | return response.Error($"没有当前托盘{taskDto.PalletType}入库任务"); |
| | | |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(task.PalletCode); |
| | | if (stockInfo == null) |
| | | return response.Error($"未找到托盘{task.PalletCode}的库存信息"); |
| | | |
| | | var locationInfo = await _locationInfoService.GetLocationInfoAsync(task.TargetAddress); |
| | | if (locationInfo == null) |
| | | return response.Error($"未找到货位{task.TargetAddress}的信息"); |
| | | |
| | | if (locationInfo.LocationStatus == (int)LocationStatusEnum.InStock) |
| | | return response.Error($"当前货位{locationInfo.LocationStatus}状态不是空闲状态,无法入库"); |
| | | |
| | | // 更新货位状态为占用 |
| | | locationInfo.LocationStatus = (int)LocationStatusEnum.InStock; |
| | | task.TaskStatus = (int)TaskInStatusEnum.InFinish; |
| | | stockInfo.StockStatus = (int)StockStatusEmun.入库完成; |
| | | _unitOfWorkManage.BeginTran(); |
| | | var addStockResult = _stockInfoService.UpdateData(stockInfo); |
| | | var updateLocationResult = _locationInfoService.UpdateData(locationInfo); |
| | | BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成); |
| | | |
| | | if (!addStockResult.Status || !updateLocationResult.Status) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return response.Error("入库完成后,添加库存或货位更新失败"); |
| | | } |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | return response.OK(); |
| | | } |
| | | |
| | | // AGV出库完成 |
| | | private async Task<AGVResponse> CompleteAgvOutboundTaskAsync(Dt_Task task) |
| | | { |
| | | AGVResponse response = new AGVResponse(); |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(task.PalletCode); |
| | | if (stockInfo == null) |
| | | return response.Error($"未找到托盘{task.PalletCode}的库存信息"); |
| | | |
| | | var locationInfo = await _locationInfoService.GetLocationInfoAsync(stockInfo.LocationCode); |
| | | if (locationInfo == null) |
| | | return response.Error($"未找到托盘{stockInfo.LocationCode}的货位信息"); |
| | | |
| | | if (stockInfo.StockStatus != (int)StockStatusEmun.出库锁定 || locationInfo.LocationStatus != (int)LocationStatusEnum.InStockLock) |
| | | return response.Error($"当前库存{stockInfo.StockStatus}或者货位{locationInfo.LocationStatus}状态信息错误"); |
| | | |
| | | locationInfo.LocationStatus = (int)LocationStatusEnum.Free; |
| | | task.TaskStatus = (int)TaskOutStatusEnum.OutFinish; |
| | | |
| | | _unitOfWorkManage.BeginTran(); |
| | | var deleteStockResult = _stockInfoService.DeleteData(stockInfo); |
| | | var updateLocationResult = _locationInfoService.UpdateData(locationInfo); |
| | | BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成); |
| | | if (!deleteStockResult.Status || !updateLocationResult.Status) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return response.Error("出库完成后,本地库存或货位更新失败"); |
| | | } |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | return response.OK(); |
| | | } |
| | | |
| | | // AGV已放货,准备输送线入库 |
| | | private async Task<AGVResponse> CompleteAgvInboundTaskAsync(Dt_Task task) |
| | | { |
| | | AGVResponse response = new AGVResponse(); |
| | | var availableLocation = await _locationInfoService.GetLocationInfo(task.Roadway); |
| | | if (availableLocation == null) |
| | | return response.Error("无可用的入库货位"); |
| | | |
| | | task.TargetAddress = availableLocation.LocationCode; |
| | | |
| | | var wmsTaskDto = _mapper.Map<WMSTaskDTO>(task); |
| | | var taskList = new List<WMSTaskDTO> { wmsTaskDto }; |
| | | var requestBody = JsonSerializer.Serialize(taskList); |
| | | |
| | | var httpResponse = _httpClientHelper.Post<WebResponseContent>(WCS_ReceiveTask, requestBody); |
| | | if (httpResponse == null || httpResponse.Data == null || !httpResponse.Data.Status) |
| | | return response.Error(httpResponse?.Data?.Message ?? "下发WCS失败"); |
| | | |
| | | task.TaskStatus = (int)TaskInStatusEnum.Line_InExecuting; |
| | | task.Dispatchertime = DateTime.Now; |
| | | |
| | | var locationInfo = await _locationInfoService.GetLocationInfoAsync(task.TargetAddress); |
| | | if (locationInfo == null) |
| | | return response.Error($"未找到托盘{task.TargetAddress}的货位信息"); |
| | | |
| | | if (locationInfo.LocationStatus != (int)LocationStatusEnum.Free) |
| | | return response.Error($"当前货位{locationInfo.LocationStatus}状态信息错误"); |
| | | |
| | | var existingStock = await _stockInfoService.GetStockInfoAsync(task.PalletCode); |
| | | if (existingStock != null) |
| | | return response.Error($"托盘{task.PalletCode}的库存信息已存在,请勿重复入库"); |
| | | |
| | | Dt_StockInfo stockInfo = 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(); |
| | | var updateTaskResult = BaseDal.UpdateData(task); |
| | | var updateLocationResult = _locationInfoService.UpdateData(locationInfo); |
| | | var addStockResult = _stockInfoService.AddData(stockInfo); |
| | | if (!updateTaskResult || !updateLocationResult.Status || !addStockResult.Status) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return response.Error("入库完成后,本地任务、库存或货位更新失败"); |
| | | } |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | return response.OK(); |
| | | } |
| | | |
| | | // AGV入库取消 |
| | | private AGVResponse CancelAgvInboundTask(Dt_Task task) |
| | | { |
| | | AGVResponse response = new AGVResponse(); |
| | | task.TaskStatus = (int)TaskInStatusEnum.InCancel; |
| | | |
| | | _unitOfWorkManage.BeginTran(); |
| | | BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成); |
| | | _unitOfWorkManage.CommitTran(); |
| | | return response.OK(); |
| | | } |
| | | |
| | | |
| | | // AGV出库取消 |
| | | private async Task<AGVResponse> CancelAgvOutboundTaskAsync(Dt_Task task) |
| | | { |
| | | AGVResponse response = new AGVResponse(); |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(task.PalletCode); |
| | | if (stockInfo == null) |
| | | return response.Error($"未找到托盘{task.PalletCode}的库存信息"); |
| | | |
| | | var locationInfo = await _locationInfoService.GetLocationInfoAsync(stockInfo.LocationCode); |
| | | if (locationInfo == null) |
| | | return response.Error($"未找到托盘{stockInfo.LocationCode}的货位信息"); |
| | | |
| | | if (stockInfo.StockStatus != (int)StockStatusEmun.出库锁定 || locationInfo.LocationStatus != (int)LocationStatusEnum.InStockLock) |
| | | return response.Error($"当前库存{stockInfo.StockStatus}或者货位{locationInfo.LocationStatus}状态信息错误"); |
| | | |
| | | stockInfo.StockStatus = (int)StockStatusEmun.入库完成; |
| | | locationInfo.LocationStatus = (int)LocationStatusEnum.InStock; |
| | | task.TaskStatus = (int)TaskOutStatusEnum.OutCancel; |
| | | |
| | | _unitOfWorkManage.BeginTran(); |
| | | var updateLocationResult = _locationInfoService.UpdateData(locationInfo); |
| | | var updateStockResult = _stockInfoService.UpdateData(stockInfo); |
| | | BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成); |
| | | if (!updateLocationResult.Status || !updateStockResult.Status) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return response.Error("出库任务取消失败"); |
| | | } |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | return response.OK(); |
| | | } |
| | | |
| | | #endregion 具体实现 |
| | | } |
| | | using Mapster;
|
| | | using System.Text.Json;
|
| | | using WIDESEA_Common.Constants;
|
| | | using WIDESEA_Common.LocationEnum;
|
| | | using WIDESEA_Common.StockEnum;
|
| | | using WIDESEA_Common.TaskEnum;
|
| | | using WIDESEA_Common.WareHouseEnum;
|
| | | using WIDESEA_Core;
|
| | | using WIDESEA_Core.Enums;
|
| | | using WIDESEA_Core.Helper;
|
| | | using WIDESEA_DTO.Task;
|
| | | using WIDESEA_IStockService;
|
| | | using WIDESEA_Model.Models;
|
| | |
|
| | | namespace WIDESEA_TaskInfoService
|
| | | {
|
| | | public partial class TaskService
|
| | | {
|
| | |
|
| | | public string AGV_OutTaskComplete = WIDESEA_Core.Helper.AppSettings.Configuration["AGV_OutTaskComplete"];
|
| | | public string WCS_ReceiveTask = WIDESEA_Core.Helper.AppSettings.Configuration["WCS_ReceiveTask"];
|
| | |
|
| | | /// <summary>
|
| | | /// 极卷库出入库申请
|
| | | /// </summary>
|
| | | public async Task<AGVResponse> ApplyInOutAsync(ApplyInOutDto applyInOutDto)
|
| | | {
|
| | | AGVResponse response = new AGVResponse();
|
| | |
|
| | | try
|
| | | {
|
| | | var validationMessage = ValidateApplyInOutRequest(applyInOutDto);
|
| | | if (validationMessage != null)
|
| | | return response.Error(validationMessage);
|
| | |
|
| | | var existingTask = await BaseDal.QueryFirstAsync(x => x.PalletCode == applyInOutDto.TrayNumber || x.OrderNo == applyInOutDto.TaskId);
|
| | | if (existingTask != null)
|
| | | return response.Error($"WMS已有当前任务,不可重复下发,任务号:{applyInOutDto.TaskId}");
|
| | |
|
| | | var task = BuildAgvTask(applyInOutDto);
|
| | |
|
| | | if (applyInOutDto.InOut == 1)
|
| | | {
|
| | | var inboundResult = await CreateAgvInboundTaskAsync(task, applyInOutDto);
|
| | | if (inboundResult != null)
|
| | | return inboundResult;
|
| | | }
|
| | | else
|
| | | {
|
| | | var outboundResult = await CreateAgvOutboundTaskAsync(task, applyInOutDto);
|
| | | if (outboundResult != null)
|
| | | return outboundResult;
|
| | | }
|
| | |
|
| | | return response.OK(BuildAgvDataDto(task, applyInOutDto));
|
| | | }
|
| | | catch (Exception ex)
|
| | | {
|
| | | _unitOfWorkManage.RollbackTran();
|
| | | return response.Error($"WMS任务创建接口错误: {ex.Message}");
|
| | | }
|
| | | }
|
| | |
|
| | | /// <summary>
|
| | | /// 手动出库完成反馈给AGV
|
| | | /// </summary>
|
| | | public async Task<WebResponseContent> OutTaskComplete(OutTaskCompleteDto outTaskCompleteDto)
|
| | | {
|
| | | WebResponseContent response = new WebResponseContent();
|
| | |
|
| | | try
|
| | | {
|
| | | var validationMessage = ValidateOutTaskCompleteRequest(outTaskCompleteDto);
|
| | | if (validationMessage != null)
|
| | | return response.Error(validationMessage);
|
| | |
|
| | | var task = await BaseDal.QueryFirstAsync(x => x.OrderNo == outTaskCompleteDto.TaskId);
|
| | | if (task == null)
|
| | | return response.Error("未找到任务信息");
|
| | |
|
| | | outTaskCompleteDto.ReqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
| | | var httpResponse = _httpClientHelper.Post<AGVResponse>(AGV_OutTaskComplete, outTaskCompleteDto.ToJson()).Data;
|
| | | if (httpResponse == null || httpResponse.Data == null)
|
| | | return response.Error(httpResponse?.Msg ?? "AGV接口调用异常");
|
| | |
|
| | | if (!httpResponse.Code)
|
| | | return response.Error(string.IsNullOrWhiteSpace(httpResponse.Msg) ? "AGV接口调用失败" : httpResponse.Msg);
|
| | |
|
| | | var syncResult = await CompleteLocalOutboundAfterAgvAckAsync(task);
|
| | | return syncResult.Status ? response.OK(httpResponse.Msg) : syncResult;
|
| | | }
|
| | | catch (Exception ex)
|
| | | {
|
| | | _unitOfWorkManage.RollbackTran();
|
| | | return response.Error($"WMS任务完成接口错误:{ex.Message}");
|
| | | }
|
| | | }
|
| | |
|
| | | /// <summary>
|
| | | /// 输送线申请进入
|
| | | /// </summary>
|
| | | public async Task<AGVResponse> ApplyEnterAsync(ApplyEnterDto applyEnterDto)
|
| | | {
|
| | | AGVResponse response = new AGVResponse();
|
| | |
|
| | | try
|
| | | {
|
| | | var validationMessage = ValidateApplyEnterRequest(applyEnterDto);
|
| | | if (validationMessage != null)
|
| | | return response.Error(validationMessage);
|
| | |
|
| | | var task = await BaseDal.QueryFirstAsync(x => x.OrderNo == applyEnterDto.TaskId);
|
| | | if (task == null)
|
| | | return response.Error($"未找到任务信息,任务号:{applyEnterDto.TaskId}");
|
| | |
|
| | | if (CanApplyEnter(task, applyEnterDto))
|
| | | return response.OK();
|
| | |
|
| | | return response.Error($"输送线{applyEnterDto.DevId}当前繁忙,请稍后重试");
|
| | | }
|
| | | catch (Exception ex)
|
| | | {
|
| | | return response.Error($"WMS输送线申请接口错误:{ex.Message}");
|
| | | }
|
| | | }
|
| | |
|
| | | /// <summary>
|
| | | /// 取放货完成
|
| | | /// </summary>
|
| | | public async Task<AGVResponse> TaskCompleteAsync(TaskCompleteDto taskCompleteDto)
|
| | | {
|
| | | AGVResponse response = new AGVResponse();
|
| | |
|
| | | try
|
| | | {
|
| | | var validationMessage = ValidateTaskCompleteRequest(taskCompleteDto);
|
| | | if (validationMessage != null)
|
| | | return response.Error(validationMessage);
|
| | |
|
| | | var task = await BaseDal.QueryFirstAsync(x => x.OrderNo == taskCompleteDto.TaskId);
|
| | | if (task == null)
|
| | | return response.Error($"未找到任务信息,任务号:{taskCompleteDto.TaskId}");
|
| | |
|
| | | return taskCompleteDto.InOut == 2
|
| | | ? await CompleteAgvOutboundTaskAsync(task)
|
| | | : await CompleteAgvInboundTaskAsync(task);
|
| | | }
|
| | | catch (Exception ex)
|
| | | {
|
| | | _unitOfWorkManage.RollbackTran();
|
| | | return response.Error($"WMS取放货完成接口错误:{ex.Message}");
|
| | | }
|
| | | }
|
| | |
|
| | | /// <summary>
|
| | | /// 任务取消
|
| | | /// </summary>
|
| | | public async Task<AGVResponse> TaskCancelAsync(TaskCancelDto taskCancelDto)
|
| | | {
|
| | | AGVResponse response = new AGVResponse();
|
| | |
|
| | | try
|
| | | {
|
| | | var validationMessage = ValidateTaskCancelRequest(taskCancelDto);
|
| | | if (validationMessage != null)
|
| | | return response.Error(validationMessage);
|
| | |
|
| | | var task = await BaseDal.QueryFirstAsync(x => x.OrderNo == taskCancelDto.TaskId);
|
| | | if (task == null)
|
| | | return response.OK();
|
| | |
|
| | | if (task.TaskStatus == (int)TaskInStatusEnum.InNew)
|
| | | return await CancelAgvInboundTask(task);
|
| | |
|
| | | if (task.TaskStatus == (int)TaskOutStatusEnum.OutNew)
|
| | | return await CancelAgvOutboundTaskAsync(task);
|
| | |
|
| | | return response.Error("任务已经在执行中,不可取消");
|
| | | }
|
| | | catch (Exception ex)
|
| | | {
|
| | | _unitOfWorkManage.RollbackTran();
|
| | | return response.Error($"WMS任务取消接口错误:{ex.Message}");
|
| | | }
|
| | | }
|
| | |
|
| | | #region 参数验证
|
| | | private static string? ValidateApplyInOutRequest(ApplyInOutDto dto)
|
| | | {
|
| | | if (dto == null) return "请求参数不能为空";
|
| | | if (string.IsNullOrWhiteSpace(dto.TrayNumber)) return "托盘号不能为空";
|
| | | if (string.IsNullOrWhiteSpace(dto.TaskId)) return "任务号不能为空";
|
| | | if (string.IsNullOrWhiteSpace(dto.MaterialType)) return "物料类型不能为空";
|
| | | if (string.IsNullOrWhiteSpace(dto.MaterialName)) return "物料描述不能为空";
|
| | | if (string.IsNullOrWhiteSpace(dto.ReqTime)) return "请求时间不能为空";
|
| | | if (dto.Floor != 1 && dto.Floor != 2) return $"楼层段错误,必须为1(模切段)或2(卷绕段),当前值:{dto.Floor}";
|
| | | if (dto.YinYang != 1 && dto.YinYang != 2) return $"阴阳极错误,必须为1(阴极)或2(阳极),当前值:{dto.YinYang}";
|
| | | if (dto.InOut != 1 && dto.InOut != 2) return $"出入库类型错误,必须为1(入库)或2(出库),当前值:{dto.InOut}";
|
| | | if (dto.InOut == 1 && (dto.Width == null || dto.Width <= 0)) return "入库时宽度不能为空且必须大于0";
|
| | | if (dto.InOut == 1 && string.IsNullOrWhiteSpace(dto.Group)) return "入库时整托组别不能为空";
|
| | | return null;
|
| | | }
|
| | |
|
| | | private static string? ValidateOutTaskCompleteRequest(OutTaskCompleteDto dto)
|
| | | {
|
| | | if (dto == null) return "请求参数不能为空";
|
| | | if (string.IsNullOrWhiteSpace(dto.TaskId)) return "任务号不能为空";
|
| | | if (string.IsNullOrWhiteSpace(dto.DevId)) return "出库口编号不能为空";
|
| | | return null;
|
| | | }
|
| | |
|
| | | private static string? ValidateApplyEnterRequest(ApplyEnterDto dto)
|
| | | {
|
| | | if (dto == null) return "请求参数不能为空";
|
| | | if (string.IsNullOrWhiteSpace(dto.DevId)) return "设备编号不能为空";
|
| | | if (string.IsNullOrWhiteSpace(dto.TaskId)) return "任务号不能为空";
|
| | | if (dto.InOut != 1 && dto.InOut != 2) return $"出入库类型错误,必须为1(入库)或2(出库),当前值:{dto.InOut}";
|
| | | return null;
|
| | | }
|
| | |
|
| | | private static string? ValidateTaskCompleteRequest(TaskCompleteDto dto)
|
| | | {
|
| | | if (dto == null) return "请求参数不能为空";
|
| | | if (string.IsNullOrWhiteSpace(dto.TaskId)) return "任务号不能为空";
|
| | | if (string.IsNullOrWhiteSpace(dto.DevId)) return "设备编号不能为空";
|
| | | if (dto.InOut != 1 && dto.InOut != 2) return $"出入库类型错误,必须为1(入库)或2(出库),当前值:{dto.InOut}";
|
| | | return null;
|
| | | }
|
| | |
|
| | | private static string? ValidateTaskCancelRequest(TaskCancelDto dto)
|
| | | {
|
| | | if (dto == null) return "请求参数不能为空";
|
| | | if (string.IsNullOrWhiteSpace(dto.TaskId)) return "任务号不能为空";
|
| | | return null;
|
| | | }
|
| | | #endregion 参数验证
|
| | |
|
| | | #region 具体实现
|
| | | // 出入库共用创建任务
|
| | | private Dt_Task BuildAgvTask(ApplyInOutDto dto)
|
| | | {
|
| | | var task = new Dt_Task
|
| | | {
|
| | | OrderNo = dto.TaskId,
|
| | | PalletCode = dto.TrayNumber,
|
| | | PalletType = dto.Floor,
|
| | | Grade = 1,
|
| | | Creater = "AGV",
|
| | | CreateDate = DateTime.Now,
|
| | | Remark = $"物料类型:{dto.MaterialType},物料描述:{dto.MaterialName}"
|
| | | };
|
| | |
|
| | | if (dto.YinYang == 1)
|
| | | {
|
| | | task.Roadway = WarehouseEnum.FJ1.ToString();
|
| | | task.WarehouseId = (int)WarehouseEnum.FJ1;
|
| | | task.SourceAddress = dto.InOut == 1 ? "D10010" : "D10020";
|
| | | task.NextAddress = "D10080";
|
| | | task.TargetAddress = "阴极卷库";
|
| | | }
|
| | | else
|
| | | {
|
| | | task.Roadway = WarehouseEnum.ZJ1.ToString();
|
| | | task.WarehouseId = (int)WarehouseEnum.ZJ1;
|
| | | task.SourceAddress = dto.InOut == 1 ? "D10100" : "D10090";
|
| | | task.NextAddress = "D10160";
|
| | | task.TargetAddress = "正极卷库";
|
| | | }
|
| | |
|
| | | return task;
|
| | | }
|
| | |
|
| | | // 构建返回AGV出入库请求体
|
| | | private AGVDataDto BuildAgvDataDto(Dt_Task task, ApplyInOutDto dto)
|
| | | {
|
| | | return new AGVDataDto
|
| | | {
|
| | | DevId = dto.InOut == 1 ? task.SourceAddress : task.TargetAddress,
|
| | | TrayNumber = task.PalletCode,
|
| | | Group = dto.Group,
|
| | | Width = dto.Width ?? 0,
|
| | | LabelNumber = dto.LabelNumber,
|
| | | ProductNo = dto.ProductNo,
|
| | | ProductName = dto.ProductName,
|
| | | Quantity = dto.Quantity,
|
| | | UomCode = dto.UomCode,
|
| | | ProductType = dto.ProductType,
|
| | | Equipment = dto.Equipment,
|
| | | ProductionDate = dto.ProductionDate,
|
| | | LowerLimitTime = dto.LowerLimitTime,
|
| | | WarningTime = dto.WarningTime,
|
| | | OverdueTime = dto.OverdueTime
|
| | | };
|
| | | }
|
| | |
|
| | | // 入库创建
|
| | | private async Task<AGVResponse?> CreateAgvInboundTaskAsync(Dt_Task task, ApplyInOutDto dto)
|
| | | {
|
| | | AGVResponse response = new AGVResponse();
|
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(dto.TrayNumber);
|
| | | if (stockInfo != null)
|
| | | return response.Error($"当前托盘{dto.TrayNumber}已经入库了");
|
| | |
|
| | | // 创建库存明细
|
| | | var details = new Dt_StockInfoDetail
|
| | | {
|
| | | MaterielCode = dto.ProductNo,
|
| | | MaterielName = dto.ProductName,
|
| | | StockQuantity = int.TryParse(dto.Quantity, out int quantity) ? quantity : 0,
|
| | | Unit = dto.UomCode,
|
| | | OrderNo = dto.ProductNo,
|
| | | ProductionDate =dto.ProductionDate,
|
| | | EffectiveDate = dto.LowerLimitTime,
|
| | | SerialNumber = dto.TrayNumber,
|
| | | Status = (int)StockStatusEmun.入库确认,
|
| | | InboundOrderRowNo = 1,
|
| | | Creater = StockConstants.AGV_USER,
|
| | | CreateDate = DateTime.Now,
|
| | | Remark = $"AGV入库任务创建,任务号:{dto.TaskId}"
|
| | | };
|
| | |
|
| | | // 创建库存主记录
|
| | | var stock = new Dt_StockInfo
|
| | | {
|
| | | PalletCode = dto.TrayNumber,
|
| | | PalletType = dto.Floor,
|
| | | WarehouseId = dto.YinYang == 1 ? (int)WarehouseEnum.FJ1 : (int)WarehouseEnum.ZJ1,
|
| | | StockStatus = (int)StockStatusEmun.入库确认,
|
| | | Creater = StockConstants.AGV_USER,
|
| | | CreateDate = DateTime.Now,
|
| | | Remark = $"AGV入库任务创建,任务号:{dto.TaskId}",
|
| | | Details = new List<Dt_StockInfoDetail> { details }
|
| | | };
|
| | |
|
| | | task.TaskType = (int)TaskInboundTypeEnum.Inbound;
|
| | | task.TaskStatus = (int)TaskInStatusEnum.InNew;
|
| | | task.CurrentAddress = task.SourceAddress;
|
| | |
|
| | | _unitOfWorkManage.BeginTran();
|
| | | try
|
| | | {
|
| | | // 先创建任务
|
| | | var taskResult = await BaseDal.AddDataAsync(task) > 0;
|
| | | if (!taskResult)
|
| | | {
|
| | | _unitOfWorkManage.RollbackTran();
|
| | | return response.Error("入库任务创建失败");
|
| | | }
|
| | | var result = _stockInfoService.Repository.AddData(stock, x => x.Details);
|
| | | if (result)
|
| | | {
|
| | | _unitOfWorkManage.CommitTran();
|
| | | return null;
|
| | | }
|
| | | else
|
| | | {
|
| | | _unitOfWorkManage.RollbackTran();
|
| | | return response.Error("库存信息创建失败");
|
| | | }
|
| | | }
|
| | | catch (Exception ex)
|
| | | {
|
| | | _unitOfWorkManage.RollbackTran();
|
| | | return response.Error($"入库任务创建异常:{ex.Message}");
|
| | | }
|
| | | }
|
| | |
|
| | | // 出库创建
|
| | | private async Task<AGVResponse?> CreateAgvOutboundTaskAsync(Dt_Task task, ApplyInOutDto dto)
|
| | | {
|
| | | AGVResponse response = new AGVResponse();
|
| | | |
| | | // 检查库存是否存在
|
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(dto.TrayNumber);
|
| | | if (stockInfo == null)
|
| | | return response.Error($"未找到托盘{dto.TrayNumber}的库存信息");
|
| | |
|
| | | // 检查库存是否有明细(即是否真的有库存)
|
| | | if (stockInfo.Details == null || !stockInfo.Details.Any())
|
| | | return response.Error($"托盘{dto.TrayNumber}没有库存明细,无法出库");
|
| | |
|
| | | // 检查库存总数量是否大于0
|
| | | var totalQuantity = stockInfo.Details.Sum(d => d.StockQuantity);
|
| | | if (totalQuantity <= 0)
|
| | | return response.Error($"托盘{dto.TrayNumber}库存数量不足,无法出库");
|
| | |
|
| | | // 根据dto参数进一步验证库存信息
|
| | | if (!string.IsNullOrEmpty(dto.ProductNo))
|
| | | {
|
| | | // 检查库存明细中是否包含指定的物料编码
|
| | | var hasMatchingMaterial = stockInfo.Details.Any(d => d.MaterielCode == dto.ProductNo);
|
| | | if (!hasMatchingMaterial)
|
| | | return response.Error($"托盘{dto.TrayNumber}中没有物料编码为{dto.ProductNo}的库存,无法出库");
|
| | | }
|
| | |
|
| | | // 检查库存状态是否允许出库
|
| | | if (stockInfo.StockStatus != (int)StockStatusEmun.入库完成)
|
| | | return response.Error($"托盘{dto.TrayNumber}正在移动中,请稍后!");
|
| | |
|
| | | // 检查货位信息
|
| | | var locationInfo = await _locationInfoService.GetLocationInfo(stockInfo.LocationCode);
|
| | | if (locationInfo == null)
|
| | | return response.Error($"未找到托盘{stockInfo.LocationCode}的货位信息");
|
| | |
|
| | | // 检查货位状态是否允许出库
|
| | | if (locationInfo.LocationStatus != (int)LocationStatusEnum.InStock)
|
| | | return response.Error($"当前货位{locationInfo.LocationStatus}状态信息错误");
|
| | |
|
| | | // 验证仓库ID是否匹配(根据dto的阴阳极参数)
|
| | | var expectedWarehouseId = dto.YinYang == 1 ? (int)WarehouseEnum.FJ1 : (int)WarehouseEnum.ZJ1;
|
| | | if (stockInfo.WarehouseId != expectedWarehouseId)
|
| | | return response.Error($"托盘{dto.TrayNumber}不在预期的仓库中,无法出库");
|
| | |
|
| | | task.TaskType = (int)TaskOutboundTypeEnum.Outbound;
|
| | | task.TaskStatus = (int)TaskOutStatusEnum.OutNew;
|
| | | task.SourceAddress = stockInfo.LocationCode;
|
| | | task.CurrentAddress = stockInfo.LocationCode;
|
| | | task.TargetAddress = dto.YinYang == 1 ? "D10020" : "D10090";
|
| | |
|
| | | var wmsTaskDto = _mapper.Map<WMSTaskDTO>(task);
|
| | | var taskList = new List<WMSTaskDTO> { wmsTaskDto };
|
| | | var requestBody = JsonSerializer.Serialize(taskList);
|
| | |
|
| | | var httpResponse = _httpClientHelper.Post<WebResponseContent>(WCS_ReceiveTask, requestBody);
|
| | | if (httpResponse == null || httpResponse.Data == null || !httpResponse.Data.Status)
|
| | | return response.Error(httpResponse?.Data?.Message ?? "下发WCS失败");
|
| | |
|
| | | stockInfo.StockStatus = (int)StockStatusEmun.出库锁定;
|
| | | locationInfo.LocationStatus = (int)LocationStatusEnum.InStockLock;
|
| | |
|
| | | _unitOfWorkManage.BeginTran();
|
| | | var addTaskResult = await BaseDal.AddDataAsync(task) > 0;
|
| | | var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(locationInfo);
|
| | | var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
|
| | | if (!addTaskResult || !updateLocationResult || !updateStockResult)
|
| | | {
|
| | | _unitOfWorkManage.RollbackTran();
|
| | | return response.Error("出库任务创建失败");
|
| | | }
|
| | | _unitOfWorkManage.CommitTran();
|
| | |
|
| | |
|
| | | return null;
|
| | | }
|
| | |
|
| | | private async Task<WebResponseContent> CompleteLocalOutboundAfterAgvAckAsync(Dt_Task task)
|
| | | {
|
| | | task.TaskStatus = (int)TaskOutStatusEnum.Line_OutFinish;
|
| | |
|
| | | _unitOfWorkManage.BeginTran();
|
| | | var updateResult = BaseDal.UpdateData(task);
|
| | | if (!updateResult)
|
| | | {
|
| | | _unitOfWorkManage.RollbackTran();
|
| | | return WebResponseContent.Instance.Error("AGV完成回传后,任务更新失败");
|
| | | }
|
| | |
|
| | | _unitOfWorkManage.CommitTran();
|
| | | return WebResponseContent.Instance.OK();
|
| | | }
|
| | |
|
| | | private bool CanApplyEnter(Dt_Task task, ApplyEnterDto dto)
|
| | | {
|
| | | if (dto.InOut == 1)
|
| | | {
|
| | | var hasExecutingOutTask = BaseDal.QueryFirst(x => x.TaskType == (int)TaskOutboundTypeEnum.Outbound
|
| | | && x.TargetAddress == task.SourceAddress
|
| | | && (x.TaskStatus == (int)TaskOutStatusEnum.SC_OutExecuting
|
| | | || x.TaskStatus == (int)TaskOutStatusEnum.Line_OutExecuting));
|
| | |
|
| | | // 如果没有正在执行的出库任务,则允许入库
|
| | | return hasExecutingOutTask == null;
|
| | | }
|
| | | else
|
| | | {
|
| | | return task.TaskType == (int)TaskOutboundTypeEnum.Outbound
|
| | | && task.TaskStatus == (int)TaskStatusEnum.Line_Finish;
|
| | | }
|
| | | }
|
| | |
|
| | | // WCS入库完成
|
| | | private async Task<WebResponseContent> CompleteAgvInboundTaskAsync(CreateTaskDto taskDto)
|
| | | {
|
| | | WebResponseContent response = new WebResponseContent();
|
| | | var task = await BaseDal.QueryFirstAsync(x => x.PalletType == taskDto.PalletType);
|
| | | if (task == null)
|
| | | return response.Error($"没有当前托盘{taskDto.PalletType}入库任务");
|
| | |
|
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(task.PalletCode);
|
| | | if (stockInfo == null)
|
| | | return response.Error($"未找到托盘{task.PalletCode}的库存信息");
|
| | |
|
| | | var locationInfo = await _locationInfoService.GetLocationInfoAsync(task.TargetAddress);
|
| | | if (locationInfo == null)
|
| | | return response.Error($"未找到货位{task.TargetAddress}的信息");
|
| | |
|
| | | if (locationInfo.LocationStatus == (int)LocationStatusEnum.InStock)
|
| | | return response.Error($"当前货位{locationInfo.LocationStatus}状态不是空闲状态,无法入库");
|
| | |
|
| | | // 更新货位状态为占用
|
| | | locationInfo.LocationStatus = (int)LocationStatusEnum.InStock;
|
| | | task.TaskStatus = (int)TaskInStatusEnum.InFinish;
|
| | | stockInfo.StockStatus = (int)StockStatusEmun.入库完成;
|
| | | _unitOfWorkManage.BeginTran();
|
| | | var addStockResult = _stockInfoService.UpdateData(stockInfo);
|
| | | var updateLocationResult = _locationInfoService.UpdateData(locationInfo);
|
| | | BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
|
| | |
|
| | | if (!addStockResult.Status || !updateLocationResult.Status)
|
| | | {
|
| | | _unitOfWorkManage.RollbackTran();
|
| | | return response.Error("入库完成后,添加库存或货位更新失败");
|
| | | }
|
| | |
|
| | | _unitOfWorkManage.CommitTran();
|
| | | return response.OK();
|
| | | }
|
| | |
|
| | | // AGV出库完成
|
| | | private async Task<AGVResponse> CompleteAgvOutboundTaskAsync(Dt_Task task)
|
| | | {
|
| | | AGVResponse response = new AGVResponse();
|
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(task.PalletCode);
|
| | | if (stockInfo == null)
|
| | | return response.Error($"未找到托盘{task.PalletCode}的库存信息");
|
| | |
|
| | | var locationInfo = await _locationInfoService.GetLocationInfoAsync(stockInfo.LocationCode);
|
| | | if (locationInfo == null)
|
| | | return response.Error($"未找到托盘{stockInfo.LocationCode}的货位信息");
|
| | |
|
| | | if (stockInfo.StockStatus != (int)StockStatusEmun.出库锁定 || locationInfo.LocationStatus != (int)LocationStatusEnum.InStockLock)
|
| | | return response.Error($"当前库存{stockInfo.StockStatus}或者货位{locationInfo.LocationStatus}状态信息错误");
|
| | |
|
| | | locationInfo.LocationStatus = (int)LocationStatusEnum.Free;
|
| | | task.TaskStatus = (int)TaskOutStatusEnum.OutFinish;
|
| | |
|
| | | _unitOfWorkManage.BeginTran();
|
| | | //var deleteStockResult = _stockInfoService.DeleteData(stockInfo)
|
| | | var deleteStockResult = await _stockInfoService.DeleteStockWithDetailsAsync(stockInfo.Id);
|
| | | var updateLocationResult = _locationInfoService.UpdateData(locationInfo);
|
| | | BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
|
| | | if (!deleteStockResult.Status || !updateLocationResult.Status)
|
| | | {
|
| | | _unitOfWorkManage.RollbackTran();
|
| | | return response.Error("出库完成后,本地库存或货位更新失败");
|
| | | }
|
| | |
|
| | | _unitOfWorkManage.CommitTran();
|
| | | return response.OK();
|
| | | }
|
| | |
|
| | | // AGV已放货,准备输送线入库
|
| | | private async Task<AGVResponse> CompleteAgvInboundTaskAsync(Dt_Task task)
|
| | | {
|
| | | AGVResponse response = new AGVResponse();
|
| | | var availableLocation = await _locationInfoService.GetLocationInfo(task.Roadway);
|
| | | if (availableLocation == null)
|
| | | return response.Error("无可用的入库货位");
|
| | |
|
| | | task.TargetAddress = availableLocation.LocationCode;
|
| | |
|
| | | var wmsTaskDto = _mapper.Map<WMSTaskDTO>(task);
|
| | | var taskList = new List<WMSTaskDTO> { wmsTaskDto };
|
| | | var requestBody = JsonSerializer.Serialize(taskList);
|
| | |
|
| | | var httpResponse = _httpClientHelper.Post<WebResponseContent>(WCS_ReceiveTask, requestBody);
|
| | | if (httpResponse == null || httpResponse.Data == null || !httpResponse.Data.Status)
|
| | | return response.Error(httpResponse?.Data?.Message ?? "下发WCS失败");
|
| | |
|
| | | task.TaskStatus = (int)TaskInStatusEnum.Line_InExecuting;
|
| | | task.Dispatchertime = DateTime.Now;
|
| | |
|
| | | var locationInfo = await _locationInfoService.GetLocationInfoAsync(task.TargetAddress);
|
| | | if (locationInfo == null)
|
| | | return response.Error($"未找到托盘{task.TargetAddress}的货位信息");
|
| | |
|
| | | if (locationInfo.LocationStatus != (int)LocationStatusEnum.Free)
|
| | | return response.Error($"当前货位{locationInfo.LocationStatus}状态信息错误");
|
| | |
|
| | | var existingStock = await _stockInfoService.GetStockInfoAsync(task.PalletCode);
|
| | | if (existingStock != null)
|
| | | return response.Error($"托盘{task.PalletCode}的库存信息已存在,请勿重复入库");
|
| | |
|
| | | //Dt_StockInfo stockInfo = 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();
|
| | | var updateTaskResult = BaseDal.UpdateData(task);
|
| | | var updateLocationResult = _locationInfoService.UpdateData(locationInfo);
|
| | | //var addStockResult = _stockInfoService.AddData(stockInfo);
|
| | | if (!updateTaskResult || !updateLocationResult.Status /*|| !addStockResult.Status*/)
|
| | | {
|
| | | _unitOfWorkManage.RollbackTran();
|
| | | return response.Error("入库完成后,本地任务、库存或货位更新失败");
|
| | | }
|
| | |
|
| | | _unitOfWorkManage.CommitTran();
|
| | | return response.OK();
|
| | | }
|
| | |
|
| | | // AGV入库取消
|
| | | private async Task<AGVResponse> CancelAgvInboundTask(Dt_Task task)
|
| | | {
|
| | | AGVResponse response = new AGVResponse();
|
| | | task.TaskStatus = (int)TaskInStatusEnum.InCancel;
|
| | | |
| | | _unitOfWorkManage.BeginTran();
|
| | | try
|
| | | {
|
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(task.PalletCode);
|
| | | if (stockInfo != null)
|
| | | {
|
| | | var deleteResult = await _stockInfoService.DeleteStockWithDetailsAsync(stockInfo.Id);
|
| | | if (!deleteResult.Status)
|
| | | {
|
| | | _unitOfWorkManage.RollbackTran();
|
| | | return response.Error($"删除库存失败: {deleteResult.Message}");
|
| | | }
|
| | | }
|
| | |
|
| | | BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
|
| | |
|
| | | _unitOfWorkManage.CommitTran();
|
| | | return response.OK();
|
| | | }
|
| | | catch (Exception ex)
|
| | | {
|
| | | _unitOfWorkManage.RollbackTran();
|
| | | return response.Error($"取消入库任务时发生异常: {ex.Message}");
|
| | | }
|
| | | }
|
| | |
|
| | |
|
| | | // AGV出库取消
|
| | | private async Task<AGVResponse> CancelAgvOutboundTaskAsync(Dt_Task task)
|
| | | {
|
| | | AGVResponse response = new AGVResponse();
|
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(task.PalletCode);
|
| | | if (stockInfo == null)
|
| | | return response.Error($"未找到托盘{task.PalletCode}的库存信息");
|
| | |
|
| | | var locationInfo = await _locationInfoService.GetLocationInfoAsync(stockInfo.LocationCode);
|
| | | if (locationInfo == null)
|
| | | return response.Error($"未找到托盘{stockInfo.LocationCode}的货位信息");
|
| | |
|
| | | if (stockInfo.StockStatus != (int)StockStatusEmun.出库锁定 || locationInfo.LocationStatus != (int)LocationStatusEnum.InStockLock)
|
| | | return response.Error($"当前库存{stockInfo.StockStatus}或者货位{locationInfo.LocationStatus}状态信息错误");
|
| | |
|
| | | stockInfo.StockStatus = (int)StockStatusEmun.入库完成;
|
| | | locationInfo.LocationStatus = (int)LocationStatusEnum.InStock;
|
| | | task.TaskStatus = (int)TaskOutStatusEnum.OutCancel;
|
| | |
|
| | | _unitOfWorkManage.BeginTran();
|
| | | var updateLocationResult = _locationInfoService.UpdateData(locationInfo);
|
| | | var updateStockResult = _stockInfoService.UpdateData(stockInfo);
|
| | | BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
|
| | | if (!updateLocationResult.Status || !updateStockResult.Status)
|
| | | {
|
| | | _unitOfWorkManage.RollbackTran();
|
| | | return response.Error("出库任务取消失败");
|
| | | }
|
| | |
|
| | | _unitOfWorkManage.CommitTran();
|
| | | return response.OK();
|
| | | }
|
| | |
|
| | | #endregion 具体实现
|
| | | }
|
| | | } |