| | |
| | | using System.ComponentModel; |
| | | using System.Reflection; |
| | | using Newtonsoft.Json; |
| | | using SqlSugar; |
| | | using WIDESEA_Common.Constants; |
| | | using WIDESEA_Common.StockEnum; |
| | | using WIDESEA_Common.TaskEnum; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_Core.Helper; |
| | | using WIDESEA_DTO.MES; |
| | | using WIDESEA_DTO.Task; |
| | | using WIDESEA_Model.Models; |
| | | |
| | |
| | | case "移库": |
| | | taskType = TaskRelocationTypeEnum.Relocation.GetHashCode(); |
| | | taskStatus = TaskRelocationStatusEnum.RelocationNew.GetHashCode(); |
| | | break; |
| | | |
| | | case "空箱入库": |
| | | taskType = TaskInboundTypeEnum.InEmpty.GetHashCode(); |
| | | taskStatus = TaskInStatusEnum.InNew.GetHashCode(); |
| | | break; |
| | | |
| | | default: |
| | |
| | | wmsTaskDtos.ToJson()); |
| | | |
| | | if (!wcsResult.IsSuccess || !wcsResult.Data.Status) |
| | | return WebResponseContent.Instance.Error($"任务已创建但发送给WCS失败: {wcsResult.Data?.Message}"); |
| | | return WebResponseContent.Instance.Error($"任务已创建但发送给WCS失败:{wcsResult.ErrorMessage}\r\n {wcsResult.Data?.Message}"); |
| | | |
| | | return WebResponseContent.Instance.OK($"手动创建任务成功,任务号: {taskNum}"); |
| | | }); |
| | |
| | | return descAttr?.Description ?? taskStatus.ToString(); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 组盘/拆盘操作并创建入库任务,完成后下发WCS |
| | | /// </summary> |
| | | /// <param name="dto">组盘/拆盘操作参数</param> |
| | | /// <returns>操作结果</returns> |
| | | public async Task<WebResponseContent> PalletOperationAndCreateTaskAsync(PalletOperationTaskDto dto) |
| | | { |
| | | try |
| | | { |
| | | // 1. 参数校验 |
| | | if (string.IsNullOrWhiteSpace(dto.PalletCode)) |
| | | return WebResponseContent.Instance.Error("托盘号不能为空"); |
| | | |
| | | if (string.IsNullOrWhiteSpace(dto.Action) |
| | | || (dto.Action != "组盘" && dto.Action != "拆盘")) |
| | | return WebResponseContent.Instance.Error("执行动作必须是'组盘'或'拆盘'"); |
| | | |
| | | if (string.IsNullOrWhiteSpace(dto.LineId)) |
| | | return WebResponseContent.Instance.Error("线体编号不能为空"); |
| | | |
| | | if (string.IsNullOrWhiteSpace(dto.WarehouseCode)) |
| | | return WebResponseContent.Instance.Error("仓库编号不能为空"); |
| | | |
| | | if (string.IsNullOrWhiteSpace(dto.RobotName)) |
| | | return WebResponseContent.Instance.Error("机械手名称不能为空"); |
| | | |
| | | // 组盘时电芯列表必传 |
| | | if (dto.Action == "组盘" && (dto.Cells == null || !dto.Cells.Any())) |
| | | return WebResponseContent.Instance.Error("组盘时电芯列表不能为空"); |
| | | |
| | | // 2. 根据仓库编号查询仓库信息,获取 WarehouseId、targetAddress 和 roadway |
| | | var warehouse = _sqlSugarClient.Queryable<Dt_Warehouse>() |
| | | .First(w => w.WarehouseCode == dto.WarehouseCode); |
| | | if (warehouse == null) |
| | | return WebResponseContent.Instance.Error($"未找到仓库编号为[{dto.WarehouseCode}]的仓库"); |
| | | |
| | | int warehouseId = warehouse.WarehouseId; |
| | | string targetAddress = warehouse.WarehouseCode; |
| | | string roadway = warehouse.WarehouseCode; |
| | | |
| | | // 3. 根据动作类型执行不同流程 |
| | | if (dto.Action == "组盘") |
| | | return await ExecuteGroupPalletAsync(dto, warehouseId, targetAddress, roadway); |
| | | |
| | | return await ExecuteSplitPalletAsync(dto, warehouseId, targetAddress, roadway); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"组盘/拆盘操作异常: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 执行组盘操作:添加库存 → 直接调用MES绑定 → 创建入库任务 → 下发WCS |
| | | /// </summary> |
| | | private async Task<WebResponseContent> ExecuteGroupPalletAsync( |
| | | PalletOperationTaskDto dto, int warehouseId, string targetAddress, string roadway) |
| | | { |
| | | return await _unitOfWorkManage.BeginTranAsync(async () => |
| | | { |
| | | // 1. 创建库存主记录(不绑定货位) |
| | | var stockInfo = new Dt_StockInfo |
| | | { |
| | | PalletCode = dto.PalletCode, |
| | | PalletType = 0, |
| | | WarehouseId = warehouseId, |
| | | LocationId = 0, |
| | | StockStatus = 0, |
| | | MesUploadStatus = 0 |
| | | }; |
| | | var stockId = await _stockInfoService.Repository.AddDataAsync(stockInfo); |
| | | if (stockId <= 0) |
| | | return WebResponseContent.Instance.Error("创建库存记录失败"); |
| | | |
| | | // 2. 批量创建库存明细 |
| | | var details = dto.Cells.Select((cell, i) => new Dt_StockInfoDetail |
| | | { |
| | | StockId = (int)stockId, |
| | | MaterielCode = cell.SfcCode, |
| | | MaterielName = "", |
| | | OrderNo = "", |
| | | ProductionDate = DateTime.Now.ToString("yyyy-MM-dd"), |
| | | EffectiveDate = "", |
| | | SerialNumber = cell.SfcCode, |
| | | StockQuantity = 1, |
| | | OutboundQuantity = 0, |
| | | Status = 0, |
| | | InboundOrderRowNo = Convert.ToInt32(cell.Channel), |
| | | Remark = cell.Channel |
| | | }).ToList(); |
| | | if (await _sqlSugarClient.Insertable(details).ExecuteCommandAsync() <= 0) |
| | | return WebResponseContent.Instance.Error("创建库存明细失败"); |
| | | |
| | | // 3. 调用MES绑定 |
| | | var (equipmentCode, resourceCode, token) = ResolveMesConfig(dto.RobotName); |
| | | var bindRequest = new BindContainerRequest |
| | | { |
| | | ContainerCode = dto.PalletCode, |
| | | EquipmentCode = equipmentCode, |
| | | ResourceCode = resourceCode, |
| | | LocalTime = DateTime.Now, |
| | | OperationType = StockConstants.MES_BIND_OPERATION_TYPE, |
| | | ContainerSfcList = dto.Cells.Select((cell, i) => new ContainerSfcItem |
| | | { |
| | | Sfc = cell.SfcCode, |
| | | Location = cell.Channel |
| | | }).ToList() |
| | | }; |
| | | var mesResult = string.IsNullOrWhiteSpace(token) |
| | | ? _mesService.BindContainer(bindRequest) |
| | | : _mesService.BindContainer(bindRequest, token); |
| | | if (!CheckMesResult(mesResult, out var mesError)) |
| | | return WebResponseContent.Instance.Error($"MES组盘绑定失败: {mesError}"); |
| | | |
| | | // 4. 创建入库任务并下发WCS |
| | | return await CreateTaskAndDispatchAsync(dto, warehouseId, targetAddress, roadway, |
| | | TaskInboundTypeEnum.Inbound, "组盘成功并创建入库任务"); |
| | | }); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 执行拆盘操作:查询库存 → 直接调用MES解绑 → 清除库存货位 → 创建空托盘入库任务 → 下发WCS |
| | | /// </summary> |
| | | private async Task<WebResponseContent> ExecuteSplitPalletAsync( |
| | | PalletOperationTaskDto dto, int warehouseId, string targetAddress, string roadway) |
| | | { |
| | | return await _unitOfWorkManage.BeginTranAsync(async () => |
| | | { |
| | | // 1. 导航查询库存及明细 |
| | | var stockInfo = await _stockInfoService.Repository |
| | | .QueryDataNavFirstAsync(s => s.PalletCode == dto.PalletCode); |
| | | if (stockInfo == null) |
| | | return WebResponseContent.Instance.Error($"未找到托盘[{dto.PalletCode}]的库存记录"); |
| | | |
| | | var sfcList = stockInfo.Details?.Select(d => d.SerialNumber).ToList() |
| | | ?? new List<string>(); |
| | | if (!sfcList.Any()) |
| | | return WebResponseContent.Instance.Error($"托盘[{dto.PalletCode}]下无电芯数据"); |
| | | |
| | | // 2. 调用MES解绑 |
| | | var (equipmentCode, resourceCode, token) = ResolveMesConfig(dto.RobotName); |
| | | var unbindRequest = new UnBindContainerRequest |
| | | { |
| | | EquipmentCode = equipmentCode, |
| | | ResourceCode = resourceCode, |
| | | LocalTime = DateTime.Now, |
| | | ContainCode = dto.PalletCode, |
| | | SfcList = sfcList |
| | | }; |
| | | var mesResult = string.IsNullOrWhiteSpace(token) |
| | | ? _mesService.UnBindContainer(unbindRequest) |
| | | : _mesService.UnBindContainer(unbindRequest, token); |
| | | if (!CheckMesResult(mesResult, out var mesError)) |
| | | return WebResponseContent.Instance.Error($"MES拆盘解绑失败: {mesError}"); |
| | | |
| | | // 3. 清除库存绑定的货位 |
| | | stockInfo.LocationId = 0; |
| | | stockInfo.LocationCode = null; |
| | | await _sqlSugarClient.Updateable(stockInfo) |
| | | .UpdateColumns(s => new { s.LocationId, s.LocationCode }) |
| | | .ExecuteCommandAsync(); |
| | | |
| | | // 4. 创建空托盘入库任务并下发WCS |
| | | return await CreateTaskAndDispatchAsync(dto, warehouseId, targetAddress, roadway, |
| | | TaskInboundTypeEnum.InEmpty, "拆盘成功并创建空托盘入库任务"); |
| | | }); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 解析MES设备配置,返回 (equipmentCode, resourceCode, token) |
| | | /// </summary> |
| | | private (string equipmentCode, string resourceCode, string token) ResolveMesConfig(string deviceName) |
| | | { |
| | | var config = _mesDeviceConfigService.GetByDeviceName(deviceName); |
| | | return ( |
| | | config?.EquipmentCode ?? StockConstants.MES_EQUIPMENT_CODE, |
| | | config?.ResourceCode ?? StockConstants.MES_RESOURCE_CODE, |
| | | config?.Token |
| | | ); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 检查MES返回结果,失败时通过 error 输出错误信息 |
| | | /// </summary> |
| | | private bool CheckMesResult(HttpResponseResult<MesResponse> result, out string error) |
| | | { |
| | | if (result?.Data != null && result.Data.IsSuccess) |
| | | { |
| | | error = null; |
| | | return true; |
| | | } |
| | | |
| | | error = result?.Data?.Msg ?? result?.ErrorMessage ?? "未知错误"; |
| | | return false; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 创建入库任务并下发WCS |
| | | /// </summary> |
| | | private async Task<WebResponseContent> CreateTaskAndDispatchAsync( |
| | | PalletOperationTaskDto dto, int warehouseId, string targetAddress, string roadway, |
| | | TaskInboundTypeEnum taskType, string successMessage) |
| | | { |
| | | int taskNum = await BaseDal.GetTaskNo(); |
| | | var task = new Dt_Task |
| | | { |
| | | TaskNum = taskNum, |
| | | PalletCode = dto.PalletCode, |
| | | SourceAddress = dto.LineId, |
| | | TargetAddress = targetAddress, |
| | | TaskType = taskType.GetHashCode(), |
| | | TaskStatus = TaskInStatusEnum.InNew.GetHashCode(), |
| | | Grade = dto.Grade, |
| | | Roadway = roadway, |
| | | WarehouseId = warehouseId, |
| | | CurrentAddress = dto.LineId, |
| | | NextAddress = targetAddress, |
| | | Creater = "manual", |
| | | CreateDate = DateTime.Now, |
| | | ModifyDate = DateTime.Now |
| | | }; |
| | | |
| | | if (await BaseDal.AddDataAsync(task) <= 0) |
| | | return WebResponseContent.Instance.Error("创建任务失败"); |
| | | |
| | | var wmsTaskDto = new WMSTaskDTO |
| | | { |
| | | TaskNum = task.TaskNum, |
| | | PalletCode = task.PalletCode, |
| | | SourceAddress = task.SourceAddress, |
| | | TargetAddress = task.TargetAddress, |
| | | TaskType = task.TaskType, |
| | | Roadway = task.Roadway, |
| | | TaskStatus = task.TaskStatus, |
| | | WarehouseId = task.WarehouseId |
| | | }; |
| | | |
| | | var wcsResult = _httpClientHelper.Post<WebResponseContent>( |
| | | "http://localhost:9292/api/Task/ReceiveManualTask", |
| | | new List<WMSTaskDTO> { wmsTaskDto }.ToJson()); |
| | | |
| | | if (!wcsResult.IsSuccess || !wcsResult.Data.Status) |
| | | return WebResponseContent.Instance.Error( |
| | | $"任务已创建但发送给WCS失败: {wcsResult.ErrorMessage}\r\n {wcsResult.Data?.Message}"); |
| | | |
| | | return WebResponseContent.Instance.OK($"{successMessage},任务号: {taskNum}"); |
| | | } |
| | | |
| | | #endregion 手动任务 |
| | | } |
| | | } |