iFactory.Godot/Artists/Scripts/Factory/SCADAService.cs

194 lines
5.5 KiB
C#
Raw Normal View History

2023-06-12 15:51:41 +08:00
using Godot;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using BITKit.Core.Entites;
2023-06-13 14:43:03 +08:00
using BITKit.Packages.Core.LazyLoad;
2023-06-12 15:51:41 +08:00
using Cysharp.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace BITKit;
/// <summary>
/// 单例SCADA Service,从http接口获取json后解析为指定数据
/// </summary>
public partial class SCADAService : Node
{
2023-06-16 17:08:59 +08:00
public const string _CurrentAngle="CurrentAngle";
public const string _CurrentRotation="CurrentRotation";
2023-06-12 15:51:41 +08:00
/// <summary>
/// 在构造函数中注入依赖
/// </summary>
public SCADAService()
{
2023-06-13 14:43:03 +08:00
BITApp.ServiceCollection.AddSingleton(this);
2023-06-12 15:51:41 +08:00
}
/// <summary>
/// 获取json的Url
/// </summary>
2023-06-15 21:52:13 +08:00
private readonly DataReference<string> _url = new("SCADA_GetInfos");
2023-06-12 15:51:41 +08:00
/// <summary>
/// 是否固定控制的Entity如果固定只会刷新一次如果不固定每帧都会刷新
/// </summary>
[Export]
private bool fixedEntities;
/// <summary>
/// 已加载的Entity
/// </summary>
private readonly Dictionary<string, IEntity> _entities = new();
/// <summary>
/// 最大并行请求数量
/// </summary>
2023-06-13 14:43:03 +08:00
private readonly LimitTimes limitConcurrent =new (1);
2023-06-12 15:51:41 +08:00
/// <summary>
/// 请求数据的间隔
/// </summary>
private readonly IntervalTimer _intervalTimer = new(1);
/// <summary>
/// 取消令牌用于取消Http Get
/// </summary>
private CancellationToken _cancellationToken;
/// <summary>
/// http客户端
/// </summary>
2023-06-13 14:43:03 +08:00
private readonly ServiceLoader<System.Net.Http.HttpClient> httpClient=new();
2023-06-12 15:51:41 +08:00
/// <summary>
/// 获取Entity并加载依赖
/// </summary>
public override async void _Ready()
{
_cancellationToken = new CancellationToken();
if (!fixedEntities) return;
await UniTask.Yield();
LoadAllEntities();
BIT4Log.Log<SCADAService>($"已加载{_entities.Count}个设备");
2023-06-15 21:52:13 +08:00
if (string.IsNullOrEmpty(_url))
{
BIT4Log.Warnning("SCADA Url为空");
}
else
{
BIT4Log.Log<SCADAService>($"已找到SCADA_GetInfos:\t{_url.Get()}");
}
2023-06-12 15:51:41 +08:00
}
/// <summary>
/// 内部方法从EntityService加载所有Entity
/// </summary>
private void LoadAllEntities()
{
foreach (var entity in DI.Get<IEntitiesService>().Entities)
{
if (entity.TryGetComponent<IdComponent>(out var deviceComponent))
{
_entities.Add(deviceComponent.Id,deviceComponent.Entity);
}
}
}
/// <summary>
/// 物理帧用于控制并发和间隔的同时请求数据
/// </summary>
/// <param name="delta"></param>
public override void _PhysicsProcess(double delta)
{
2023-06-13 14:43:03 +08:00
//等待依赖加载
//请求间隔控制+请求并发控制
if (_intervalTimer.Allow is false || httpClient.IsLoaded is false) return;
2023-06-15 21:52:13 +08:00
//如果url为空
if (string.IsNullOrEmpty(_url)) return;
2023-06-13 14:43:03 +08:00
if (!limitConcurrent.AllowOnly) return;
//提交并发
limitConcurrent.CanUpdate();
//发送请求
Request();
2023-06-12 15:51:41 +08:00
}
2023-06-15 21:52:13 +08:00
2023-06-12 15:51:41 +08:00
/// <summary>
/// 从http请求json
/// </summary>
private async void Request()
{
//获取json
2023-06-15 21:52:13 +08:00
2023-06-12 15:51:41 +08:00
try
{
2023-06-15 21:52:13 +08:00
var url = _url.Get();
var json = await httpClient.Value.GetStringAsync(url, _cancellationToken);
2023-06-12 15:51:41 +08:00
//取消执行,如果已取消令牌
_cancellationToken.ThrowIfCancellationRequested();
2023-06-15 21:52:13 +08:00
//处理json
ProcessJson(json);
}
catch (InvalidOperationException)
{
BIT4Log.Warnning(_url.Get());
//返回并发数量
limitConcurrent.Release();
2023-06-12 15:51:41 +08:00
}
catch (OperationCanceledException)
{
//返回并发数量
2023-06-13 14:43:03 +08:00
limitConcurrent.Release();
2023-06-12 15:51:41 +08:00
}
}
2023-06-15 21:52:13 +08:00
2023-06-12 15:51:41 +08:00
/// <summary>
/// 解析json
/// </summary>
/// <param name="json">从SCADA获取的Json</param>
private void ProcessJson(string json)
{
//首先从result中获取数组
var jArray = JsonConvert.DeserializeObject<JObject>(json)["result"]!.ToObject<JArray>();
//然后遍历所有数组的内容
foreach (var element in jArray)
{
//获取数组元素的Id
var id = element["id"]!.ToObject<string>();
//通过Id查找已加载的Entity
if (!_entities.TryGetValue(id, out var entity)) continue;
//加载数组中的"value"为json
var _json = element["value"]!.ToObject<string>();
//获取被加载为string的json
2023-06-15 21:52:13 +08:00
var obj = JsonConvert.DeserializeObject(_json);
2023-06-12 15:51:41 +08:00
//反序列化string为原始json
2023-06-15 21:52:13 +08:00
var value = JObject.Parse(obj!.ToString()!);
2023-06-12 15:51:41 +08:00
//提交json和entity
ProcessEntity(value,entity);
}
2023-06-15 21:52:13 +08:00
//返回并发数量
limitConcurrent.Release();
2023-06-12 15:51:41 +08:00
}
/// <summary>
/// 提交jObject数据和Entity进行解析和处理
/// </summary>
/// <param name="jObject">json [result] [0] [value] 中的原始json</param>
/// <param name="entity">引用实体如PLC-ZL</param>
private static void ProcessEntity(JObject jObject, IEntity entity)
{
//从Entity中加载所有Rotation Component
var rotationComponents = entity
.Components
.Where(x => x is RotationComponent)
.Select((x => (RotationComponent)x));
//遍历所有Rotation Component
foreach (var rotationComponent in rotationComponents)
{
//加载rotation需要的path,如 var angle = value["J1"]
var path = rotationComponent.Path;
//加载以获取到的角度
var currentAngle = jObject[path]!.ToObject<float>();
//最终角度 = 当前角度*角度权重 + 角度偏移 + 原始角度
var euler = currentAngle * rotationComponent.Weight + rotationComponent.Offset + rotationComponent.OriginalEuler;
//为Node3D.Rotation提交最后的角度计算结果
2023-06-16 17:08:59 +08:00
rotationComponent.RotationDegrees = euler;
2023-06-15 21:52:13 +08:00
2023-06-16 17:08:59 +08:00
rotationComponent.SetMeta(_CurrentAngle,(int)currentAngle);
rotationComponent.SetMeta(_CurrentRotation,euler);
2023-06-12 15:51:41 +08:00
}
}
}