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; /// /// 单例SCADA Service,从http接口获取json后解析为指定数据 /// public partial class SCADAService : Node { /// /// 在构造函数中注入依赖 /// public SCADAService() { BITApp.ServiceCollection.AddSingleton(this); } /// /// 获取json的Url /// [Export] private string url; /// /// 是否固定控制的Entity,如果固定,只会刷新一次,如果不固定,每帧都会刷新 /// [Export] private bool fixedEntities; /// /// 已加载的Entity /// private readonly Dictionary _entities = new(); /// /// 最大并行请求数量 /// private readonly LimitTimes limitConcurrent =new (1); /// /// 请求数据的间隔 /// private readonly IntervalTimer _intervalTimer = new(1); /// /// 取消令牌,用于取消Http Get /// private CancellationToken _cancellationToken; /// /// http客户端 /// private readonly ServiceLoader httpClient=new(); /// /// 获取Entity并加载依赖 /// public override async void _Ready() { _cancellationToken = new CancellationToken(); if (!fixedEntities) return; await UniTask.Yield(); LoadAllEntities(); BIT4Log.Log($"已加载{_entities.Count}个设备"); } /// /// 内部方法,从EntityService加载所有Entity /// private void LoadAllEntities() { foreach (var entity in DI.Get().Entities) { if (entity.TryGetComponent(out var deviceComponent)) { _entities.Add(deviceComponent.Id,deviceComponent.Entity); } } } /// /// 物理帧用于控制并发和间隔的同时请求数据 /// /// public override void _PhysicsProcess(double delta) { //等待依赖加载 //请求间隔控制+请求并发控制 if (_intervalTimer.Allow is false || httpClient.IsLoaded is false) return; if (!limitConcurrent.AllowOnly) return; //提交并发 limitConcurrent.CanUpdate(); //发送请求 Request(); } /// /// 从http请求json /// private async void Request() { //获取json var json =await httpClient.Value.GetStringAsync(url, _cancellationToken); try { //取消执行,如果已取消令牌 _cancellationToken.ThrowIfCancellationRequested(); } catch (OperationCanceledException) { //返回并发数量 limitConcurrent.Release(); return; } //返回并发数量 limitConcurrent.Release(); //处理json ProcessJson(json); } /// /// 解析json /// /// 从SCADA获取的Json private void ProcessJson(string json) { //首先从result中获取数组 var jArray = JsonConvert.DeserializeObject(json)["result"]!.ToObject(); //然后遍历所有数组的内容 foreach (var element in jArray) { //获取数组元素的Id var id = element["id"]!.ToObject(); //通过Id查找已加载的Entity if (!_entities.TryGetValue(id, out var entity)) continue; //加载数组中的"value"为json var _json = element["value"]!.ToObject(); //获取被加载为string的json var obj = Json.ParseString(_json); //反序列化string为原始json var value = JObject.Parse(obj.ToString()); //提交json和entity ProcessEntity(value,entity); } } /// /// 提交jObject数据和Entity进行解析和处理 /// /// json [result] [0] [value] 中的原始json /// 引用实体,如PLC-ZL 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(); //最终角度 = 当前角度*角度权重 + 角度偏移 + 原始角度 var euler = currentAngle * rotationComponent.Weight + rotationComponent.Offset + rotationComponent.OriginalEuler; //为Node3D.Rotation提交最后的角度计算结果 rotationComponent.Rotation = euler; } } }