#region << 版 本 注 释 >> /*---------------------------------------------------------------- * 命名空间:WIDESEAWCS_TaskInfoService * 创建者:胡童庆 * 创建时间:2024/8/2 16:13:36 * 版本:V1.0.0 * 描述: * * ---------------------------------------------------------------- * 修改人: * 修改时间: * 版本:V1.0.1 * 修改说明: * *----------------------------------------------------------------*/ #endregion << 版 本 注 释 >> using AutoMapper; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using NPOI.SS.Formula.Functions; using SqlSugar; using System.Diagnostics.CodeAnalysis; using System.DirectoryServices.Protocols; using System.Net.Http.Headers; using System.Security.Policy; using WIDESEA_DTO.Agv; using WIDESEA_External.Model; using WIDESEAWCS_Common; using WIDESEAWCS_Common.APIEnum; using WIDESEAWCS_Common.TaskEnum; using WIDESEAWCS_Core; using WIDESEAWCS_Core.BaseRepository; using WIDESEAWCS_Core.BaseServices; using WIDESEAWCS_Core.Enums; using WIDESEAWCS_Core.Helper; using WIDESEAWCS_Core.LogHelper; using WIDESEAWCS_DTO; using WIDESEAWCS_DTO.Agv; using WIDESEAWCS_DTO.TaskInfo; using WIDESEAWCS_IBasicInfoRepository; using WIDESEAWCS_IBasicInfoService; using WIDESEAWCS_ITaskInfoRepository; using WIDESEAWCS_ITaskInfoService; using WIDESEAWCS_Model.Models; using WIDESEAWCS_QuartzJob.Models; using WIDESEAWCS_QuartzJob.Repository; using WIDESEAWCS_QuartzJob.Service; using ICacheService = WIDESEAWCS_Core.Caches.ICacheService; namespace WIDESEAWCS_TaskInfoService { public partial class TaskService : ServiceBase, ITaskService { private readonly IMapper _mapper; private readonly IUnitOfWorkManage _unitOfWorkManage; private readonly ICacheService _cacheService; private readonly IRouterService _routerService; private readonly ITaskExecuteDetailService _taskExecuteDetailService; private readonly ITaskExecuteDetailRepository _taskExecuteDetailRepository; private readonly IStationMangerRepository _stationMangerRepository; private readonly IRouterRepository _routerRepository; private readonly IApiInfoRepository _apiInfoRepository; private readonly ILocationInfoRepository _locationInfoRepository; private readonly ILocationInfoService _locationInfoService; private Dictionary _taskOrderBy = new() { {nameof(Dt_Task.Grade),OrderByType.Desc }, {nameof(Dt_Task.TaskNum),OrderByType.Asc}, {nameof(Dt_Task.CreateDate),OrderByType.Asc}, }; public Dictionary TaskOrderBy { get { return _taskOrderBy; } set { _taskOrderBy = value; } } public List TaskInboundTypes => typeof(TaskTypeEnum).GetEnumIndexList().Where(x => x >= 500 && x < 900).ToList(); public List TaskOutboundTypes => typeof(TaskTypeEnum).GetEnumIndexList().Where(x => x >= 100 && x < 500).ToList(); public List TaskRelocationTypes => typeof(TaskTypeEnum).GetEnumIndexList().Where(x => x >= 900 && x < 1000).ToList(); public TaskService(ITaskRepository BaseDal, IMapper mapper, ICacheService cacheService, IRouterService routerService, ITaskExecuteDetailService taskExecuteDetailService, ITaskExecuteDetailRepository taskExecuteDetailRepository, IStationMangerRepository stationMangerRepository, IRouterRepository routerRepository, IApiInfoRepository apiInfoRepository,ILocationInfoRepository locationInfoRepository,IUnitOfWorkManage unitOfWorkManage, ILocationInfoService locationInfoService) : base(BaseDal) { _mapper = mapper; _cacheService = cacheService; _routerService = routerService; _taskExecuteDetailService = taskExecuteDetailService; _taskExecuteDetailRepository = taskExecuteDetailRepository; _stationMangerRepository = stationMangerRepository; _routerRepository = routerRepository; _apiInfoRepository = apiInfoRepository; _locationInfoRepository = locationInfoRepository; _unitOfWorkManage = unitOfWorkManage; _locationInfoService = locationInfoService; } static object lock_taskReceive = new object(); /// /// 接收WMS任务信息 /// /// WMS任务对象集合 /// 返回处理结果 public WebResponseContent ReceiveWMSTask([NotNull] WMSTaskDTO taskDTO) { WebResponseContent content = new WebResponseContent(); try { lock (lock_taskReceive) { List tasks = new List(); List locationInfos = _locationInfoRepository.GetCanOut(taskDTO.Tasks.Select(x=>x.ContainerCode).ToList()); TasksItem? tasksItem = taskDTO.Tasks.FirstOrDefault(x => !locationInfos.Select(t => t.PalletCode).Contains(x.ContainerCode)); if (tasksItem != null) throw new Exception($"任务{tasksItem.TaskCode}料箱号{tasksItem.ContainerCode}不存在"); Dt_LocationInfo? noOutLocation = locationInfos.FirstOrDefault(x=>x.LocationStatus != LocationStatusEnum.InStock.ObjToInt() || x.EnableStatus != EnableStatusEnum.Normal.ObjToInt()); if (noOutLocation != null) throw new Exception($"料箱{noOutLocation.PalletCode}货位{noOutLocation.LocationCode}状态不可出库"); List stationMangers = _stationMangerRepository.QueryData(); //下发任务组 string taskGroup= taskDTO.TaskGroupCode.IsNullOrEmpty() ? Guid.NewGuid().ToString().Replace("-","") : taskDTO.TaskGroupCode; foreach (var item in taskDTO.Tasks.OrderBy(x=>x.ToStationCode)) { if (item.ToStationCode.IsNullOrEmpty()) throw new Exception($"任务{item.TaskCode}出库目标操作台不能为空"); //获取操作台 Dt_StationManger? stationManger = stationMangers.FirstOrDefault(x => x.PickStationCode == item.ToStationCode); if (stationManger == null) throw new Exception($"任务{item.TaskCode}出库目标操作台{item.ToStationCode}不存在"); Dt_Task task = _mapper.Map(item); Dt_LocationInfo locationInfo = locationInfos.FirstOrDefault(x=>x.PalletCode==item.ContainerCode); task.SourceAddress = locationInfo.LocationCode; task.CurrentAddress = locationInfo.LocationCode; task.NextAddress = stationManger.PickStationCode; task.TargetAddress = stationManger.PickStationCode; task.GroupId = taskGroup; task.TaskType = TaskTypeEnum.Outbound.ObjToInt(); task.Roadway = locationInfo.RoadwayNo; task.DeviceCode = stationManger.CraneCode; task.TaskState = TaskStatusEnum.AGV_Execute.ObjToInt(); tasks.Add(task); } locationInfos.ForEach(x => { x.LocationStatus=LocationStatusEnum.Lock.ObjToInt(); }); //添加任务更新货位数据 _unitOfWorkManage.BeginTran(); BaseDal.AddData(tasks); _locationInfoRepository.UpdateData(locationInfos); _unitOfWorkManage.CommitTran(); _taskExecuteDetailService.AddTaskExecuteDetail(tasks.Select(x => x.TaskNum).ToList(), "接收WMS任务"); content = tasks.Count > 0 ? content.OK("成功") : content.Error("失败"); } } catch (Exception ex) { content.Error($"任务接收错误,错误信息:{ex.Message}"); } return content; } static object lock_containerFlow = new object(); /// /// 容器入库创建任务 /// /// public WebResponseContent ContainerFlow(ContainerFlowDTO containerFlowDTO, string deviceCode) { WebResponseContent content = new WebResponseContent(); try { lock (lock_containerFlow) { List locationInfos = _locationInfoRepository.QueryData(); Dt_LocationInfo? locationInfo = locationInfos.FirstOrDefault(x=>x.PalletCode== containerFlowDTO.ContainerCode); if (locationInfo != null) throw new Exception($"料箱号{containerFlowDTO.ContainerCode}已存在"); if (BaseDal.QueryFirst(x=>x.PalletCode==containerFlowDTO.ContainerCode)!=null) throw new Exception($"料箱号{containerFlowDTO.ContainerCode}任务已存在"); Dt_LocationInfo? noInLocation = locationInfos.FirstOrDefault(x => x.LocationStatus == LocationStatusEnum.Free.ObjToInt() && x.EnableStatus == EnableStatusEnum.Normal.ObjToInt()); if (noInLocation == null) throw new Exception($"可用货位不足!"); Dt_StationManger stationManger = _stationMangerRepository.QueryFirst(x => x.StationType == StationTypeEnum.StationType_OnlyInbound.ObjToInt() && x.StationDeviceCode == deviceCode); //下发任务组 Dt_Task task = new Dt_Task(); task.PalletCode = containerFlowDTO.ContainerCode; task.SourceAddress = containerFlowDTO.SlotCode; task.CurrentAddress = containerFlowDTO.SlotCode; task.NextAddress = stationManger.StationCode; task.TargetAddress = ""; task.WMSId = ""; task.TaskType = TaskTypeEnum.Inbound.ObjToInt(); task.Roadway = noInLocation.RoadwayNo; task.DeviceCode = stationManger.StationDeviceCode; task.TaskState = TaskStatusEnum.CL_Executing.ObjToInt(); //添加任务 BaseDal.AddData(task); _taskExecuteDetailService.AddTaskExecuteDetail(new List() { task.TaskNum }, "创建入库任务"); content.OK("成功"); } } catch (Exception ex) { content.Error($"错误信息:{ex.Message}"); } return content; } static object lock_requestInTask = new object(); /// /// 申请入库 /// /// public WebResponseContent RequestInTask(string stationCode,string barCode) { WebResponseContent content = new WebResponseContent(); try { lock (lock_requestInTask) { Dt_Task task = BaseDal.QueryFirst(x => x.PalletCode == barCode && x.NextAddress == stationCode && x.TaskState == TaskStatusEnum.CL_Executing.ObjToInt()); if (task == null) throw new Exception($"{barCode}料箱未找到任务!"); Dt_LocationInfo? locationInfo = _locationInfoService.AssignLocation(); if (locationInfo == null) throw new Exception($"可用货位不足!"); task.NextAddress = locationInfo.LocationCode; task.TargetAddress = locationInfo.LocationCode; task.CurrentAddress = stationCode; task.DeviceCode = "AGV"; task.TaskState = TaskStatusEnum.AGV_Execute.ObjToInt(); locationInfo.LocationStatus = LocationStatusEnum.Lock.ObjToInt(); //更新任务和货位数据 _unitOfWorkManage.BeginTran(); BaseDal.UpdateData(task); _locationInfoRepository.UpdateData(locationInfo); _unitOfWorkManage.CommitTran(); _taskExecuteDetailService.AddTaskExecuteDetail(new List() { task.TaskNum }, $"分配货位{locationInfo.LocationCode}"); content.OK("成功"); } } catch (Exception ex) { _unitOfWorkManage.RollbackTran(); content.Error($"错误信息:{ex.Message}"); } return content; } public static string Post(string serviceAddress, string requestJson = "", string contentType = "application/json", Dictionary? headers = null) { string result = string.Empty; DateTime beginDate = DateTime.Now; try { using (HttpContent httpContent = new StringContent(requestJson)) { httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); using HttpClient httpClient = new HttpClient(); httpClient.Timeout = new TimeSpan(0, 0, 30); string LoginToken = AppSettings.Get("MESLoginToken"); headers = new Dictionary { //正式 { "LoginToken", LoginToken } }; if (headers != null) { foreach (var header in headers) httpClient.DefaultRequestHeaders.Add(header.Key, header.Value); } HttpResponseMessage responseMessage = httpClient.PostAsync(serviceAddress, httpContent).Result; result = responseMessage.Content.ReadAsStringAsync().Result; } return result; } catch (Exception e) { throw new Exception(e.Message); } finally { Logger.Add(serviceAddress, requestJson == null ? "" : requestJson, result, beginDate); } } /// /// 更新任务异常信息显示 /// /// 任务号 /// 异常信息 public WebResponseContent UpdateTaskExceptionMessage(int taskNum, string message) { WebResponseContent content = new WebResponseContent(); try { Dt_Task task = BaseDal.QueryFirst(x => x.TaskNum == taskNum); if (task == null) return WebResponseContent.Instance.Error($"未找到该任务信息,任务号:【{taskNum}】"); task.TaskState = (int)TaskStatusEnum.Pending; task.ExceptionMessage = message; task.ModifyDate = DateTime.Now; BaseDal.UpdateData(task); _taskExecuteDetailService.AddTaskExecuteDetail(task, task.ExceptionMessage); content = WebResponseContent.Instance.OK(); } catch (Exception ex) { content = WebResponseContent.Instance.Error(ex.Message); } return content; } /// /// 恢复挂起任务 /// /// 任务号 /// 返回处理结果 public WebResponseContent TaskStatusRecovery(int taskNum) { WebResponseContent content = new WebResponseContent(); try { Dt_Task task = BaseDal.QueryFirst(x => x.TaskNum == taskNum); if (task == null) return WebResponseContent.Instance.Error($"未找到该任务信息,任务号:【{taskNum}】"); if (task.TaskState != (int)TaskStatusEnum.Pending) { return content = WebResponseContent.Instance.Error($"该任务状态不可恢复,任务号:【{taskNum}】,任务状态:【{task.TaskState}】"); } Dt_TaskExecuteDetail taskExecuteDetail = _taskExecuteDetailRepository.QueryFirst(x => x.TaskId == task.TaskId && x.IsNormal, new Dictionary { { nameof(Dt_TaskExecuteDetail.TaskDetailId), OrderByType.Desc } }); if (taskExecuteDetail != null) { task.TaskState = taskExecuteDetail.TaskState; } else { task.TaskState = (int)TaskStatusEnum.New; } task.ExceptionMessage = string.Empty; BaseDal.UpdateData(task); _taskExecuteDetailService.AddTaskExecuteDetail(task, $"人工恢复挂起任务,恢复挂起时任务状态【{task.TaskState}】"); content = WebResponseContent.Instance.OK(); } catch (Exception ex) { content = WebResponseContent.Instance.Error(ex.Message); } return content; } /// /// 回滚任务状态 /// /// 任务号 /// 返回处理结果 public WebResponseContent RollbackTaskStatusToLast(int taskNum) { WebResponseContent content = new(); try { Dt_Task task = BaseDal.QueryFirst(x => x.TaskNum == taskNum); if (task == null) return WebResponseContent.Instance.Error($"未找到该任务信息,任务号:【{taskNum}】"); int oldState = task.TaskState; Dt_TaskExecuteDetail taskExecuteDetail = _taskExecuteDetailRepository.QueryFirst(x => x.TaskId == task.TaskId && x.TaskState < task.TaskState && x.TaskState > 0, new Dictionary { { nameof(Dt_TaskExecuteDetail.TaskDetailId), OrderByType.Desc } }); if (taskExecuteDetail != null) { task.TaskState = taskExecuteDetail.TaskState; task.CurrentAddress = taskExecuteDetail.CurrentAddress; task.NextAddress = taskExecuteDetail.NextAddress; } else { return content = WebResponseContent.Instance.Error($"未找到任务明细信息,该任务状态不可回滚到上一步,任务号:【{taskNum}】,任务状态:【{task.TaskState}】"); } task.ExceptionMessage = string.Empty; BaseDal.UpdateData(task); _taskExecuteDetailService.AddTaskExecuteDetail(task, $"人工将任务状态从【{oldState}】回滚到【{task.TaskState}】"); content = WebResponseContent.Instance.OK(); } catch (Exception ex) { content = WebResponseContent.Instance.Error(ex.Message); } return content; } /// /// 接受WMS手动完成任务 /// /// /// public WebResponseContent RecWMSTaskCompleted(int taskNum) { try { Dt_Task task = BaseDal.QueryFirst(x => x.TaskNum == taskNum); if (task != null) { task.TaskState = TaskStatusEnum.Finish.ObjToInt(); BaseDal.DeleteAndMoveIntoHty(task, OperateTypeEnum.人工完成); } return WebResponseContent.Instance.OK(); } catch (Exception ex) { return WebResponseContent.Instance.Error(ex.Message); } } /// /// AGV任务放行 /// /// public WebResponseContent AgvTaskFlow(string code) { WebResponseContent content=new WebResponseContent(); try { string? url = _apiInfoRepository.QueryFirst(x => x.ApiCode == APIEnum.AgvTaskFlow.ToString())?.ApiAddress; if (string.IsNullOrEmpty(url)) throw new Exception($"{code},未找到AGV任务放行接口,请检查接口配置"); AgvTaskFlowDTO agvTaskFlowDTO = new AgvTaskFlowDTO() { RequestId = Guid.NewGuid().ToString().Replace("-", ""), MissionCode = code }; string request = JsonConvert.SerializeObject(agvTaskFlowDTO, settings); string response = HttpHelper.Post(url, request); AgvResponseContent agvResponse = JsonConvert.DeserializeObject(response) ?? throw new Exception($"{code},未接收到AGV任务放行返回值"); if (!agvResponse.Success) throw new Exception($"料箱{code},AGV任务放行错误,信息:{agvResponse.Message}"); content.OK(); } catch (Exception ex) { content.Error(ex.Message); } return content; } /// /// WMS料箱到达拣选位上报 /// /// public WebResponseContent WMSPickUp(string stationCode,string pickCode) { WebResponseContent content = new WebResponseContent(); try { string? url = _apiInfoRepository.QueryFirst(x => x.ApiCode == APIEnum.WMSPickArrivedUp.ToString())?.ApiAddress; if (string.IsNullOrEmpty(url)) throw new Exception($"未找到WMS料箱到达拣选位上报接口,请检查接口配置"); ContainerArriveDTO containerArriveDTO = new ContainerArriveDTO() { SlotCode = stationCode, ContainerCode = pickCode }; string request = JsonConvert.SerializeObject(containerArriveDTO, settings); string response = HttpHelper.Post(url, request); WMSResponseContent wMSResponse = JsonConvert.DeserializeObject(response) ?? throw new Exception($"{pickCode},未接收到WMS料箱到达拣选位上报返回值"); if (wMSResponse.Code != "0") throw new Exception($"料箱{pickCode}WMS料箱到达拣选位上报错误,信息:{wMSResponse.Msg}"); content.OK(); } catch (Exception ex) { content.Error(ex.Message); } return content; } /// /// 任务完成 /// /// /// public WebResponseContent TaskCompleted(int taskNum) { WebResponseContent content = new WebResponseContent(); try { Dt_Task task = BaseDal.QueryFirst(x => x.TaskNum == taskNum); if (task != null && task.TaskType.GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup)//出库任务逻辑 { Dt_LocationInfo locationInfo = _locationInfoRepository.QueryFirst(x=>x.PalletCode==task.PalletCode); if (locationInfo.LocationStatus != LocationStatusEnum.Lock.ObjToInt()) { return content.Error($"{locationInfo.LocationCode}货位状态不正确"); } task.TaskState = TaskStatusEnum.Finish.ObjToInt(); locationInfo.LocationStatus = LocationStatusEnum.Free.ObjToInt(); locationInfo.PalletCode = ""; _unitOfWorkManage.BeginTran(); _locationInfoRepository.UpdateData(locationInfo); BaseDal.DeleteAndMoveIntoHty(task, App.User?.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成); _unitOfWorkManage.CommitTran(); } else if(task != null && task.TaskType.GetTaskTypeGroup() == TaskTypeGroup.InboundGroup)//入库任务逻辑 { //string? url = _apiInfoRepository.QueryFirst(x => x.ApiCode == APIEnum.WMSInBoundBack.ToString())?.ApiAddress; //if (string.IsNullOrEmpty(url)) //{ // _taskExecuteDetailService.AddTaskExecuteDetail(taskNum, $"未找到WMS入库上报接口,请检查接口配置"); // UpdateTaskExceptionMessage(taskNum, $"未找到WMS入库上报接口,请检查接口配置"); // return content.Error($"{taskNum},未找到WMS入库上报接口,请检查接口配置"); //} //ContainerInFinishDTO containerInFinishDTO = new ContainerInFinishDTO() //{ // TaskCode= task.TaskNum.ToString(), // ContainerCode = task.PalletCode, // FromStationCode = task.SourceAddress, // ToLocationCode = task.TargetAddress //}; //string request = JsonConvert.SerializeObject(containerInFinishDTO, settings); ////调用接口 //string response = HttpHelper.Post(url, request); //WMSResponseContent wMSResponse = JsonConvert.DeserializeObject(response) ??throw new Exception($"{taskNum},未接收到WMS入库上报返回值"); //if (wMSResponse.Code!="0") throw new Exception($"入库任务{task.TaskNum}WMS入库上报错误,信息:{wMSResponse.Msg}"); Dt_LocationInfo locationInfo = _locationInfoRepository.QueryFirst(x => x.LocationCode == task.TargetAddress); if (locationInfo.LocationStatus != LocationStatusEnum.Lock.ObjToInt()) { return content.Error($"{locationInfo.LocationCode}货位状态不正确"); } task.TaskState = TaskStatusEnum.Finish.ObjToInt(); locationInfo.LocationStatus = LocationStatusEnum.InStock.ObjToInt(); locationInfo.PalletCode = task.PalletCode; _unitOfWorkManage.BeginTran(); _locationInfoRepository.UpdateData(locationInfo); BaseDal.DeleteAndMoveIntoHty(task, App.User?.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成); _unitOfWorkManage.CommitTran(); } content.OK("任务完成"); } catch (Exception ex) { _unitOfWorkManage.RollbackTran(); content.Error(ex.Message); } return content; } } }