| | |
| | | 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_DTO.MES; |
| | | using WIDESEA_DTO.Stock; |
| | | using WIDESEA_DTO.Task; |
| | | using Newtonsoft.Json; |
| | | using System.Diagnostics; |
| | | using WIDESEA_IBasicService; |
| | | using WIDESEA_IRecordService; |
| | | using WIDESEA_IStockService; |
| | | using WIDESEA_ITaskInfoService; |
| | | using WIDESEA_Model.Models; |
| | |
| | | private readonly ITask_HtyService _task_HtyService; |
| | | private readonly IStockInfo_HtyService _stockInfo_HtyService; |
| | | private readonly IUnitOfWorkManage _unitOfWorkManage; |
| | | private readonly IRecordService _recordService; |
| | | private readonly IMESDeviceConfigService _mesDeviceConfigService; |
| | | private readonly IMesLogService _mesLogService; |
| | | |
| | | public IRepository<Dt_Task> Repository => BaseDal; |
| | | |
| | |
| | | IMesService mesService, |
| | | ITask_HtyService task_HtyService, |
| | | IStockInfo_HtyService stockInfo_HtyService, |
| | | IUnitOfWorkManage unitOfWorkManage) : base(BaseDal) |
| | | IUnitOfWorkManage unitOfWorkManage, |
| | | IRecordService recordService, |
| | | IMESDeviceConfigService mesDeviceConfigService, |
| | | IMesLogService mesLogService) : base(BaseDal) |
| | | { |
| | | _mapper = mapper; |
| | | _stockInfoService = stockInfoService; |
| | |
| | | _task_HtyService = task_HtyService; |
| | | _stockInfo_HtyService = stockInfo_HtyService; |
| | | _unitOfWorkManage = unitOfWorkManage; |
| | | _recordService = recordService; |
| | | _mesDeviceConfigService = mesDeviceConfigService; |
| | | _mesLogService = mesLogService; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 查找托盘是否存在未完成任务。 |
| | | /// </summary> |
| | | 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); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"查询任务失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 保存任务历史。 |
| | | /// </summary> |
| | | private async Task<WebResponseContent> SaveTaskHistoryAsync(Dt_Task task, string operateType) |
| | | { |
| | | var historyTask = _mapper.Map<Dt_Task_Hty>(task); |
| | | historyTask.InsertTime = DateTime.Now; |
| | | historyTask.OperateType = operateType; |
| | | |
| | | 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 TaskAddressConstants.DEFAULT_ADDRESS; |
| | | |
| | | string? matchedPrefix = null; |
| | | foreach (var kvp in addressMap) |
| | | { |
| | | if (roadway.Contains(kvp.Key)) |
| | | { |
| | | matchedPrefix = kvp.Key; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (matchedPrefix == null) |
| | | return TaskAddressConstants.DEFAULT_ADDRESS; |
| | | |
| | | 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> |
| | | private string DetermineTargetAddressByRemark(string remark, string roadway, Dictionary<string, List<string>> addressMap) |
| | | { |
| | | // 根据 Remark 确定目标地址 |
| | | if (!string.IsNullOrWhiteSpace(remark)) |
| | | { |
| | | return remark switch |
| | | { |
| | | StockRemarkConstants.GW1 => _roundRobinService.GetNextAddress(StockRemarkConstants.GW1, TaskAddressConstants.GW1_ADDRESSES.ToList()), |
| | | StockRemarkConstants.GW2 => TaskAddressConstants.GW2_ADDRESS, |
| | | StockRemarkConstants.CW1 => TaskAddressConstants.CW1_ADDRESS, |
| | | _ => DetermineTargetAddress(roadway, addressMap) |
| | | }; |
| | | } |
| | | |
| | | // Remark 为空时,回退到巷道配置 |
| | | return DetermineTargetAddress(roadway, addressMap); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 异步执行MES上传 - 不阻塞主业务逻辑 |
| | | /// </summary> |
| | | /// <param name="palletCode">托盘号</param> |
| | | /// <param name="successStatus">成功时的状态枚举值(奇数)</param> |
| | | /// <param name="uploadFunc">具体的MES调用函数</param> |
| | | private async Task MesUploadAsync(string palletCode, MesUploadStatusEnum successStatus, Func<Task<HttpResponseResult<MesResponse>>> uploadFunc) |
| | | { |
| | | var stopwatch = Stopwatch.StartNew(); |
| | | string requestJson = ""; |
| | | string responseJson = ""; |
| | | bool isSuccess = false; |
| | | string errorMessage = ""; |
| | | |
| | | try |
| | | { |
| | | // 调用MES |
| | | var result = await uploadFunc(); |
| | | |
| | | stopwatch.Stop(); |
| | | isSuccess = result?.Data?.IsSuccess ?? false; |
| | | errorMessage = result?.Data?.Msg ?? result?.ErrorMessage ?? "未知错误"; |
| | | responseJson = JsonConvert.SerializeObject(result); |
| | | |
| | | // 根据成功/失败决定状态值:奇数=成功,偶数=失败 |
| | | var uploadStatus = isSuccess ? (int)successStatus : (int)successStatus + 1; |
| | | |
| | | // 更新库存表状态(不等待) |
| | | _ = _stockInfoService.UpdateMesUploadStatusAsync(palletCode, uploadStatus); |
| | | |
| | | // 记录MES日志 |
| | | await LogMesCallAsync(palletCode, successStatus.ToString(), requestJson, responseJson, |
| | | stopwatch.ElapsedMilliseconds, isSuccess ? "成功" : "失败", errorMessage); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | stopwatch.Stop(); |
| | | errorMessage = ex.Message; |
| | | |
| | | // 更新状态为失败(successStatus+1 即为失败状态) |
| | | var uploadStatus = (int)successStatus + 1; |
| | | _ = _stockInfoService.UpdateMesUploadStatusAsync(palletCode, uploadStatus); |
| | | |
| | | // 记录异常日志 |
| | | await LogMesCallAsync(palletCode, successStatus.ToString(), requestJson, responseJson, |
| | | stopwatch.ElapsedMilliseconds, "失败", errorMessage); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 记录MES接口调用日志 |
| | | /// </summary> |
| | | private async Task LogMesCallAsync(string palletCode, string apiType, string requestJson, |
| | | string responseJson, long durationMs, string status, string errorMessage) |
| | | { |
| | | try |
| | | { |
| | | var mesLog = new MesApiLogDto |
| | | { |
| | | PalletCode = palletCode, |
| | | ApiType = apiType, |
| | | RequestJson = requestJson, |
| | | ResponseJson = responseJson, |
| | | IsSuccess = status == "成功", |
| | | ErrorMessage = errorMessage, |
| | | ElapsedMs = (int)durationMs, |
| | | Creator = "System" |
| | | }; |
| | | await _mesLogService.LogAsync(mesLog); |
| | | } |
| | | catch |
| | | { |
| | | // 日志记录失败不影响主流程 |
| | | } |
| | | } |
| | | } |
| | | } |