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

194 lines
5.5 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 Godot;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using BITKit.Core.Entites;
using BITKit.Packages.Core.LazyLoad;
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
{
public const string _CurrentAngle="CurrentAngle";
public const string _CurrentRotation="CurrentRotation";
/// <summary>
/// 在构造函数中注入依赖
/// </summary>
public SCADAService()
{
BITApp.ServiceCollection.AddSingleton(this);
}
/// <summary>
/// 获取json的Url
/// </summary>
private readonly DataReference<string> _url = new("SCADA_GetInfos");
/// <summary>
/// 是否固定控制的Entity如果固定只会刷新一次如果不固定每帧都会刷新
/// </summary>
[Export]
private bool fixedEntities;
/// <summary>
/// 已加载的Entity
/// </summary>
private readonly Dictionary<string, IEntity> _entities = new();
/// <summary>
/// 最大并行请求数量
/// </summary>
private readonly LimitTimes limitConcurrent =new (1);
/// <summary>
/// 请求数据的间隔
/// </summary>
private readonly IntervalTimer _intervalTimer = new(1);
/// <summary>
/// 取消令牌用于取消Http Get
/// </summary>
private CancellationToken _cancellationToken;
/// <summary>
/// http客户端
/// </summary>
private readonly ServiceLoader<System.Net.Http.HttpClient> httpClient=new();
/// <summary>
/// 获取Entity并加载依赖
/// </summary>
public override async void _Ready()
{
_cancellationToken = new CancellationToken();
if (!fixedEntities) return;
await UniTask.Yield();
LoadAllEntities();
BIT4Log.Log<SCADAService>($"已加载{_entities.Count}个设备");
if (string.IsNullOrEmpty(_url))
{
BIT4Log.Warnning("SCADA Url为空");
}
else
{
BIT4Log.Log<SCADAService>($"已找到SCADA_GetInfos:\t{_url.Get()}");
}
}
/// <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)
{
//等待依赖加载
//请求间隔控制+请求并发控制
if (_intervalTimer.Allow is false || httpClient.IsLoaded is false) return;
//如果url为空
if (string.IsNullOrEmpty(_url)) return;
if (!limitConcurrent.AllowOnly) return;
//提交并发
limitConcurrent.CanUpdate();
//发送请求
Request();
}
/// <summary>
/// 从http请求json
/// </summary>
private async void Request()
{
//获取json
try
{
var url = _url.Get();
var json = await httpClient.Value.GetStringAsync(url, _cancellationToken);
//取消执行,如果已取消令牌
_cancellationToken.ThrowIfCancellationRequested();
//处理json
ProcessJson(json);
}
catch (InvalidOperationException)
{
BIT4Log.Warnning(_url.Get());
//返回并发数量
limitConcurrent.Release();
}
catch (OperationCanceledException)
{
//返回并发数量
limitConcurrent.Release();
}
}
/// <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
var obj = JsonConvert.DeserializeObject(_json);
//反序列化string为原始json
var value = JObject.Parse(obj!.ToString()!);
//提交json和entity
ProcessEntity(value,entity);
}
//返回并发数量
limitConcurrent.Release();
}
/// <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提交最后的角度计算结果
rotationComponent.RotationDegrees = euler;
rotationComponent.SetMeta(_CurrentAngle,(int)currentAngle);
rotationComponent.SetMeta(_CurrentRotation,euler);
}
}
}