1
This commit is contained in:
39
Artists/Scripts/Factory/Core/DeviceValueComponent.cs
Normal file
39
Artists/Scripts/Factory/Core/DeviceValueComponent.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using Godot;
|
||||
|
||||
namespace BITKit;
|
||||
/// <summary>
|
||||
/// 设备值组件,仅包括设备值的数据
|
||||
/// </summary>
|
||||
public partial class DeviceValueComponent : EntityComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取角度的路径
|
||||
/// </summary>
|
||||
[Export] public string Path { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
[Export]
|
||||
public string Value
|
||||
{
|
||||
get => _value;
|
||||
set
|
||||
{
|
||||
_value = value;
|
||||
if (Engine.IsEditorHint() is false)
|
||||
{
|
||||
OnValueChanged?.Invoke(value,LastUpdateTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime LastUpdateTime { get; set; }
|
||||
|
||||
private string _value;
|
||||
/// <summary>
|
||||
/// 当值改变时的回调
|
||||
/// </summary>
|
||||
public event Action<string,DateTime> OnValueChanged;
|
||||
}
|
43
Artists/Scripts/Factory/Core/IOTCloudService.cs
Normal file
43
Artists/Scripts/Factory/Core/IOTCloudService.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using BITKit;
|
||||
using BITKit.Entities;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
||||
using Newtonsoft.Json.Linq;
|
||||
#pragma warning disable CS0649 // 从未对字段赋值,字段将一直保持其默认值
|
||||
|
||||
namespace BITFactory;
|
||||
|
||||
public partial class IOTCloudService : EntityComponent
|
||||
{
|
||||
[Inject] private IEntitiesService _entitiesService;
|
||||
|
||||
private void Parse(string json)
|
||||
{
|
||||
var jObject = JObject.Parse(json);
|
||||
|
||||
foreach (var idComponent in _entitiesService.QueryComponents<IdComponent>())
|
||||
{
|
||||
if(jObject.TryGetValue(idComponent.Id,out var deviceToken) is false)continue;
|
||||
var deviceValue = deviceToken.ToObject<JObject>();
|
||||
|
||||
foreach (var valueComponent in idComponent.Entity.Components.OfType<DeviceValueComponent>())
|
||||
{
|
||||
var lastUpdateTime = DateTime.Now;
|
||||
if (deviceValue.TryGetValue("LastUpdateTime", out var _timeStr))
|
||||
{
|
||||
lastUpdateTime = DateTime.ParseExact(_timeStr.ToObject<string>(), "yyyy-MM-dd HH:mm:ss:fff", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (!deviceValue.TryGetValue(valueComponent.Path, out var value)) continue;
|
||||
|
||||
if (valueComponent.LastUpdateTime != lastUpdateTime)
|
||||
valueComponent.Value = value.ToObject<string>();
|
||||
valueComponent.LastUpdateTime = lastUpdateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -4,12 +4,8 @@ namespace BITKit;
|
||||
/// <summary>
|
||||
/// ECS中iFactory.Rotation的角度组件
|
||||
/// </summary>
|
||||
public partial class RotationComponent : EntityComponent
|
||||
public partial class RotationComponent : DeviceValueComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取角度的路径
|
||||
/// </summary>
|
||||
[Export] public string Path { get; private set; }
|
||||
/// <summary>
|
||||
/// 角度的绝对权重,例如90*0,0,1 = 0,0,90
|
||||
/// </summary>
|
||||
@@ -25,7 +21,7 @@ public partial class RotationComponent : EntityComponent
|
||||
/// <summary>
|
||||
/// 可读可写的当前角度
|
||||
/// </summary>
|
||||
public float CurrentAngle;
|
||||
public float CurrentAngle { get; set; }
|
||||
|
||||
[ExportCategory("Nodes")]
|
||||
[Export] public Node3D node3D;
|
62
Artists/Scripts/Factory/PathRenderer.cs
Normal file
62
Artists/Scripts/Factory/PathRenderer.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using BITKit;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BITFactory;
|
||||
public partial class PathRenderer : EntityBehaviour
|
||||
{
|
||||
[Export] private DeviceValueComponent valueComponent;
|
||||
[Export] private Node3D root;
|
||||
|
||||
private readonly List<MeshInstance3D> meshInstances=new();
|
||||
public override void OnAwake()
|
||||
{
|
||||
base.OnAwake();
|
||||
valueComponent.OnValueChanged += OnValueChanged;
|
||||
}
|
||||
|
||||
private void OnValueChanged(string arg1, DateTime arg2)
|
||||
{
|
||||
foreach (var x in meshInstances)
|
||||
{
|
||||
x.QueueFree();
|
||||
}
|
||||
meshInstances.Clear();
|
||||
Vector3 lastPoint = Vector3.Zero;
|
||||
foreach (var pair in JArray.Parse(arg1))
|
||||
{
|
||||
var x = pair["X"]!.ToObject<float>()!;
|
||||
var y = pair["Y"]!.ToObject<float>()!;
|
||||
var currentPoint =root.Position + new Vector3(x, 0, y);
|
||||
if (lastPoint != Vector3.Zero)
|
||||
{
|
||||
meshInstances.Add(Line(lastPoint, currentPoint,Colors.Orange));
|
||||
}
|
||||
lastPoint = currentPoint;
|
||||
}
|
||||
}
|
||||
|
||||
public static MeshInstance3D Line(Vector3 pos1, Vector3 pos2, Color? color = null)
|
||||
{
|
||||
var meshInstance = new MeshInstance3D();
|
||||
var immediateMesh = new ImmediateMesh();
|
||||
var material = new StandardMaterial3D();
|
||||
|
||||
meshInstance.Mesh = immediateMesh;
|
||||
meshInstance.CastShadow = GeometryInstance3D.ShadowCastingSetting.Off;
|
||||
|
||||
immediateMesh.SurfaceBegin(Mesh.PrimitiveType.Lines, material);
|
||||
immediateMesh.SurfaceAddVertex(pos1);
|
||||
immediateMesh.SurfaceAddVertex(pos2);
|
||||
immediateMesh.SurfaceEnd();
|
||||
|
||||
material.ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded;
|
||||
material.AlbedoColor = color ?? Colors.WhiteSmoke;
|
||||
|
||||
(Engine.GetMainLoop() as SceneTree).Root.AddChild(meshInstance);
|
||||
|
||||
return meshInstance;
|
||||
}
|
||||
}
|
@@ -1,167 +0,0 @@
|
||||
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,IProvider<string>,IActivable
|
||||
{
|
||||
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>
|
||||
/// 是否启用
|
||||
/// </summary>
|
||||
[Export]
|
||||
public bool Enabled { get; set; } = true;
|
||||
/// <summary>
|
||||
/// 已加载的Entity
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, IEntity> _entities = new();
|
||||
|
||||
/// <summary>
|
||||
/// 获取Entity并加载依赖
|
||||
/// </summary>
|
||||
public override async void _Ready()
|
||||
{
|
||||
if (!fixedEntities) return;
|
||||
await UniTask.Yield();
|
||||
LoadAllEntities();
|
||||
BIT4Log.Log<SCADAService>($"已加载{_entities.Count}个设备");
|
||||
|
||||
if (string.IsNullOrEmpty(_url))
|
||||
{
|
||||
BIT4Log.Warning("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>
|
||||
/// 从http请求json
|
||||
/// </summary>
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 解析json
|
||||
/// </summary>
|
||||
/// <param name="json">从SCADA获取的Json</param>
|
||||
public void Set(string json)
|
||||
{
|
||||
if(Enabled is false)return;
|
||||
//首先从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 _key = element["value"]!.ToString();
|
||||
string _json;
|
||||
if (_key.Substring(0) is "\"")
|
||||
{
|
||||
_json = element["value"]!.ToObject<string>();
|
||||
}
|
||||
else
|
||||
{
|
||||
_json = element["value"].ToString();
|
||||
}
|
||||
|
||||
//获取被加载为string的json
|
||||
var obj = JsonConvert.DeserializeObject(_json);
|
||||
//反序列化string为原始json
|
||||
var value = JObject.Parse(obj!.ToString()!);
|
||||
//提交json和entity
|
||||
ProcessEntity(value,entity);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 提交jObject数据和Entity进行解析和处理
|
||||
/// </summary>
|
||||
/// <param name="jObject">json [result] [index] [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 rawAngle = jObject[path]!.ToObject<float>();
|
||||
//补间角度
|
||||
var currentAngle = rotationComponent.CurrentAngle = Mathf.Lerp(rotationComponent.CurrentAngle, rawAngle,
|
||||
90 * BITAppForGodot.DeltaTime);
|
||||
//最终角度 = 当前角度*角度权重 + 角度偏移 + 原始角度
|
||||
var euler = currentAngle * rotationComponent.Weight + rotationComponent.Offset + rotationComponent.OriginalEuler;
|
||||
//为Node3D.Rotation提交最后的角度计算结果
|
||||
|
||||
euler.X = Mathf.DegToRad(euler.X);
|
||||
euler.Y = Mathf.DegToRad(euler.Y);
|
||||
euler.Z = Mathf.DegToRad(euler.Z);
|
||||
rotationComponent.node3D.Rotation = euler;
|
||||
//rotationComponent.RotationDegrees = euler;
|
||||
|
||||
|
||||
rotationComponent.SetMeta(_CurrentAngle,(int)rawAngle);
|
||||
rotationComponent.SetMeta(_CurrentRotation,euler);
|
||||
}
|
||||
}
|
||||
|
||||
public string Get()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SetActive(bool active) => Enabled = active;
|
||||
|
||||
}
|
Reference in New Issue
Block a user