using System.Diagnostics; using System.Dynamic; using System.Linq.Expressions; using JNPF; using JNPF.Common.Contracts; using JNPF.Common.Core.Manager; using JNPF.Common.Enums; using JNPF.Common.Extension; using JNPF.Common.Manager; using JNPF.Common.Security; using JNPF.FriendlyException; using JNPF.Systems.Interfaces.System; using Mapster; using Microsoft.AspNetCore.Mvc; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using SqlSugar; using Tnb.BasicData.Entities; using Tnb.Common.Extension; using Tnb.Common.Utils; using Tnb.WarehouseMgr.Entities; using Tnb.WarehouseMgr.Entities.Configs; using Tnb.WarehouseMgr.Entities.Consts; using Tnb.WarehouseMgr.Entities.Dto; using Tnb.WarehouseMgr.Entities.Dto.Inputs; using Tnb.WarehouseMgr.Entities.Entity; using Tnb.WarehouseMgr.Entities.Enums; using Tnb.WarehouseMgr.Interfaces; namespace Tnb.WarehouseMgr { /// /// 库房业务类(出入库) /// public class WareHouseService : ServiceLoggerBase, IWareHouseService { private readonly ISqlSugarClient _db; private readonly IDictionaryDataService _dictionaryDataService; private readonly IBillRullService _billRullService; private readonly IUserManager _userManager; private readonly ICacheManager _cacheManager; private readonly IElevatorControlService _elevatorControlService; private static readonly Dictionary _elevatorAgvCtlStatusMap = new(StringComparer.OrdinalIgnoreCase); private readonly ElevatorControlConfiguration _eleCtlCfg = App.Configuration.Build(); public Func AddUnExecuteTask { get; set; } public WareHouseService(ISqlSugarRepository repository, IDictionaryDataService dictionaryDataService, IBillRullService billRullService, IUserManager userManager, ICacheManager cacheManager, IElevatorControlService elevatorControlService) //: base(repository.AsSugarClient()) { _db = repository.AsSugarClient(); _dictionaryDataService = dictionaryDataService; _billRullService = billRullService; _userManager = userManager; _cacheManager = cacheManager; _elevatorControlService = elevatorControlService; _ = InitializationTask; } /// /// 根据载具Id带出库位、仓库信息 /// /// 载具id /// /// returns: ///
{ ///
carry_id:载具Id ///
carry_name:载具名称 ///
location_id:库位Id ///
location_name:库位名称 ///
warehouse_id:库房Id ///
warehouse_name:库房名称 ///
} ///
[HttpGet] public async Task GetLocationAndWorkHouseByCarryId([FromRoute] string carryId) { var items = await _db.Queryable().LeftJoin((a, b) => a.location_id == b.id) .LeftJoin((a, b, c) => b.wh_id == c.id) .Where(a => a.id == carryId) .Select((a, b, c) => new { carry_id = a.id, a.carry_name, location_id = b.id, b.location_name, warehouse_id = c.id, warehouse_name = c.whname, }) .ToListAsync(); return items ?? Enumerable.Empty(); } /// /// 库房业务,入库、出库申请新增修改功能 /// /// /// [HttpPost] public async Task ApplyFor(InOutStockApplyforUpInput input) { if (input == null) { throw new ArgumentNullException(nameof(input)); } async Task _updateLocalFunc(InOutStockApplyforUpInput input) where TStockD : BaseEntity, new() where TStockCode : BaseEntity, IInOutStockCode, new() { TStockD instockD = input.Adapt(); global::System.Collections.Generic.List? stockCodes = input.InstockCodes?.Adapt>(); if (stockCodes?.Count > 0) { stockCodes.ForEach(x => { if (x.id.IsNullOrWhiteSpace()) { x.id = SnowflakeIdHelper.NextId(); } x.bill_d_id = instockD.id; }); } return await Update(instockD, stockCodes!); } bool isOk = input.inoutStockType switch { EnumInOutStockType.In => await _updateLocalFunc(input), EnumInOutStockType.Out => await _updateLocalFunc(input), _ => throw new ArgumentOutOfRangeException(nameof(input.inoutStockType), $"Not expected EnumInOutStockType value: {input.inoutStockType}"), }; if (!isOk) { throw Oops.Oh(ErrorCode.COM1001); } } /// /// 根据明细Id获取出入库明细信息 /// /// [HttpGet] public async Task GetInOutStockCodesById([FromQuery] InOutStockDetailQuery input) { dynamic? result = input.inoutStockType switch { EnumInOutStockType.In => await FetchInOutStockCodesById(input.bill_d_id), EnumInOutStockType.Out => await FetchInOutStockCodesById(input.bill_d_id), _ => throw new NotImplementedException(), }; return result ?? Enumerable.Empty(); } /// /// 入库策略 /// /// [HttpGet] public async Task> InStockStrategy([FromQuery] InStockStrategyQuery input) { List items = new(); try { WmsInstockPolicies policy = await _db.Queryable().Where(it => it.status == 1).FirstAsync(); if (policy == null) { throw new AppFriendlyException("没有可用的策略", 500); } Expression> whereExp = Expressionable.Create() .And(it => it.wh_id == input.warehouse_id) .And(it => it.is_lock == 0) .And(it => it.is_type == ((int)EnumLocationType.存储库位).ToString()) .And(it => it.is_use == ((int)EnumCarryStatus.空闲).ToString()) .ToExpression(); items = await _db.Queryable().Where(whereExp).OrderBy(policy.policy).ToListAsync(); } catch (Exception) { throw; } return items.Take(input.Size).ToList(); } /// /// 出库策略 /// /// [HttpGet] public async Task> OutStockStrategy([FromQuery] OutStockStrategyQuery input) { Expressionable whereExprable = Expressionable.Create() .And((a, b, c) => a.is_lock == 0) .And((a, b, c) => !string.IsNullOrEmpty(a.location_id)) .And((a, b, c) => c.is_type == ((int)EnumLocationType.存储库位).ToString()) .And((a, b, c) => a.out_status == "0") .And((a, b, c) => c.wh_id == input.warehouse_id) .AndIF(!string.IsNullOrEmpty(input.material_id), (a, b, c) => b.material_id == input.material_id) .AndIF(!string.IsNullOrEmpty(input.code_batch), (a, b, c) => b.code_batch == input.code_batch) .AndIF(!string.IsNullOrEmpty(input.material_specification), (a, b, c) => b.material_specification == input.material_specification) .AndIF(!string.IsNullOrEmpty(input.container_no), (a, b, c) => b.container_no == input.container_no) .AndIF(!string.IsNullOrEmpty(input.carrystd_id), (a, b, c) => a.carrystd_id == input.carrystd_id); Expression> carryStatusFilterExp = !input.material_id.IsNullOrWhiteSpace() ? (a, b, c) => a.carry_status == ((int)EnumCarryStatus.占用).ToString() : (a, b, c) => a.carry_status == ((int)EnumCarryStatus.空闲).ToString(); _ = whereExprable.And(carryStatusFilterExp); Expression> whereExpr = whereExprable.ToExpression(); SqlSugarClient cyDb = _db.CopyNew(); WmsInstockPolicies policy = await cyDb.Queryable().Where(it => it.status == 1).FirstAsync(); if (policy == null) { throw new AppFriendlyException("没有可用策略", 500); } List items = await cyDb.Queryable().LeftJoin((a, b) => a.id == b.carry_id) .LeftJoin((a, b, c) => a.location_id == c.id) .Where(whereExpr) .OrderBy(policy.policy) .Select() .ToListAsync(); return input.Size > 0 ? items.Take(input.Size).ToList() : items; } /// /// 生成任务执行 /// /// [HttpPost] public async Task GenTaskExecute() { await s_taskExecuteSemaphore.WaitAsync(); Stopwatch sw = Stopwatch.StartNew(); CancellationTokenSource agvCts = new(); SqlSugarClient db = _db.CopyNew(); try { //获取电梯数据 List elevatorList = await db.Queryable().InnerJoin((a, b) => a.id == b.bill_id) .Select((a, b) => new WmsElevatorH { bill_id = b.bill_id, location_id = b.location_id, location_code = b.location_code, point_id = b.point_id, point_code = b.point_code, floor = b.floor }, true).ToListAsync(); //获取所有未下发的预任务申请 List preTasks = await db.Queryable().InnerJoin((a, b) => a.startlocation_id == b.location_id && a.carry_id == b.id) .InnerJoin((a, b, c) => a.area_id == c.id) .InnerJoin((a, b, c, d) => a.endlocation_id == d.id && d.is_use == "0") .Where(a => a.status == WmsWareHouseConst.PRETASK_BILL_STATUS_DXF_ID && !string.IsNullOrWhiteSpace(a.startlocation_id)) .OrderBy(a => new { priority = SqlFunc.Desc(a.priority), a.bill_code }) .Select((a, b, c, d) => new WmsPretaskH { move_num = c.move_num, third_eqp_type = c.third_eqp_type, }, true) .ToListAsync(); List agvElevatorTasks = preTasks .Where(it => it.endlocation_code.StartsWith("DT", StringComparison.OrdinalIgnoreCase) && !it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase)) .ToList(); //it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase) var elePreTasks = preTasks.Where(it => it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase)).ToList(); var normalPreTasks = preTasks.Where(it => !agvElevatorTasks.Concat(elePreTasks).Select(x => x.endlocation_code).Contains(it.endlocation_code)).ToList(); IEnumerable firstEleGrp = agvElevatorTasks.GroupBy(g => g.endlocation_code).Select(t => t.OrderBy(o => o.bill_code).FirstOrDefault()); //agvElevatorTasks = agvElevatorTasks.FindAll(x => firstEleGrp.Select(y => y.endlocation_code).Contains(x.endlocation_code)).DistinctBy(x=>x.endlocation_code).ToList(); agvElevatorTasks = firstEleGrp?.ToList() ?? Enumerable.Empty().ToList()!; //如果电梯任务,预Agv任务存在相同目标库位,删除Agv任务保证电梯任务先行 var equalEndLocPreTasks = elePreTasks.Select(x => x.endlocation_code).Intersect(agvElevatorTasks.Select(x => x.endlocation_code)); if (equalEndLocPreTasks.Any()) { agvElevatorTasks = agvElevatorTasks.Where(x => !equalEndLocPreTasks.Contains(x.endlocation_code)).ToList(); } preTasks = normalPreTasks.Concat(agvElevatorTasks).Concat(elePreTasks).ToList(); List ids = preTasks.Select(x => x.id).Distinct().ToList(); List? preTaskCodes = await db.Queryable().Where(it => ids.Contains(it.bill_id)).ToListAsync(); if (preTasks.Count > 0) { //根据预任务管理区分组,获取到所有分组后的预任务,遍历每个预任务 是否为任务链,通过管理区ID List> preTaskGroups = preTasks.GroupBy(g => g.area_code).ToList(); List disTasks = new(); List distaskCodes = new(); foreach (IGrouping? itGroup in preTaskGroups) { List items = itGroup.Adapt>(); for (int i = 0, cnt = items.Count; i < cnt; i++) { items[i].id = SnowflakeIdHelper.NextId(); items[i].status = WmsWareHouseConst.TASK_BILL_STATUS_DZX_ID; } int moveNum = itGroup.First().move_num; int itemsCount = items.Count; int mod = itemsCount % moveNum > 0 ? (itemsCount / moveNum) + 1 : itemsCount / moveNum; WmsDistaskH[] arrary = items.ToArray(); //for (int i = 1; i <= mod; i++) { if (moveNum >= 1) { List areaPreTasks = itGroup.ToList(); if (areaPreTasks.Any(x => x.third_eqp_type.ToEnum() != EnumTaskChainType.CTU)) { foreach (var x in items) { string groupCode = _billRullService.GetBillNumber(WmsWareHouseConst.WMS_TASK_EXECUTE_ENCODE).Result; x.is_chain = 0; x.groups = groupCode; x.bill_code = $"{groupCode}-1"; } } else if ((moveNum >= areaPreTasks.Count && areaPreTasks.Count > 1) || moveNum <= areaPreTasks.Count) { string groupCode = await _billRullService.GetBillNumber(WmsWareHouseConst.WMS_TASK_EXECUTE_ENCODE); items.ForEach(x => x.is_chain = 1); int start = 0; int end = Math.Min(itemsCount, moveNum); List arrList = new(mod); while (start < itemsCount) { WmsDistaskH[] subArray = arrary[start..end]; arrList.Add(subArray); start = end; end = Math.Min(end + moveNum, arrary.Length); } foreach (WmsDistaskH[] arr in arrList) { for (int j = 1, len = arr.Length; j <= len; j++) { arr[j - 1].groups = groupCode; arr[j - 1].bill_code = $"{groupCode}-{j}"; } } } } } if (preTaskCodes?.Count > 0) { foreach (WmsDistaskH disTask in items) { List curPreTaskCodes = preTaskCodes.FindAll(x => x.bill_id == disTask.pretask_id); List curDisTaskCodes = curPreTaskCodes.Adapt>(); curDisTaskCodes.ForEach(x => { x.id = SnowflakeIdHelper.NextId(); x.bill_id = disTask.id; x.create_time = DateTime.Now; }); distaskCodes.AddRange(curDisTaskCodes); } } disTasks.AddRange(items); } await db.Ado.BeginTranAsync(); List endPointIds = disTasks.Where(t => t.area_code.StartsWith("ELE", StringComparison.OrdinalIgnoreCase)).Select(t => t.endpoint_id).ToList(); Logger.Information($"endPointIds:{string.Join(",", endPointIds)}"); if (endPointIds?.Count > 0) { elevatorList = elevatorList.FindAll(x => endPointIds.Contains(x.point_id)); Logger.Information($"过滤后的elevatorList:{JsonConvert.SerializeObject(elevatorList)}"); if (elevatorList?.Count > 0) { foreach (WmsElevatorH? e in elevatorList) { WmsDistaskH? disTask = disTasks.Find(x => x.endpoint_id == e.point_id); if (disTask != null) { disTask.device_id = e.elevator_id; } } } } //disTasks.ForEach(x => x.id = SnowflakeIdHelper.NextId()); int row = await db.Insertable(disTasks).ExecuteCommandAsync(); if (preTaskCodes?.Count > 0) { row = await db.Insertable(distaskCodes).ExecuteCommandAsync(); } if (row > 0) { List preTaskIds = preTasks.Select(x => x.id).ToList(); row = await db.Updateable().SetColumns(it => new WmsPretaskH { status = WmsWareHouseConst.PRETASK_BILL_STATUS_YXF_ID }).Where(it => preTaskIds.Contains(it.id)).ExecuteCommandAsync(); } await db.Ado.CommitTranAsync(); if (string.Equals(_eleCtlCfg.Environment, ElevatorConsts.EnvironmentName, StringComparison.OrdinalIgnoreCase)) { //呼梯操作 //获取目标库位为电梯库位的任务 var agvDTTasks = disTasks.Where(it => it.endlocation_code.StartsWith("DT", StringComparison.OrdinalIgnoreCase) && !it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase)).ToList(); foreach (var at in agvDTTasks) { var ele = elevatorList.Find(x => x.location_code == at.endlocation_code); Logger.Information($"ele.elevator_id={ele?.elevator_id}"); if (ele != null) { at.device_id = ele.elevator_id; } } List<(string endlocation_code, string device_id, string id, string? start_floor)> endLocCodes = agvDTTasks .Select(it => (it.endlocation_code, it.device_id, it.id, it.start_floor)).ToList(); if (endLocCodes?.Count > 0) { await CallingLanding(endLocCodes); } //执行电梯任务 List? elevatorTasks = disTasks.Where(it => it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase)).ToList(); if (elevatorTasks?.Count > 0) { Logger.Information($"当前电梯任务数:{elevatorTasks?.Count ?? 0}"); Logger.Information("准备执行电梯任务"); Logger.Information("执行电梯任务"); foreach (WmsDistaskH? elevatorTask in elevatorTasks) { await ExecuteTargetFloorTask(elevatorTask); } } ////调用AGV创建任务链接口 List agvTasks = disTasks.Where(it => !it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase)).ToList(); if (agvTasks?.Count > 0) { await AgvDispatch(agvTasks, agvCts.Token); } } } } catch (Exception ex) when (ex is HttpRequestException hReqEx) { agvCts.Cancel(); } catch (Exception ex) { Logger.Error("生成预任务执行时出现错误", ex); await db.Ado.RollbackTranAsync(); throw; } finally { _ = s_taskExecuteSemaphore.Release(); agvCts.Dispose(); } } /// /// 呼梯操作 /// /// /// private async Task CallingLanding(List<(string endlocation_code, string device_id, string id, string floorNO)> endLocCodes) { Logger.Information($" 开始呼梯操作............."); Logger.Information($"电梯信息:{JsonConvert.SerializeObject(s_elevatorMap)}"); try { foreach ((_, string devId, string disTaskId, string floorNO) in endLocCodes) { Logger.Information($"devId:{devId}"); if (!s_elevatorMap.TryGetValue(devId, out object? elevatorCode)) { continue; } string? devName = elevatorCode.ToString(); Logger.Information($"电梯编号:{devName}"); Logger.Information($"当前:{devName.Match(@"\d+")}#梯"); await _elevatorControlService.WriteTagAsync(devName, ElevatorConsts.AGVControl, 1); (int sysStatus, int runStatus, int curFloorNo, int doorStatus, int agvStatus) eleStatusMulti = (-1, -1, -1, -1, -1); if (!_elevatorAgvCtlStatusMap.TryGetValue(devId, out int agvCtlStatus) || agvCtlStatus != (int)EnumAgvStatus.AGV运行状态) { Logger.Information("获取电梯状态"); Logger.Information($"_elevatorControlService ==null{_elevatorControlService == null}"); var tags = new[] { "SysStatus", "RunStatus", "FloorNo", "DoorStatus", "AGVStatus" }; do { eleStatusMulti = await _elevatorControlService.GetElevatorStatus(devName, tags, CancellationToken.None); await Task.Delay(1000); } while (eleStatusMulti.agvStatus != (int)EnumAgvStatus.AGV运行状态); Logger.Information($"{devName.Match(@"\d+")}#, 当前Agv状态:{eleStatusMulti.agvStatus.ToEnum()}"); _elevatorAgvCtlStatusMap[devId] = eleStatusMulti.agvStatus; } Logger.Information($"任务开始目标楼层为:{floorNO}"); int floorN = await GetRealFloor(floorNO.ParseToInt()); //如果电梯在当前楼层则不呼梯 if (floorN == eleStatusMulti.curFloorNo) { Logger.Information($"{devName.Match(@"\d+")}#,在当前楼层,无需呼梯"); continue; } Logger.Information($"实际目标楼层为:{floorN}"); WmsElevatorUnexecute elevatorQueueItem = new() { distask_id = disTaskId, elevator_id = devId, elevator_code = devName, floor = floorN, //5代表4楼 task_status = "待执行", create_id = _userManager.UserId, create_time = DateTime.Now }; List elevatorQueue = await _db.Queryable().Where(it => it.distask_id == disTaskId && it.task_status == "执行中").ToListAsync(); if ((elevatorQueue.IsNull() || elevatorQueue.Count < 1) && floorN != eleStatusMulti.curFloorNo) { elevatorQueueItem.task_status = "执行中"; bool callLiftRes = await _elevatorControlService.CallLift(devName, floorN, CancellationToken.None); string successful = "成功", fail = "失败"; string callLiftResult = callLiftRes ? successful : fail; Logger.Information($"{devName.Match(@"\d+")}#, 呼梯结果:{callLiftResult}"); } //如果当前电梯有任务在做,将当前呼梯任务放入待执行队列 _ = await _db.Insertable(elevatorQueueItem).ExecuteCommandAsync(); } } catch (Exception ex) { Logger.Error("呼梯操作错误", ex); Logger.Error($"呼梯操作错误堆栈跟踪:{Environment.NewLine}{ex.StackTrace}"); throw; } } /// /// 执行到目标楼层电梯任务 /// /// /// private async Task ExecuteTargetFloorTask(WmsDistaskH disTask) { //收到放货确认通知,向电梯发送到3楼的指令 Logger.Information($"开始执行电梯任务,任务ID:{disTask.id}"); try { if (!s_elevatorMap.TryGetValue(disTask.device_id, out object? elevatorCode)) { return; } string devName = s_elevatorMap[disTask.device_id]?.ToString() ?? _eleCtlCfg.DevName3; Logger.Information($"当前:{devName.Match(@"\d+")}#梯"); _ = await _elevatorControlService.WriteTagAsync(devName, ElevatorConsts.AGVControl, 1); (int sysStatus, int runStatus, int curFloorNo, int doorStatus, int agvStatus) eleStatusMulti = (-1, -1, -1, -1, -1); var tags = new[] { "SysStatus", "RunStatus", "FloorNo", "DoorStatus", "AGVStatus" }; if (!_elevatorAgvCtlStatusMap.TryGetValue(disTask.id, out int agvCtlStatus) || agvCtlStatus != (int)EnumAgvStatus.AGV运行状态) { do { eleStatusMulti = await _elevatorControlService.GetElevatorStatus(devName, tags, CancellationToken.None); await Task.Delay(1000); } while (eleStatusMulti.agvStatus != (int)EnumAgvStatus.AGV运行状态); Logger.Information($"{devName.Match(@"\d+")}#, 当前Agv状态:{eleStatusMulti.agvStatus.ToEnum()}"); _elevatorAgvCtlStatusMap[disTask.id] = eleStatusMulti.agvStatus; } int doorStatus = -1; bool closeDoorRes = await _elevatorControlService.SendOpenCloseCmd(devName, 4); //向电梯发送前门关门指令 Logger.Information($"关门结果:{closeDoorRes}"); do { doorStatus = await _elevatorControlService.GetTagAsync(devName, ElevatorConsts.DoorStatus); await Task.Delay(1000); } while (doorStatus != 4); Logger.Information($"当前门状态:{doorStatus}"); int floor = await GetRealFloor(disTask.end_floor.ParseToInt()); Logger.Information($"目标楼层:{floor}"); //发送到目标楼的指令 dynamic reuslt = await _elevatorControlService.WriteTagAsync(devName, ElevatorConsts.FloorExecute, floor); //电梯任务手动执行任务状态上报 (int sysStatus, int runStatus, int floorNo, int doorStatus, int agvStatus) tuple = (-1, -1, -1, -1, -1); do { tuple = await _elevatorControlService.GetElevatorStatus(devName,tags, CancellationToken.None); await Task.Delay(1000); } while (tuple.sysStatus != 3 && tuple.runStatus != 0); Logger.Information($"sysStatus:{tuple.sysStatus},runStatus:{tuple.runStatus},floorNo:{tuple.floorNo},disTask.end_floor={disTask.end_floor}"); if (tuple.sysStatus.ToEnum() == EnumSysStatus.正常状态 && tuple.runStatus.ToEnum() == EnumRunStatus.停梯) { Logger.Information($"disTask.require_id={disTask.require_id}"); List disTaskIds = new() { disTask.id }; var upInput = new { disTaskIds = disTask.id }; TaskExecuteAfterUpInput teaUpInput = new() { disTaskIds = disTaskIds, }; await TaskExecuteAfter(teaUpInput); TaskCompleUpInput tcUpInput = new() { disTaskIds = disTaskIds, }; await TaskComplate(tcUpInput); } } catch (Exception ex) { Logger.Error("执行到目标楼层电梯任务失败", ex); throw; } } /// /// Agv调度 /// /// /// /// private async Task AgvDispatch(List disTasks, CancellationToken token) { //调用AGV创建任务链接口 try { AgvRequestConfig requestCfg = App.Configuration.Build(); string url = requestCfg.AgvRequestUrls.CreateTaskChainUrl; var taskChainCodeDic = disTasks.Where(t => !t.groups.IsNullOrWhiteSpace()).GroupBy(g => g.groups!) .ToDictionary(x => x.Key, x => x.Select(it => new { taskCode = it.bill_code, sourceName = it.startpoint_code, targetName = it.endpoint_code, containerCode = it.carry_code, })); Logger.Information($"请求地址:{url}"); foreach ((string k, object v) in taskChainCodeDic) { dynamic reqBody = new ExpandoObject(); reqBody.taskChainCode = k; reqBody.type = (int)EnumTaskChainType.AGV; reqBody.sequential = false; reqBody.taskChainPriority = 0; reqBody.taskList = v; Logger.Information($"请求参数:{JsonConvert.SerializeObject(reqBody)}"); dynamic respBody = await HttpClientHelper.PostStreamAsync(url, reqBody, token); Logger.Information($"调用Agv接口响应结果:{respBody}"); } } catch (Exception ex) { Logger.Error("agv调度失败异常", ex); throw; } } /// /// 任务执行 /// /// /// [HttpPost] public async Task TaskExecute(TaskExecuteUpInput input) { try { await _db.Ado.BeginTranAsync(); //更任务执行 for (int i = 0, cnt = input.disTaskIds.Count; i < cnt; i++) { _ = input.EqpIds?.Count > 0 ? await _db.Updateable().SetColumns(it => new WmsDistaskH { status = WmsWareHouseConst.TASK_BILL_STATUS_YXD_ID, device_id = input.EqpIds[i] }).Where(it => input.disTaskIds.Contains(it.id)).ExecuteCommandAsync() : await _db.Updateable().SetColumns(it => new WmsDistaskH { status = WmsWareHouseConst.TASK_BILL_STATUS_YXD_ID }).Where(it => input.disTaskIds.Contains(it.id)).ExecuteCommandAsync(); //await _db.Updateable().SetColumns(it => setColVal).Where(it => input.disTaskIds.Contains(it.id)).ExecuteCommandAsync(); } List preTaskIds = await _db.Queryable().Where(it => input.disTaskIds.Contains(it.id)).Select(it => it.pretask_id).ToListAsync(); if (preTaskIds.Count > 0) { //更预任务申请表状态 _ = await _db.Updateable().SetColumns(it => new WmsPretaskH { status = WmsWareHouseConst.PRETASK_BILL_STATUS_START_ID }).Where(it => preTaskIds.Contains(it.id)).ExecuteCommandAsync(); } await _db.Ado.CommitTranAsync(); } catch (Exception ex) { Logger.Error("任务执行失败", ex); await _db.Ado.RollbackTranAsync(); throw; } } /// /// 任务执行取操作返回(后续操作) /// /// [HttpPost] public async Task TaskExecuteAfter(TaskExecuteAfterUpInput input) { //更新任务执行表单据状态 try { await _db.Ado.BeginTranAsync(); _ = await _db.Updateable().SetColumns(it => new WmsDistaskH { status = WmsWareHouseConst.TASK_BILL_STATUS_RUNING_ID, act_start_date = DateTime.Now }).Where(it => input.disTaskIds.Contains(it.id)).ExecuteCommandAsync(); //清空载具库位数据 var carryAndLocIds = await _db.Queryable().Where(it => input.disTaskIds.Contains(it.id)).Select(it => new { it.carry_id, it.startlocation_id }).ToListAsync(); if (carryAndLocIds?.Count > 0) { List carryIds = carryAndLocIds.Select(x => x.carry_id).ToList(); _ = await _db.Updateable().SetColumns(it => new WmsCarryH { location_id = null, location_code = null }).Where(it => carryIds.Contains(it.id)).ExecuteCommandAsync(); } //更新起始库位,状态改为空闲、锁定状态,未锁定 if (carryAndLocIds?.Count > 0) { List startLocationIds = carryAndLocIds.Select(x => x.startlocation_id).ToList(); _ = await _db.Updateable().SetColumns(it => new BasLocation { is_use = ((int)EnumCarryStatus.空闲).ToString(), is_lock = 0 }).Where(it => startLocationIds.Contains(it.id)).ExecuteCommandAsync(); } await _db.Ado.CommitTranAsync(); } catch (Exception ex) { Logger.Error("设备取返回操作失败", ex); await _db.Ado.RollbackTranAsync(); } } /// /// 任务完成 /// /// [HttpPost] public async Task TaskComplate(TaskCompleUpInput input) { try { await _db.Ado.BeginTranAsync(); //更新任务执行表,单据状态为 完成 _ = await _db.Updateable().SetColumns(it => new WmsDistaskH { status = WmsWareHouseConst.TASK_BILL_STATUS_COMPLE_ID, act_end_date = DateTime.Now }).Where(it => input.disTaskIds.Contains(it.id)).ExecuteCommandAsync(); List disTasks = await _db.Queryable().InnerJoin((a, b) => a.carry_id == b.id).Where(a => input.disTaskIds.Contains(a.id)).Select((a, b) => new WmsDistaskH { carry_status = b.carry_status }, true).ToListAsync(); if (disTasks?.Count > 0) { // 更新预任务申请表,单据状态为 已完成 List preTaskIds = disTasks.Select(x => x.pretask_id).ToList(); _ = await _db.Updateable().SetColumns(it => new WmsPretaskH { status = WmsWareHouseConst.PRETASK_BILL_STATUS_COMPLE_ID }).Where(it => preTaskIds.Contains(it.id)).ExecuteCommandAsync(); //更新电梯任务数量 var eles = await _db.Queryable().Where(it => disTasks.Select(x => x.area_code).Contains(it.area_code)).ToListAsync(); if (eles?.Count > 0) await _db.Updateable(eles).ReSetValue(it => it.task_nums--).ExecuteCommandAsync(); //更新载具,锁定状态为未锁定,更新载具的库位当前任务的目标库位 Logger.Information("更新载具及库位准备中....."); List<(string carry_id, string carry_status, string endlocation_id, string endlocation_code)> multiList = disTasks.Select(it => (it.carry_id, it.carry_status, it.endlocation_id, it.endlocation_code)).ToList(); Dictionary locWhIdMap = await _db.Queryable().Where(it => multiList.Select(x => x.endlocation_id).Contains(it.id)).ToDictionaryAsync(it => it.id, it => it.wh_id); List carryIts = new(); List carryCodeIts = new(); List locIts = new(); for (int i = 0, cnt = multiList.Count; i < cnt; i++) { WmsCarryH carry = new() { id = multiList[i].carry_id, is_lock = 0, location_id = multiList[i].endlocation_id, location_code = multiList[i].endlocation_code }; string endLocId = multiList[i].endlocation_id; WmsCarryCode carryCode = new() { warehouse_id = locWhIdMap.ContainsKey(endLocId) ? locWhIdMap[endLocId].ToString() : "", location_id = multiList[i].endlocation_id, location_code = multiList[i].endlocation_code }; carryIts.Add(carry); carryCodeIts.Add(carryCode); BasLocation loc = new() { id = multiList[i].endlocation_id, is_lock = 0, is_use = multiList[i].carry_status }; if (multiList[i].carry_status.ToEnum() == EnumCarryStatus.空闲) { loc.is_use = ((int)EnumCarryStatus.占用).ToString(); } locIts.Add(loc); } Logger.Information("更新载具及库位开始....."); _ = await _db.Updateable(carryIts).UpdateColumns(it => new { it.is_lock, it.location_id, it.location_code }).ExecuteCommandAsync(); //更新条码的库位和仓库信息 _ = await _db.Updateable(carryCodeIts).UpdateColumns(it => new { it.warehouse_id, it.location_id, it.location_code }).Where(it => multiList.Select(x => x.carry_id).Contains(it.carry_id)).ExecuteCommandAsync(); //更新库位信息,使用状态为 使用,锁定状态为未锁定 _ = await _db.Updateable(locIts).UpdateColumns(it => new { it.is_use, it.is_lock }).ExecuteCommandAsync(); /* var loginType= _userManager?.LoginType ?? "web"; Log.Information($"_userManager.LoginType={loginType}"); */ //更新业务主表的单据状态 foreach (WmsDistaskH? dt in disTasks) { Logger.Information("开始业务回更"); List disTaskCodes = await _db.Queryable().Where(it => it.bill_id == dt.id).ToListAsync(); WareHouseUpInput upInput = new() { bizTypeId = dt.biz_type, requireId = dt.require_id!, distaskCodes = disTaskCodes, carryIds = disTasks.Select(x => x.carry_id).ToList() }; /*if (!_userManager?.LoginType.IsNullOrEmpty() ?? false) { upInput.loginType = "app"; } else { upInput.loginType = "web"; }*/ Logger.Information("loginType赋值前"); upInput.loginType = "web";//(!string.IsNullOrEmpty(_userManager?.LoginType) ? "app" : "web") ?? "web"; Logger.Information($"upInput.loginType={upInput.loginType}"); if (dt.is_sign == 1 && dt.chain_type == "3") { Logger.Information("执行业务回更操作....."); await DoUpdate(upInput); } } } await _db.Ado.CommitTranAsync(); } catch (Exception ex) { Logger.Error($"任务结束失败", ex); Logger.Error($"任务结束失败堆栈异常", ex); await _db.Ado.RollbackTranAsync(); throw; } finally { _ = GenTaskExecute(); } } /// /// 出入库策略启用、禁用状态修改 /// /// /// [HttpPost] public async Task ModifyPoliciesStatus(ModifyEnabledInput input) { async Task _updateStatus(ModifyEnabledInput input) where T : BaseEntity, IUpdatePoliciesStatus, new() { T obj = new() { status = input.status }; _ = await _db.Updateable(obj).UpdateColumns(it => it.status).Where(it => input.ids.Contains(it.id)).ExecuteCommandAsync(); } switch (input.strategyType) { case EnumInOutStockType.In: await _updateStatus(input); break; case EnumInOutStockType.Out: await _updateStatus(input); break; } } /// /// 生成预任务 /// /// 预任务集合 /// 预任务编码集合 /// public async Task GenPreTask(List preTasks, List preTaskCodes) { //如果预任务出现起终库位相同,则删除对应预任务 //modifiy by ly on 20230922 将当前预任务操作者设为四场管理员 preTasks.ForEach(pt => { pt.org_id = WmsWareHouseConst.AdministratorOrgId; pt.create_id = WmsWareHouseConst.AdministratorUserId; }); if (preTasks.FindAll(it => it.startlocation_id == it.endlocation_id)?.Count > 0) { _ = preTasks.RemoveAll(it => it.startlocation_id == it.endlocation_id); } List> grpList = preTasks.OrderBy(o => o.bill_code).GroupBy(g => g.carry_id).ToList(); if (grpList?.Count > 0) { foreach (IGrouping? grp in grpList) { WmsPretaskH[] arr = grp.ToArray(); if (arr.Length > 1) { WmsPretaskH[] subArr = arr[..^1]; System.Array.ForEach(subArr, a => a.chain_type = "1"); } } } int row = await _db.Insertable(preTasks).ExecuteCommandAsync(); if (preTaskCodes?.Count > 0) { row = await _db.Insertable(preTaskCodes).ExecuteCommandAsync(); } var eleP = preTasks.Find(x => x.area_code.Contains("ELE")); if (eleP != null) { row = await _db.Updateable().SetColumns(it => it.task_nums == it.task_nums + 1).Where(it => it.area_code == eleP.area_code).ExecuteCommandAsync(); } return row > 0; } /// /// 生成预任务后续处理 /// /// [NonAction] public async Task GenInStockTaskHandleAfter(GenPreTaskUpInput input, Expression> setCarryColumnsExp, Expression> setLocaionColumbExp) { try { await _db.Ado.BeginTranAsync(); //根据生成的预任务,插入预任务操作记录 if (input.PreTaskRecord != null) { _ = await _db.Insertable(input.PreTaskRecord).ExecuteCommandAsync(); } if (input.PreTaskHandleCodes.Count > 0) { _ = await _db.Insertable(input.PreTaskHandleCodes).ExecuteCommandAsync(); } //根据载具ID,更新是否锁定和赋值起始库位 if (setCarryColumnsExp != null) { Expression> whereExp = input.CarryIds?.Count > 0 ? it => input.CarryIds.Contains(it.id) : it => it.id == input.CarryId; _ = await _db.Updateable().SetColumns(setCarryColumnsExp).Where(whereExp).ExecuteCommandAsync(); } if (input.CarryStartLocationId.IsNullOrWhiteSpace() == false) { _ = await _db.Updateable().SetColumns(setLocaionColumbExp).Where(it => input.LocationIds.Contains(it.id)).ExecuteCommandAsync(); } //根据所有库位更新库位的锁定状态为“锁定” if (setLocaionColumbExp != null && input.LocationIds?.Count > 0) { _ = await _db.Updateable().SetColumns(setLocaionColumbExp).Where(it => input.LocationIds.Contains(it.id)).ExecuteCommandAsync(); } await _db.Ado.CommitTranAsync(); } catch (Exception) { await _db.Ado.RollbackTranAsync(); throw; } } /// /// 路径算法 /// /// /// /// [NonAction] public async Task> PathAlgorithms(string pStartId, string pEndId) { List roads = await _db.Queryable().Where(it => it.status == 1).ToListAsync(); List points = await LocPathCalcAlgorithms(pStartId, pEndId, roads); try { if (points.FindAll(x => x.location_code != null && x.location_code.Contains("dt", StringComparison.OrdinalIgnoreCase))?.Count > 0) { //查询当前电梯点 List curEleDs = await _db.Queryable().Where(it => points.Select(x => x.id).Contains(it.point_id)).ToListAsync(); //如果有电梯点,则会进行电梯的均匀分配 if (curEleDs?.Count > 0) { //当前电梯 WmsElevatorH curEle = await _db.Queryable().SingleAsync(it => it.id == curEleDs.First().bill_id && it.enabled == 1); //同电梯组电梯 List sGpEle = await _db.Queryable().Where(it => it.elevator_group == curEle.elevator_group && it.id != curEle.id && it.enabled == 1).ToListAsync(); if (curEle == null && sGpEle?.Count > 0) { throw new AppFriendlyException("电梯被禁用或未配置", 500); } //判断电梯组中各电梯任务数 if (curEle == null || sGpEle.FindAll(x => Math.Abs(x.task_nums - curEle.task_nums) % 2 == 1)?.Count > 0) { List sGpDs = await _db.Queryable().Where(it => it.bill_id == sGpEle.First().id).ToListAsync(); if (sGpDs?.Count > 0) { List sGpPoints = await _db.Queryable().Where(it => sGpDs.Select(x => x.point_id).Contains(it.id)).ToListAsync(); string sFEndId = sGpDs.Single(x => x.floor == curEleDs.First().floor).point_id; string eFStartId = sGpDs.Single(x => x.floor == curEleDs.Last().floor).point_id; List sFPoints = await LocPathCalcAlgorithms(pStartId, sFEndId, roads); List elePoints = new(); foreach (WmsElevatorD pt in curEleDs) { WmsPointH? elePoint = sGpPoints.Find(x => x.floor == pt.floor); if (elePoint != null) { elePoints.Add(elePoint); } } List eFPoints = await LocPathCalcAlgorithms(eFStartId, pEndId, roads); _ = elePoints.Remove(elePoints.First()); _ = elePoints.Remove(elePoints.Last()); points.Clear(); points.AddRange(sFPoints); points.AddRange(elePoints); points.AddRange(eFPoints); } } } } } catch (Exception ex) { Logger.Error("路径算法异常", ex); throw; } return points; } /// /// 路径算法 当出现多个载具同时出库,可能需要进入电梯时 /// /// /// /// /// [NonAction] public async Task> PathAlgorithmsEle(string pStartId, string pEndId, int ele) { List roads = await _db.Queryable().Where(it => it.status == 1).ToListAsync(); List points = await LocPathCalcAlgorithms(pStartId, pEndId, roads); try { if (points.FindAll(x => x.location_code != null && x.location_code.Contains("dt", StringComparison.OrdinalIgnoreCase))?.Count > 0) { //查询当前电梯点 List curEleDs = await _db.Queryable().Where(it => points.Select(x => x.id).Contains(it.point_id)).ToListAsync(); //如果有电梯点,则会进行电梯的均匀分配 if (curEleDs?.Count > 0) { //当前电梯 WmsElevatorH curEle = await _db.Queryable().SingleAsync(it => it.id == curEleDs.First().bill_id && it.enabled == 1); //同电梯组电梯 List sGpEle = await _db.Queryable().Where(it => it.elevator_group == curEle.elevator_group && it.id != curEle.id && it.enabled == 1).ToListAsync(); if (curEle == null && sGpEle?.Count > 0) { throw new AppFriendlyException("电梯被禁用或未配置", 500); } if (ele % 2 == 1) { //判断电梯组中各电梯任务数 if (curEle == null || sGpEle.FindAll(x => Math.Abs(x.task_nums - curEle.task_nums) % 2 == 1)?.Count > 0) { List sGpDs = await _db.Queryable().Where(it => it.bill_id == sGpEle.First().id).ToListAsync(); if (sGpDs?.Count > 0) { List sGpPoints = await _db.Queryable().Where(it => sGpDs.Select(x => x.point_id).Contains(it.id)).ToListAsync(); string sFEndId = sGpDs.Single(x => x.floor == curEleDs.First().floor).point_id; string eFStartId = sGpDs.Single(x => x.floor == curEleDs.Last().floor).point_id; List sFPoints = await LocPathCalcAlgorithms(pStartId, sFEndId, roads); List elePoints = new(); foreach (WmsElevatorD pt in curEleDs) { WmsPointH? elePoint = sGpPoints.Find(x => x.floor == pt.floor); if (elePoint != null) { elePoints.Add(elePoint); } } List eFPoints = await LocPathCalcAlgorithms(eFStartId, pEndId, roads); _ = elePoints.Remove(elePoints.First()); _ = elePoints.Remove(elePoints.Last()); points.Clear(); points.AddRange(sFPoints); points.AddRange(elePoints); points.AddRange(eFPoints); } } } } } } catch (Exception ex) { Logger.Error("路径算法异常", ex); throw; } return points; } #region PrivateMethods private async Task> LocPathCalcAlgorithms(string pStartId, string pEndId, List roads) { #region dp //List results = new(); //var points = await _db.Queryable().ToListAsync(); //Dictionary isVisited = roads.Select(x => x.startpoint_id).Distinct().ToDictionary(x => x, x => false); //List pointIds = new(); //List codes = new(); //Dp dp = new(); //dynamic obj = new ExpandoObject(); //obj.isArrivedEpoint = false; //dp.DpFunc(roads, pointIds, isVisited, pStartId, pEndId, obj); //foreach (var pid in pointIds) //{ // var point = points.Find(x => x.id == pid); // if (point != null) // { // results.Add(point); // } //} //return results; #endregion #region dijkstra List? points = await _db.Queryable().ToListAsync(); WmsPointH? startObj = points.Find(x => x.id == pStartId); WmsPointH? endObj = points.Find(x => x.id == pEndId); int sIndex = points.IndexOf(startObj); int eIndex = points.IndexOf(endObj); if (eIndex < sIndex) { (eIndex, sIndex) = (sIndex, eIndex); (points[eIndex], points[sIndex]) = (points[sIndex], points[eIndex]); } string[] vexs = points.Select(p => p.id).ToArray(); EData[] edges = new EData[roads.Count]; for (int i = 0; i < edges.Length; i++) { string start = roads[i].startpoint_id; string end = roads[i].endpoint_id; int weight = roads[i].distance; edges[i] = new EData(start, end, weight); } Dijkstra pG = new(vexs, edges); int[] prev = new int[pG.mVexs.Length]; int[] dist = new int[pG.mVexs.Length]; List vertexs = new() { startObj }; pG.CalcDijkstra(sIndex, prev, dist); List pointIds = points.Select(p => p.id).ToList(); Stack result = new(); GetPoints(pointIds, prev, result, eIndex); List results = new() { startObj }; if (points?.Count > 0) { //points.Where(it => result.Contains(it.id)); foreach (string i in result) { WmsPointH? point = points?.Find(x => x.id == i); if (point != null) { results.Add(point); } } } return results; #endregion } /// /// 获取匹配的最短路径节点 /// /// /// /// /// /// /// private void MatchPoint(List results, List roads, List shortestPathPoints, Dictionary isVisited, string pStartId, string pEndId) { List sRoads = roads.Where(x => x.startpoint_id == pStartId).ToList(); for (int j = 0; j < sRoads.Count; j++) { WmsPointH? sPoint = shortestPathPoints.Find(x => x.id == sRoads[j].endpoint_id); if (sPoint != null && isVisited.ContainsKey(sPoint.id) && !isVisited[sPoint.id] && sPoint.id != pEndId) { string code = sPoint.point_code; results.Add(sPoint); isVisited[sPoint.id] = true; MatchPoint(results, roads, shortestPathPoints, isVisited, sPoint.id, pEndId); } } } /// /// 根据终止节点获取最短路径顶点 /// /// /// /// /// private static void GetPoints(List pointIds, int[] prev, Stack result, int eIdx) { int index = eIdx; while (index != 0) { result.Push(pointIds[index]); index = prev[index]; } } private async Task Update(T1 entity, List entities) where T1 : BaseEntity, new() where T2 : BaseEntity, new() { bool isOk = false; try { await _db.Ado.BeginTranAsync(); isOk = await _db.Updateable(entity).ExecuteCommandHasChangeAsync(); if (entities?.Count > 0) { int row = await _db.Storageable(entities).ExecuteCommandAsync(); isOk = row > 0; } await _db.Ado.CommitTranAsync(); } catch (Exception) { await _db.Ado.RollbackTranAsync(); } return isOk; } /// /// 根据明细Id获取出入库明细信息 /// /// 出入库明细 /// 明细输出类 /// 出入库条码 /// 明细Id /// private async Task FetchInOutStockCodesById(string billDId) where TStockD : BaseEntity, new() where TOutput : IInOutStockDetail, new() where TStockCode : BaseEntity, IInOutStockCode, new() { Dictionary dic = await _dictionaryDataService.GetDictionaryByTypeId(WmsWareHouseConst.WMS_INSTOCK_D_BILL_STATUS_TYPEID); List data = await _db.Queryable() .Where(a => a.id == billDId) .Select(a => new TOutput { CodeDetails = SqlFunc.Subqueryable().Where(it => it.bill_d_id == a.id).ToList(), }, true) .Mapper(it => { it.line_status = it.line_status != null && dic.ContainsKey(key: it.line_status) ? dic[it.line_status]?.ToString() : ""; it.warehouse_id = _db.Queryable().Single(y => y.id == it.warehouse_id)?.whname ?? ""; }) .ToListAsync(); return data; } #endregion } }