Files
tnb.server/WarehouseMgr/Tnb.WarehouseMgr/ElevatorControlService.cs

521 lines
22 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
{
/// <summary>
/// 电梯控制业务服务类
/// </summary>
public class ElevatorControlService : BaseWareHouseService, IElevatorControlService
{
private readonly ElevatorControlConfiguration _elevatorCtlCfg = new();
private readonly BackgroundService _agvHeartbeatMonitor;
private static readonly Dictionary<string, Func<BackgroundService, bool>> _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<ElevatorControlConfiguration>();
_sp = sp;
_backgudSvc = bgSvc;
_cacheMgr = cacheMgr;
}
/// <summary>
/// 呼梯测试
/// </summary>
/// <returns></returns>
[HttpPost, AllowAnonymous]
public async Task<dynamic> 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>() == EnumSysStatus.正常状态 && runStatus.ToEnum<EnumRunStatus>() == EnumRunStatus.停梯 &&
// agvStatus.ToEnum<EnumAgvStatus>() != EnumAgvStatus.AGV运行状态)
//{
// if (doorStatus.ToEnum<EnumDoorStatus>() != 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<string>("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<int>("V");
}
}
//value = await _cacheMgr.GetAsync("Elevator3");
}
catch (Exception ex)
{
throw;
}
return isSuccefuly;
}
/// <summary>
/// 三楼电梯操作流程
/// </summary>
/// <returns></returns>
[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<object> SetRequestParameter(string tagName, object value)
{
TaskCompletionSource<object> 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<Dictionary<string, string>> SetParameter(dynamic obj)
{
Dictionary<string, string> parameters = new()
{
//["DevName"] = _elevatorCtlCfg.DevName,
["token"] = _elevatorCtlCfg.token,
};
if (obj is IDictionary<string, object> dynamicDic)
{
foreach ((string k, object v) in dynamicDic)
{
parameters[k] = v?.ToString() ?? string.Empty;
}
}
return Task.FromResult(parameters);
}
/// <summary>
/// 向系统发送开关门指令
/// </summary>
/// <param name="devName"></param>
/// <param name="value">开关门指令</param>
/// <returns></returns>
public async Task<bool> SendOpenCloseCmd(string devName, int value)
{
var flag = false;
Dictionary<string, string> 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<EnumSysStatus>()},运行状态:{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;
}
/// <summary>
/// 设置Agv控制请求状态
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
private async Task<dynamic> SetAgvControlStatus(int value)
{
Dictionary<string, string> dicCommand = new(StringComparer.OrdinalIgnoreCase)
{
//["DevName"] = _elevatorCtlCfg.DevName,
["TagName"] = "AGVControl",
["Value"] = value.ToString(),
["token"] = _elevatorCtlCfg.token
};
return await HttpClientHelper.GetAsync(_elevatorCtlCfg.WriteTagUrl, pars: dicCommand);
}
/// <summary>
/// 向指定的标签属性写入值
/// </summary>
/// <param name="devName"></param>
/// <param name="tagName"></param>
/// <param name="value"></param>
/// <returns></returns>
public async Task<dynamic> WriteTagAsync(string devName, string tagName, int value)
{
Dictionary<string, string> 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);
}
/// <summary>
/// 根据标签名称获取标签值
/// </summary>
/// <param name="devName"></param>
/// <param name="tagName"></param>
/// <returns></returns>
public async Task<int> GetTagAsync(string devName, string tagName)
{
/*Dictionary<string, string> 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<int>("V");*/
var eleStatusMap = await RedisHelper.HGetAllAsync(devName);
if (eleStatusMap.ContainsKey(tagName))
{
JObject jo = JObject.Parse(eleStatusMap[tagName]);
return jo.Value<int>("Value");
}
return -1;
}
/// <summary>
/// 获取电梯状态
/// </summary>
/// <param name="devName"></param>
/// <param name="token"></param>
/// <returns></returns>
[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<string, string> 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<JObject?> objs = jo["Items"].Values<JObject>().ToList();
//if (objs?.Count == 4)
{
if (objs[0].Value<string>("Name").Equals("SysStatus")
&& objs[1].Value<string>("Name").Equals("RunStatus")
&& objs[3].Value<string>("Name").Equals("FloorNo"))
{
multi = (objs[0].Value<int>("V"), objs[1].Value<int>("V"), objs[3].Value<int>("V"), objs[2].Value<int>("V"), objs[10].Value<int>("V"));
}
}
}
catch (Exception ex)
{
Logger.Error("获取电梯状态错误", ex);
throw;
}
return multi;
}
/// <summary>
/// 获取电梯状态
/// </summary>
/// <param name="devName"></param>
/// <param name="tags"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpPost("GetElevatorStatus")]
public async Task<(int sysStatus, int runStatus, int floorNo, int doorStatus, int agvStatus)> GetElevatorStatus([NotNull] string devName, [FromBody] IEnumerable<string> tags, CancellationToken token)
{
/* async Task<string> GetTag(string tag)
{
var dicCommand = new Dictionary<string, string>
{
["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<JObject> jos = new();
foreach (var tag in tags)
{
if (statusMap.ContainsKey(tag))
{
jos.Add(JObject.Parse(statusMap[tag]));
}
}
var propertyMap = new Dictionary<string, Action<int>>()
{
{ 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<string>("TagName");
int value = jo!.Value<int>("Value");
if (propertyMap.TryGetValue(tagName!, out var setProperty))
{
setProperty(value);
}
}
}
}
finally
{
s_elevatorStatusSemaphore.Release();
}
return (sysStatus, runStatus, floorNo, doorStatus, agvStatus);
}
public async Task<Dictionary<string, int>> GetELevatorStatusMap(string devName, IEnumerable<string> tags, CancellationToken token)
{
Task<string> GetTag(string tag)
{
var dicCommand = new Dictionary<string, string>
{
["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<string>("Name"), x => x.Value<int>("V")) ?? default;
return statusMap;
}
/// <summary>
/// 检查Agv状态
/// </summary>
/// <param name="devName"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<bool> CheckAgvStatus(string devName, CancellationToken cancellationToken)
{
bool isInAgvStatus = false;
int agvStatus = await GetTagAsync(devName, ElevatorConsts.AGVStatus);
if (agvStatus.ToEnum<EnumAgvStatus>() != EnumAgvStatus.AGV运行状态)
{
dynamic result = await WriteTagAsync(devName, ElevatorConsts.AGVControl, 1);
JObject jo = JObject.Parse(result);
if (jo != null && jo!.Value<string>("Result")!.Equals("Ok", StringComparison.OrdinalIgnoreCase))
{
isInAgvStatus = true;
}
}
return isInAgvStatus;
}
/// <summary>
/// 呼梯操作
/// </summary>
/// <param name="devName">设备名称</param>
/// <param name="floor">呼叫楼层</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns></returns>
public async Task<bool> 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<EnumSysStatus>().ToString()},runStatus:{runStatus.ToEnum<EnumRunStatus>().ToString()},doorStatus:{doorStatus.ToEnum<EnumDoorStatus>().ToString()},agvStatus:{agvStatus.ToEnum<EnumAgvStatus>().ToString()}");
//判断当前楼层是否是放货楼层,如不是则呼叫电梯到当前楼层
if (sysStatus.ToEnum<EnumSysStatus>() == EnumSysStatus. && runStatus.ToEnum<EnumRunStatus>() == EnumRunStatus. &&
agvStatus.ToEnum<EnumAgvStatus>() == EnumAgvStatus.AGV运行状态)
{
if (doorStatus.ToEnum<EnumDoorStatus>() != 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<string>("Result")?.Equals("Ok", StringComparison.OrdinalIgnoreCase) ?? false;
}
}
return isSuccefuly;
}
private List<Task<dynamic>> ParallelWriteTagAsync(CloseElevatorInput input)
{
var tasks = new List<Task<dynamic>>(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);
// }
//}
}
}
}