From 02b718157d70bf875a33ccff95464f2d3874b7bc Mon Sep 17 00:00:00 2001 From: "yang.lee" Date: Fri, 13 Oct 2023 09:38:05 +0800 Subject: [PATCH] =?UTF-8?q?Wms=20=E9=A2=84=E4=BB=BB=E5=8A=A1=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=EF=BC=8C=E6=96=B0=E5=A2=9EAgv=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=EF=BC=8C=E5=91=BC=E6=A2=AF=E4=BB=BB=E5=8A=A1=EF=BC=8C=E7=94=B5?= =?UTF-8?q?=E6=A2=AF=E4=BB=BB=E5=8A=A1=E6=9D=A1=E4=BB=B6=E8=BF=87=E6=BB=A4?= =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E5=8C=BA=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Entity/WmsElevatorH.part.cs | 16 ++++ .../AgvHeartbeatMonitorService.cs | 13 ++- .../Tnb.WarehouseMgr/DeviceProviderService.cs | 46 +++++++---- .../ElevatorControlService.cs | 80 ++++++------------- .../TimedTaskBackgroundService.cs | 53 +++++++----- .../Tnb.WarehouseMgr/WareHouseService.cs | 41 ++++++++-- apihost/Tnb.API.Entry/Startup.cs | 2 +- 7 files changed, 151 insertions(+), 100 deletions(-) create mode 100644 WarehouseMgr/Tnb.WarehouseMgr.Entities/Entity/WmsElevatorH.part.cs diff --git a/WarehouseMgr/Tnb.WarehouseMgr.Entities/Entity/WmsElevatorH.part.cs b/WarehouseMgr/Tnb.WarehouseMgr.Entities/Entity/WmsElevatorH.part.cs new file mode 100644 index 00000000..69a3bc44 --- /dev/null +++ b/WarehouseMgr/Tnb.WarehouseMgr.Entities/Entity/WmsElevatorH.part.cs @@ -0,0 +1,16 @@ +using System.Security.Principal; +using JNPF.Common.Contracts; +using JNPF.Common.Security; +using SqlSugar; + +namespace Tnb.WarehouseMgr.Entities; + +/// +/// WMS电梯设定主表 +/// + +public partial class WmsElevatorH +{ + [SugarColumn(IsIgnore = true)] + public int end_floor { get; set; } +} diff --git a/WarehouseMgr/Tnb.WarehouseMgr/AgvHeartbeatMonitorService.cs b/WarehouseMgr/Tnb.WarehouseMgr/AgvHeartbeatMonitorService.cs index 897e4661..ae3d71ac 100644 --- a/WarehouseMgr/Tnb.WarehouseMgr/AgvHeartbeatMonitorService.cs +++ b/WarehouseMgr/Tnb.WarehouseMgr/AgvHeartbeatMonitorService.cs @@ -37,8 +37,17 @@ namespace Tnb.WarehouseMgr { while (!stoppingToken.IsCancellationRequested) { - await HttpClientHelper.GetAsync(_elevatorControlConfiguration.WriteTagUrl, parameter); - await Task.Delay(TimeSpan.FromMinutes(1)); + try + { + var result = await HttpClientHelper.GetAsync(_elevatorControlConfiguration.WriteTagUrl, pars: parameter); + await Console.Out.WriteLineAsync($"心跳检测结果:{result}"); + await Task.Delay(TimeSpan.FromSeconds(5)); + } + catch (Exception ex) + { + + throw; + } } }); diff --git a/WarehouseMgr/Tnb.WarehouseMgr/DeviceProviderService.cs b/WarehouseMgr/Tnb.WarehouseMgr/DeviceProviderService.cs index bcb8303d..f2ef4cd2 100644 --- a/WarehouseMgr/Tnb.WarehouseMgr/DeviceProviderService.cs +++ b/WarehouseMgr/Tnb.WarehouseMgr/DeviceProviderService.cs @@ -83,6 +83,8 @@ namespace Tnb.WarehouseMgr [HttpPost, NonUnify, AllowAnonymous] public async Task LoadConfirm(ConfirmInput input) { + Log.Information("取货确认.................."); + try { var elevator = await _db.Queryable().LeftJoin((a, b) => a.id == b.bill_id) @@ -124,35 +126,51 @@ namespace Tnb.WarehouseMgr /// /// [HttpPost, NonUnify, AllowAnonymous] - public async Task UnloadConfirm(ConfirmInput input) + public async Task UnloadConfirm(ConfirmInput input)// { + Log.Information("放货确认.................."); try { //根据Agv传递的参数获取,对应的电梯 var elevator = await _db.Queryable().LeftJoin((a, b) => a.id == b.bill_id) - .LeftJoin((a, b, c) => b.location_id == c.startlocation_id) - .LeftJoin((a, b, c, d) => c.startlocation_id == d.location_id) - .Where((a, b, c, d) => d.point_code == input.sourceName && c.bill_code == input.taskCode) + .LeftJoin((a, b, c) => b.location_id == c.endlocation_id) + .LeftJoin((a, b, c, d) => c.endlocation_id == d.location_id) + .Where((a, b, c, d) => d.point_code == input.targetName && c.bill_code == input.taskCode) + .Select((a, b, c, d) => new WmsElevatorH { end_floor = SqlFunc.ToInt32(c.end_floor) }, true) .FirstAsync(); - (int sysStatus, int runStatus, int floorNo, int doorStatus, int agvStatus) = await _elevatorControlService.GetElevatorStatus(elevator.elevator_code, CancellationToken.None); - Log.Information($"电梯当前状态->系统状态:{sysStatus.ToEnum().ToString()},运行状态:{runStatus.ToEnum().ToString()},Agv状态:{agvStatus.ToEnum().ToString()},当前楼层:{floorNo}"); + (int sysStatus, int runStatus, int floorNo, int doorStatus, int agvStatus) = await _elevatorControlService.GetElevatorStatus("Elevator1", CancellationToken.None);//elevator.elevator_code + Log.Information($"电梯当前状态->系统状态:{sysStatus.ToEnum().ToString()},运行状态:{runStatus},门状态:{doorStatus},Agv状态:{agvStatus},当前楼层:{floorNo}"); //判断Agv电梯是否进入状态 - if (agvStatus.ToEnum() != EnumAgvStatus.AGV运行状态) + if (agvStatus != (int)EnumAgvStatus.AGV运行状态) { - await _elevatorControlService.WriteTagAsync(elevator.elevator_code, ElevatorConsts.AGVControl, 1); + await _elevatorControlService.WriteTagAsync("Elevator1", ElevatorConsts.AGVControl, 1); } + Log.Information("目前正常"); + //电梯到达目标楼层后,判断当前电梯门状态是否为开门到位保持状态 - if (doorStatus.ToEnum() != EnumDoorStatus.开门到位保持) + if (doorStatus != (int)EnumDoorStatus.开门到位保持) { - await _elevatorControlService.SendOpenCloseCmd(elevator.elevator_code, 3); //发送电梯前门开门指令 + await _elevatorControlService.SendOpenCloseCmd("Elevator1", 3); //发送电梯前门开门指令 } - if (sysStatus.ToEnum() == EnumSysStatus.正常状态 && runStatus.ToEnum() == EnumRunStatus.停梯 - && doorStatus.ToEnum() == EnumDoorStatus.开门到位保持 && floorNo == 4) + Log.Information("fadsfadsfadsfadsfdsa"); + if (sysStatus == (int)EnumSysStatus.正常状态 && runStatus == (int)EnumRunStatus.停梯 + && doorStatus == (int)EnumDoorStatus.开门到位保持) { - elevator.current_floor = floorNo; - await _db.Updateable(elevator).UpdateColumns(it => it.current_floor).ExecuteCommandAsync(); + Log.Information("进入开门状态,马上要成功了"); + try + { + elevator.current_floor = elevator.end_floor; + await _db.Updateable(elevator).UpdateColumns(it => it.current_floor).ExecuteCommandAsync(); + + } + catch (Exception ex) + { + Log.Error("更新延迟队列异常",ex); + throw; + } + return await ToApiResult(HttpStatusCode.OK, "成功"); } return await ToApiResult(HttpStatusCode.InternalServerError, "电梯还未开门,请重试!"); diff --git a/WarehouseMgr/Tnb.WarehouseMgr/ElevatorControlService.cs b/WarehouseMgr/Tnb.WarehouseMgr/ElevatorControlService.cs index 89fd7d80..94f74050 100644 --- a/WarehouseMgr/Tnb.WarehouseMgr/ElevatorControlService.cs +++ b/WarehouseMgr/Tnb.WarehouseMgr/ElevatorControlService.cs @@ -40,64 +40,30 @@ namespace Tnb.WarehouseMgr } /// - /// 一楼电梯操作流程 + /// 呼梯测试 /// /// [HttpPost] - public async Task FirstFloorElevatorFlow(string value) + public async Task CallLiftTest(string devName, int floor) { - //CancellationTokenSource cts = new CancellationTokenSource();//扩展用 - ////如果不是1楼,判断电梯状态,如果电梯是空闲的向电梯发送一个到一楼的指令 - //var wirteRes = await SetAgvControlStatus(1); - //JObject jo = JObject.Parse(wirteRes); - //var propName = "IsStarted"; - //if (!_fetchStartedStausValue.TryGetValue(propName, out var func)) - //{ - // var isStartedProp = _agvHeartbeatMonitor.GetType().GetProperty(propName); - // var paramExp = Expression.Parameter(typeof(BackgroundService), "_agvHeartbeatMonitor"); - // var propExp = Expression.Property(Expression.ConvertChecked(paramExp, isStartedProp.DeclaringType), isStartedProp); - // var body = Expression.Lambda>(propExp, paramExp); - // func = body.Compile(); - // _fetchStartedStausValue[propName] = func; - //} - //var isStarted = func(_agvHeartbeatMonitor); - //if (!isStarted) - //{ - // _agvHeartbeatMonitor.StartAsync(cts.Token); - //} - - ////获取电梯状态 - //(int sysStatus, int runStatus, int floorNo) multi = (-1, -1, -1); - //do - //{ - // //multi = await GetElevatorStatus(CancellationToken.None); - // await Task.Delay(2000); - //} while (multi.sysStatus != 3 && multi.runStatus != 0); - //if (multi.sysStatus == 3 && multi.runStatus == 0 && multi.floorNo != 1) - //{ - // await WriteTagAsync("FloorExecute", 1); - - - // if (true) //此处为Agv到一楼开门位,通知操作目前默认为true - // { - - // //向电梯发送前门开门指令 - // await SendOpenCloseCmd(3); - // //获取门状态 是否为 开门到位保持 - // var doorStatus = await GetTagAsync("DoorStatus"); - // if (doorStatus == 3) - // { - // //通知Agv进入电梯,放货,默认为true - // if (true) - // { - // //向电梯发送关门指令 - // await SendOpenCloseCmd(4); - // } - // } - - // } - //} - return Task.FromResult(0); + var isSuccefuly = false; + (int sysStatus, int runStatus, int floorNo, int doorStatus, int agvStatus) = await GetElevatorStatus(devName, CancellationToken.None); + //判断当前楼层是否是放货楼层,如不是则呼叫电梯到当前楼层 + if (sysStatus.ToEnum() == EnumSysStatus.正常状态 && runStatus.ToEnum() == EnumRunStatus.停梯 && + agvStatus.ToEnum() != EnumAgvStatus.AGV运行状态) + { + if (doorStatus.ToEnum() != EnumDoorStatus.关门到位保持) + { + await SendOpenCloseCmd(devName, (int)EnumAgvControl.前门关门); + } + var result = await WriteTagAsync(devName, ElevatorConsts.FloorExecute, floor); //呼叫电梯到指定楼层 + if (!string.IsNullOrEmpty(result)) + { + JObject jo = JObject.Parse(result); + isSuccefuly = jo?.Value("Result")?.Equals("Ok", StringComparison.OrdinalIgnoreCase) ?? false; + } + } + return isSuccefuly; } /// /// 三楼电梯操作流程 @@ -327,17 +293,17 @@ namespace Tnb.WarehouseMgr (int sysStatus, int runStatus, int floorNo, int doorStatus, int agvStatus) = await GetElevatorStatus(devName, CancellationToken.None); //判断当前楼层是否是放货楼层,如不是则呼叫电梯到当前楼层 if (sysStatus.ToEnum() == EnumSysStatus.正常状态 && runStatus.ToEnum() == EnumRunStatus.停梯 && - agvStatus.ToEnum() != EnumAgvStatus.AGV运行状态 && floorNo != 4) + agvStatus.ToEnum() != EnumAgvStatus.AGV运行状态) { if (doorStatus.ToEnum() != EnumDoorStatus.关门到位保持) { await SendOpenCloseCmd(devName, (int)EnumAgvControl.前门关门); } - var result = await WriteTagAsync(devName, ElevatorConsts.FloorExecute, floor); //呼叫电梯到4楼 + var result = await WriteTagAsync(devName, ElevatorConsts.FloorExecute, floor); //呼叫电梯到指定楼层 if (!string.IsNullOrEmpty(result)) { JObject jo = JObject.Parse(result); - isSuccefuly = jo?.Value("Result").Equals("Ok", StringComparison.OrdinalIgnoreCase) ?? false; + isSuccefuly = jo?.Value("Result")?.Equals("Ok", StringComparison.OrdinalIgnoreCase) ?? false; } } return isSuccefuly; diff --git a/WarehouseMgr/Tnb.WarehouseMgr/TimedTaskBackgroundService.cs b/WarehouseMgr/Tnb.WarehouseMgr/TimedTaskBackgroundService.cs index 195cae49..a1babb65 100644 --- a/WarehouseMgr/Tnb.WarehouseMgr/TimedTaskBackgroundService.cs +++ b/WarehouseMgr/Tnb.WarehouseMgr/TimedTaskBackgroundService.cs @@ -26,8 +26,11 @@ using Microsoft.Extensions.Options; using Natasha.CSharp; using NetTaste; using Tnb.Common.Extension; +using Tnb.Common.Utils; using Tnb.WarehouseMgr.Entities.Attributes; +using Tnb.WarehouseMgr.Entities.Configs; using Tnb.WarehouseMgr.Entities.Dto.Inputs; +using Tnb.WarehouseMgr.Entities.Dto.Outputs; using Tnb.WarehouseMgr.Entities.Enums; using Tnb.WarehouseMgr.Entities.Exceptions; using Tnb.WarehouseMgr.Interfaces; @@ -85,14 +88,26 @@ namespace Tnb.WarehouseMgr { _eventPublisher = App.GetRequiredService(); var whSvc = App.GetRequiredService(); - TimedTask(token => whSvc.GenTaskExecute(token), stoppingToken, 3); + TimedTask(token => whSvc.GenTaskExecute(token), stoppingToken, 1); //齐套出库 - var kittingOutService = App.GetRequiredService(); - TimedTask(token => kittingOutService.KittingOutByAdd(token), stoppingToken, 3); - TimedTask(token => kittingOutService.KittingOutByIsToBeShipped(token), stoppingToken, 3); + //var kittingOutService = App.GetRequiredService(); + //TimedTask(token => kittingOutService.KittingOutByAdd(token), stoppingToken, 1); + //TimedTask(token => kittingOutService.KittingOutByIsToBeShipped(token), stoppingToken, 1); //齐套分拣 - var setSortingService = App.GetRequiredService(); - TimedTask(token => setSortingService.PackSortingByAdd(token), stoppingToken, 3); + //var setSortingService = App.GetRequiredService(); + //TimedTask(token => setSortingService.PackSortingByAdd(token), stoppingToken, 1); + + var _elevatorControlConfiguration = App.Configuration.Build(); + TimedTask(async token => + { + var parameter = new Dictionary(); + parameter["DevName"] = _elevatorControlConfiguration.DevName; + parameter["TagName"] = "AGVKeepalive"; + parameter["Value"] = "123"; + parameter["token"] = _elevatorControlConfiguration.token; + var result = await HttpClientHelper.GetAsync(_elevatorControlConfiguration.WriteTagUrl, pars: parameter); + await Console.Out.WriteLineAsync($"心跳检测结果:{result}"); + },stoppingToken,30,TimeSpanUnit.Seconds); //最低库存检查 var transferSignService = App.GetRequiredService(); TimedTask(token => transferSignService.IsMinStorage(token), stoppingToken, 30, TimeSpanUnit.Minutes); @@ -115,19 +130,19 @@ namespace Tnb.WarehouseMgr { if (ex is TimedTaskException timedTaskEx and not null) { - await _eventPublisher.PublishAsync(new LogEventSource("Log:CreateExLog", timedTaskEx.options!, new SysLogEntity - { - Id = SnowflakeIdHelper.NextId(), - Category = 4, - UserId = timedTaskEx.UserId, - UserName = timedTaskEx.UserName, - IPAddress = NetHelper.Ip, - RequestURL = timedTaskEx.RequestURL, - RequestMethod = timedTaskEx.RequestMethod, - Json = timedTaskEx + "\n" + timedTaskEx.InnerException?.StackTrace + "\n" + timedTaskEx?.TargetSite?.GetParameters().ToString(), - //PlatForm = string.Format("{0}-{1}", userAgent.OS.ToString(), userAgent.RawValue), - CreatorTime = DateTime.Now - })); + //await _eventPublisher.PublishAsync(new LogEventSource("Log:CreateExLog", timedTaskEx.options!, new SysLogEntity + //{ + // Id = SnowflakeIdHelper.NextId(), + // Category = 4, + // UserId = timedTaskEx.UserId, + // UserName = timedTaskEx.UserName, + // IPAddress = NetHelper.Ip, + // RequestURL = timedTaskEx.RequestURL, + // RequestMethod = timedTaskEx.RequestMethod, + // Json = timedTaskEx + "\n" + timedTaskEx.InnerException?.StackTrace + "\n" + timedTaskEx?.TargetSite?.GetParameters().ToString(), + // //PlatForm = string.Format("{0}-{1}", userAgent.OS.ToString(), userAgent.RawValue), + // CreatorTime = DateTime.Now + //})); } }); await TaskDelay(timeType, interval); diff --git a/WarehouseMgr/Tnb.WarehouseMgr/WareHouseService.cs b/WarehouseMgr/Tnb.WarehouseMgr/WareHouseService.cs index a5449e30..9f3238a4 100644 --- a/WarehouseMgr/Tnb.WarehouseMgr/WareHouseService.cs +++ b/WarehouseMgr/Tnb.WarehouseMgr/WareHouseService.cs @@ -342,15 +342,21 @@ namespace Tnb.WarehouseMgr //呼梯操作 //获取目标库位为电梯库位的任务 - var endLocCodes = disTasks.Select(it => (it.endlocation_code, it.device_id, it.id, it.start_floor)).Where(it => it.endlocation_code.StartsWith("DT", StringComparison.OrdinalIgnoreCase)).ToList(); + var endLocCodes = disTasks + .Where(it => it.endlocation_code.StartsWith("DT", StringComparison.OrdinalIgnoreCase) && + !it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase) + ).Select(it => (it.endlocation_code, it.device_id, it.id, it.start_floor)).ToList(); if (endLocCodes?.Count > 0) { await CallingLanding(endLocCodes); } //执行电梯任务 var elevatorTasks = disTasks.Where(it => it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase)).ToList(); + Log.Information($"当前电梯任务数:{elevatorTasks?.Count ?? 0}"); + Log.Information("准备执行电梯任务"); if (elevatorTasks?.Count > 0) { + Log.Information("执行电梯任务"); foreach (var elevatorTask in elevatorTasks) { await ExecuteTargetFloorTask(elevatorTask); @@ -358,7 +364,11 @@ namespace Tnb.WarehouseMgr } ////调用AGV创建任务链接口 - await AgvDispatch(disTasks, agvCts.Token); + var agvTasks = disTasks.Where(it => !it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase)).ToList(); + if (agvTasks?.Count > 0) + { + await AgvDispatch(agvTasks, agvCts.Token); + } } } @@ -384,6 +394,7 @@ namespace Tnb.WarehouseMgr /// private async Task CallingLanding(List<(string endlocation_code, string device_id, string id, string floorNO)> endLocCodes) { + Log.Information($"开始呼梯操作............."); try { var item = endLocCodes.FirstOrDefault(); @@ -391,6 +402,8 @@ namespace Tnb.WarehouseMgr var devName = _eleCtlCfg.DevName; { var agvStatus = await _elevatorControlService.GetTagAsync(devName, ElevatorConsts.AGVControl); + Log.Information($"当前Agv状态:{agvStatus.ToEnum().ToString()}"); + //判断当前设备是否为运行状态,不是则进入Agv电梯控制状态 if (agvStatus.ToEnum() != EnumAgvStatus.AGV运行状态) { @@ -398,9 +411,16 @@ namespace Tnb.WarehouseMgr } } { + (int sysStatus, int runStatus, int curFloorNo, int doorStatus, int agvStatus) = await _elevatorControlService.GetElevatorStatus(devName, CancellationToken.None); + foreach (var (_, devId, disTaskId, floorNO) in endLocCodes) { + Log.Information($"任务开始目标楼层为:{floorNO}"); + var floorN = await GetRealFloor(floorNO.ParseToInt()); + + Log.Information($"实际目标楼层为:{floorN}"); + WmsElevatorUnexecute elevatorQueueItem = new() { distask_id = disTaskId, @@ -412,11 +432,14 @@ namespace Tnb.WarehouseMgr create_time = DateTime.Now }; var elevatorQueue = await _db.Queryable().Where(it => it.elevator_id == devId && it.task_status == "执行中").ToListAsync(); - if ((elevatorQueue.IsNull() || elevatorQueue.Count < 1)) + if ((elevatorQueue.IsNull() || elevatorQueue.Count < 1) && floorNO.ParseToInt() != curFloorNo) { - elevatorQueueItem.task_status = "执行中"; - await _elevatorControlService.CallLift(devName, floorNO.ParseToInt(), CancellationToken.None); + elevatorQueueItem.task_status = "执行中"; + var callLiftRes = await _elevatorControlService.CallLift(devName, floorN, CancellationToken.None); + string successful = "成功", fail = "失败"; + var callLiftResult = callLiftRes ? successful : fail; + Log.Information($"呼梯结果:{callLiftResult}"); } //如果当前电梯有任务在做,将当前呼梯任务放入待执行队列 @@ -461,21 +484,25 @@ namespace Tnb.WarehouseMgr private async Task ExecuteTargetFloorTask(WmsDistaskH disTask) { //收到放货确认通知,向电梯发送到3楼的指令 + Log.Information($"开始执行电梯任务,任务ID:{disTask.id}"); try { - var elevatorQueueItem = await _db.Queryable().FirstAsync(it => disTask.id == it.distask_id && it.task_status == "执行中"); + var elevatorQueueItem = await _db.Queryable().FirstAsync(it => it.task_status == "执行中"); if (elevatorQueueItem != null) { //var disTask = disTasks.Find(x => x.id == elevatorQueueItem.distask_id); //if (disTask?.status == WmsWareHouseConst.TASK_BILL_STATUS_COMPLE_ID) { var doorStatus = -1; - await _elevatorControlService.SendOpenCloseCmd(elevatorQueueItem.elevator_code, 4); //向电梯发送前门关门指令 + var closeDoorRes = await _elevatorControlService.SendOpenCloseCmd(elevatorQueueItem.elevator_code, 4); //向电梯发送前门关门指令 + Log.Information($"关门结果:{closeDoorRes}"); do { doorStatus = await _elevatorControlService.GetTagAsync(elevatorQueueItem.elevator_code, ElevatorConsts.DoorStatus); } while (doorStatus != 4); var floor = await GetRealFloor(disTask.end_floor.ParseToInt()); + Log.Information($"目标楼层:{floor}"); + Log.Information($"当前门状态:{doorStatus}"); //发送到目标楼的指令 var reuslt = await _elevatorControlService.WriteTagAsync(elevatorQueueItem.elevator_code, ElevatorConsts.FloorExecute, floor); //电梯任务手动执行任务状态上报 diff --git a/apihost/Tnb.API.Entry/Startup.cs b/apihost/Tnb.API.Entry/Startup.cs index d5290c25..1647effe 100644 --- a/apihost/Tnb.API.Entry/Startup.cs +++ b/apihost/Tnb.API.Entry/Startup.cs @@ -66,7 +66,7 @@ public class Startup : AppStartup services.AddTaskMessageNotify(); //定时任务 - //services.AddHostedService(); + services.AddHostedService(); //Agv心跳监听服务 //services.AddHostedService();