521 lines
22 KiB
C#
521 lines
22 KiB
C#
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);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
}
|