using System.Diagnostics.CodeAnalysis; using System.Dynamic; using DingTalk.Api.Request; using JNPF; using JNPF.Common.Extension; using JNPF.Common.Manager; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Tnb.Common.Extension; using Tnb.Common.Utils; using Tnb.WarehouseMgr.Entities.Configs; using Tnb.WarehouseMgr.Entities.Consts; using Tnb.WarehouseMgr.Entities.Dto.Inputs; using Tnb.WarehouseMgr.Entities.Enums; using Tnb.WarehouseMgr.Interfaces; namespace Tnb.WarehouseMgr { /// /// 电梯控制业务服务类 /// public class ElevatorControlService : BaseWareHouseService, IElevatorControlService { private readonly ElevatorControlConfiguration _elevatorCtlCfg = new(); private readonly BackgroundService _agvHeartbeatMonitor; private static readonly Dictionary> _fetchStartedStausValue = new(); private readonly bool isFrontDoorBit = false; //是否到前门位 private readonly IServiceProvider _sp; private readonly BackgroundService _backgudSvc; private readonly ICacheManager _cacheMgr; public ElevatorControlService(IServiceProvider sp, BackgroundService bgSvc, ICacheManager cacheMgr) { _elevatorCtlCfg = App.Configuration.Build(); _sp = sp; _backgudSvc = bgSvc; _cacheMgr = cacheMgr; } /// /// 呼梯测试 /// /// [HttpPost, AllowAnonymous] public async Task CallLiftTest() { bool isSuccefuly = false; //var tags = new[] { "SysStatus", "RunStatus", "FloorNo", "DoorStatus", "AGVStatus" }; //(int sysStatus, int runStatus, _, int doorStatus, int agvStatus) = await GetElevatorStatus(devName, tags, CancellationToken.None); ////判断当前楼层是否是放货楼层,如不是则呼叫电梯到当前楼层 //if (sysStatus.ToEnum() == EnumSysStatus.正常状态 && runStatus.ToEnum() == EnumRunStatus.停梯 && // agvStatus.ToEnum() != EnumAgvStatus.AGV运行状态) //{ // if (doorStatus.ToEnum() != EnumDoorStatus.关门到位保持) // { // _ = await SendOpenCloseCmd(devName, (int)EnumAgvControl.前门关门); // } // dynamic 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; // } //} var value = ""; var tags = new[] { "SysStatus", "RunStatus", "FloorNo", "DoorStatus", "AGVStatus" }; try { var statusMap = await RedisHelper.HGetAllAsync("Elevator3"); foreach (var tag in tags) { if (statusMap.ContainsKey(tag)) { var cacheItem = statusMap[tag]; var jo = JObject.Parse(cacheItem); var v = jo.Value("V"); } } //value = await _cacheMgr.GetAsync("Elevator3"); } catch (Exception ex) { throw; } return isSuccefuly; } /// /// 三楼电梯操作流程 /// /// [HttpPost] public dynamic ThreeFloorElevatorFlow() { //test by close door //_agvHeartbeatMonitor.StartAsync(CancellationToken.None); //await SendOpenCloseCmd(4); //await SetAgvControlStatus(1); //监听电梯门是否为关闭的状态 //var dataStatus = 0; //do //{ // dataStatus = await GetTagAsync("DoorStatus"); //} while (dataStatus != 4); //if (dataStatus == 4) //{ // //根据状态确认关闭后,向电梯发送指令从1~3楼 //FloorExecute 楼层触发 // await WriteTagAsync("FloorExecute", 1); // //获取电梯到达3楼的到位信号 // (_, int runStatus, int floorNo) = (-1, -1, -1); // do // { // var multi = await GetElevatorStatus(CancellationToken.None); // runStatus = multi.sysStatus; floorNo = multi.floorNo; // } while (runStatus != 0 && floorNo != 3); // // 控制电梯到达指定楼层默认开门的行为,改为默认不开门 条件:Agv 到达3楼开门位时,为true // if (true)//3楼Agv到达开门位后,向电梯发送开门指令, 默认true 当前 // { // //向电梯发送前门开门指令 // await SendOpenCloseCmd(3); // //获取门状态 是否为 开门到位保持 // var doorStatus = await GetTagAsync("DoorStatus"); // if (doorStatus == 3) //开门到位保持状态 // { // //通知Agv进入电梯,取货 // //向电梯发送关门指令 // await SendOpenCloseCmd(4); // doorStatus = await GetTagAsync("DoorStatus"); // if (doorStatus == 4) //门状态,为关门到位保持 // { // //解锁,Agv状态,将其至为 // var agvStatus = await GetTagAsync("AGVStatus"); // if (agvStatus != 2) // { // await SetAgvControlStatus(0); // } // } // } // } //} return Task.FromResult(0); } private Task SetRequestParameter(string tagName, object value) { TaskCompletionSource tcs = new(); dynamic reqParam = new ExpandoObject(); //reqParam.DevName = _elevatorCtlCfg.DevName; reqParam.TagName = tagName; reqParam.value = value; reqParam.token = _elevatorCtlCfg.token; tcs.SetResult(reqParam); return tcs.Task; } private Task> SetParameter(dynamic obj) { Dictionary parameters = new() { //["DevName"] = _elevatorCtlCfg.DevName, ["token"] = _elevatorCtlCfg.token, }; if (obj is IDictionary dynamicDic) { foreach ((string k, object v) in dynamicDic) { parameters[k] = v?.ToString() ?? string.Empty; } } return Task.FromResult(parameters); } /// /// 向系统发送开关门指令 /// /// /// 开关门指令 /// public async Task SendOpenCloseCmd(string devName, int value) { var flag = false; Dictionary dicCommand = new(StringComparer.OrdinalIgnoreCase) { ["DevName"] = devName, ["token"] = _elevatorCtlCfg.token, ["TagName"] = "DoorExecute", ["Value"] = value.ToString() }; var eleStatusMap = await RedisHelper.HGetAllAsync(devName); try { (int sysStatus, int runStatus, int floorNo, int doorStatus, int agvStatus) = await GetElevatorStatus(devName, CancellationToken.None);//elevator.elevator_code Logger.Information($"【SendOpenCloseCmd】 电梯当前状态->系统状态:{sysStatus.ToEnum()},运行状态:{runStatus},门状态:{doorStatus},Agv状态:{agvStatus},当前楼层:{floorNo}"); //判断Agv电梯是否进入状态 if (agvStatus != (int)EnumAgvStatus.AGV运行状态) _ = await WriteTagAsync(devName, ElevatorConsts.AGVControl, 1); Logger.Information($"【SendOpenCloseCmd】 向系统发送开关门指令 {_elevatorCtlCfg.WriteTagUrl} {JsonConvert.SerializeObject(dicCommand)}"); var res = await HttpClientHelper.GetAsync(_elevatorCtlCfg.WriteTagUrl, pars: dicCommand); Logger.Information($"【SendOpenCloseCmd】 向系统发送开关门指令 结果:{res}"); flag = true; //flag = await RedisHelper.HSetAsync(devName, ElevatorConsts.DoorExecute, value); } catch (Exception ex) { Logger.Information($"【SendOpenCloseCmd】 向系统发送开关门指令发生异常 {ex}"); return false; } return flag; } /// /// 设置Agv控制请求状态 /// /// /// private async Task SetAgvControlStatus(int value) { Dictionary dicCommand = new(StringComparer.OrdinalIgnoreCase) { //["DevName"] = _elevatorCtlCfg.DevName, ["TagName"] = "AGVControl", ["Value"] = value.ToString(), ["token"] = _elevatorCtlCfg.token }; return await HttpClientHelper.GetAsync(_elevatorCtlCfg.WriteTagUrl, pars: dicCommand); } /// /// 向指定的标签属性写入值 /// /// /// /// /// public async Task WriteTagAsync(string devName, string tagName, int value) { Dictionary dicCommand = new(StringComparer.OrdinalIgnoreCase) { ["DevName"] = devName, ["token"] = _elevatorCtlCfg.token, ["TagName"] = tagName, ["Value"] = value.ToString() }; Logger.Information($"【WriteTagAsync】 呼梯指令开始发送 {_elevatorCtlCfg.WriteTagUrl} {JsonConvert.SerializeObject(dicCommand)}"); return await HttpClientHelper.GetAsync(_elevatorCtlCfg.WriteTagUrl, pars: dicCommand); //return await RedisHelper.HSetAsync(devName, tagName, value); } /// /// 根据标签名称获取标签值 /// /// /// /// public async Task GetTagAsync(string devName, string tagName) { /*Dictionary dicCommand = new() { ["DevName"] = devName, ["token"] = _elevatorCtlCfg.token, ["TagName"] = tagName }; string result = await HttpClientHelper.GetAsync(_elevatorCtlCfg.GetTagUrl, pars: dicCommand); JObject jo = JObject.Parse(result); return jo.Value("V");*/ var eleStatusMap = await RedisHelper.HGetAllAsync(devName); if (eleStatusMap.ContainsKey(tagName)) { JObject jo = JObject.Parse(eleStatusMap[tagName]); return jo.Value("Value"); } return -1; } /// /// 获取电梯状态 /// /// /// /// [HttpGet] public async Task<(int sysStatus, int runStatus, int floorNo, int doorStatus, int agvStatus)> GetElevatorStatus(string devName, CancellationToken token) { (int sysStatus, int runStatus, int floorNo, int doorStatus, int agvStatus) multi = (-1, -1, -1, -1, -1); try { Dictionary pars = new() { ["DevName"] = devName, ["Pos"] = "1", ["Count"] = "11", ["token"] = _elevatorCtlCfg.token }; string url = _elevatorCtlCfg.GetTagListUrl; string systemInfo = await HttpClientHelper.GetAsync(url, pars: pars); JObject jo = JObject.Parse(systemInfo); List objs = jo["Items"].Values().ToList(); //if (objs?.Count == 4) { if (objs[0].Value("Name").Equals("SysStatus") && objs[1].Value("Name").Equals("RunStatus") && objs[3].Value("Name").Equals("FloorNo")) { multi = (objs[0].Value("V"), objs[1].Value("V"), objs[3].Value("V"), objs[2].Value("V"), objs[10].Value("V")); } } } catch (Exception ex) { Logger.Error("获取电梯状态错误", ex); throw; } return multi; } /// /// 获取电梯状态 /// /// /// /// /// [HttpPost("GetElevatorStatus")] public async Task<(int sysStatus, int runStatus, int floorNo, int doorStatus, int agvStatus)> GetElevatorStatus([NotNull] string devName, [FromBody] IEnumerable tags, CancellationToken token) { /* async Task GetTag(string tag) { var dicCommand = new Dictionary { ["DevName"] = devName, ["token"] = _elevatorCtlCfg.token, ["TagName"] = tag }; return await HttpClientHelper.GetAsync(_elevatorCtlCfg.GetTagUrl, pars: dicCommand); } */ await s_elevatorStatusSemaphore.WaitAsync(token); var (sysStatus, runStatus, floorNo, doorStatus, agvStatus) = (0, 0, 0, 0, 0); try { //var tasks = tags.Select(tag => GetTag(tag)); //var results = await Task.WhenAll(tasks.Select(task => task)); var statusMap = await RedisHelper.HGetAllAsync(devName); List jos = new(); foreach (var tag in tags) { if (statusMap.ContainsKey(tag)) { jos.Add(JObject.Parse(statusMap[tag])); } } var propertyMap = new Dictionary>() { { ElevatorConsts.SysStatus, v => sysStatus = v }, { ElevatorConsts.RunStatus, v => runStatus = v }, { ElevatorConsts.FloorNo, v => floorNo = v }, { ElevatorConsts.DoorStatus, v => doorStatus = v }, { ElevatorConsts.AGVStatus, v => agvStatus = v }, }; if (jos?.Count > 0) { foreach (var jo in jos) { if (jo == null) { continue; } string? tagName = jo!.Value("TagName"); int value = jo!.Value("Value"); if (propertyMap.TryGetValue(tagName!, out var setProperty)) { setProperty(value); } } } } finally { s_elevatorStatusSemaphore.Release(); } return (sysStatus, runStatus, floorNo, doorStatus, agvStatus); } public async Task> GetELevatorStatusMap(string devName, IEnumerable tags, CancellationToken token) { Task GetTag(string tag) { var dicCommand = new Dictionary { ["DevName"] = devName, ["token"] = _elevatorCtlCfg.token, ["TagName"] = tag }; return HttpClientHelper.GetAsync(_elevatorCtlCfg.GetTagUrl, pars: dicCommand); } var tasks = tags.Select(tag => GetTag(tag)); var results = await Task.WhenAll(tasks); var statusMap = results?.Select(x => JObject.Parse(x)).ToDictionary(x => x.Value("Name"), x => x.Value("V")) ?? default; return statusMap; } /// /// 检查Agv状态 /// /// /// /// public async Task CheckAgvStatus(string devName, CancellationToken cancellationToken) { bool isInAgvStatus = false; int agvStatus = await GetTagAsync(devName, ElevatorConsts.AGVStatus); if (agvStatus.ToEnum() != EnumAgvStatus.AGV运行状态) { dynamic result = await WriteTagAsync(devName, ElevatorConsts.AGVControl, 1); JObject jo = JObject.Parse(result); if (jo != null && jo!.Value("Result")!.Equals("Ok", StringComparison.OrdinalIgnoreCase)) { isInAgvStatus = true; } } return isInAgvStatus; } /// /// 呼梯操作 /// /// 设备名称 /// 呼叫楼层 /// 取消令牌 /// public async Task CallLift(string devName, int floor, CancellationToken cancellationToken) { bool isSuccefuly = false; var tags = new[] { "SysStatus", "RunStatus", "FloorNo", "DoorStatus", "AGVStatus" }; (int sysStatus, int runStatus, _, int doorStatus, int agvStatus) = await GetElevatorStatus(devName, tags, CancellationToken.None); Logger.Information($"【CallLift】 开始呼梯(FloorExecute)到{floor}(此楼层编号经实际楼层转换),当前{devName.Match(@"\d+")}#梯,sysStatus:{sysStatus.ToEnum().ToString()},runStatus:{runStatus.ToEnum().ToString()},doorStatus:{doorStatus.ToEnum().ToString()},agvStatus:{agvStatus.ToEnum().ToString()}"); //判断当前楼层是否是放货楼层,如不是则呼叫电梯到当前楼层 if (sysStatus.ToEnum() == EnumSysStatus.正常状态 && runStatus.ToEnum() == EnumRunStatus.停梯 && agvStatus.ToEnum() == EnumAgvStatus.AGV运行状态) { if (doorStatus.ToEnum() != EnumDoorStatus.关门到位保持) { _ = await SendOpenCloseCmd(devName, (int)EnumAgvControl.前门关门); } dynamic result = await WriteTagAsync(devName, ElevatorConsts.FloorExecute, floor); //呼叫电梯到指定楼层 Logger.Information($"【CallLift】 呼梯结果:{JsonConvert.SerializeObject(result)}"); if (!string.IsNullOrEmpty(result)) { JObject jo = JObject.Parse(result); isSuccefuly = jo?.Value("Result")?.Equals("Ok", StringComparison.OrdinalIgnoreCase) ?? false; } } return isSuccefuly; } private List> ParallelWriteTagAsync(CloseElevatorInput input) { var tasks = new List>(input.devNames.Count()); foreach (var devName in input.devNames) { tasks.Add(WriteTagAsync(devName, ElevatorConsts.AGVControl, input.value)); } return tasks; } public async Task CloseElevatorControl(CloseElevatorInput input) { if (input.devNames == null || !input.devNames.Any()) { throw new ArgumentNullException(nameof(input.devNames)); } var tasks = ParallelWriteTagAsync(input); var writeRes = await Task.WhenAll(tasks); //var timedTaskSvc = _backgudSvc as TimedTaskBackgroundService; //if (timedTaskSvc != null) //{ // if (input.flag.Equals("close", StringComparison.OrdinalIgnoreCase)) // { // _ = timedTaskSvc.CloseAgvHeartbeat(input.devNames); // } // else // { // _ = timedTaskSvc.OpenAgvHeartbeat(input.devNames); // } //} } } }