Files
Studio/Assets/Scripts/Studio/Connect/StudioService.cs
2025-05-27 15:24:25 +09:00

331 lines
11 KiB
C#

using Newtonsoft.Json.Linq;
using Studio.Core;
using Studio.Setting.Connect;
using Studio.Util;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
namespace Studio
{
public class StudioServiceIdEventArgs : EventArgs
{
public readonly string Type;
public Dictionary<string, string> Entity;
public StudioServiceIdEventArgs(string type, Dictionary<string, string> entity)
{
Type = type;
Entity = entity;
}
}
public class StudioServiceTypeEventArgs : EventArgs
{
public readonly string Type;
public Dictionary<string, Dictionary<string, string>> Entitis;
public StudioServiceTypeEventArgs(string type, Dictionary<string, Dictionary<string, string>> entitis)
{
Type = type;
Entitis = entitis;
}
}
public class StudioService : UnitySingleton<StudioService>
{
private Dictionary<string, Dictionary<string, EventHandler<StudioServiceIdEventArgs>>> listenerIdMap = new();
private Dictionary<string, EventHandler<StudioServiceTypeEventArgs>> listenerTypeMap = new();
private StudioRepoistory repository;
private Dictionary<string, float> updateTime = new();
public bool isConnected
{
get
{
return repository.isConnected;
}
}
#region
private int totalRequestPacket;
private int totalResponsePacket;
private long maxElapsedTime;
private long totalElapsedTime;
private int maxReponesSize;
private int maxReuqestSize;
private string maxReuqestApi;
private string maxResponseApi;
private string maxResponseTimeApi;
#endregion
public Dictionary<string, StudioEntityWithState<List<Dictionary<string, string>>>> apiData = new();
public Dictionary<string, (Dictionary<string, Dictionary<string, string>>, TimeSpan)> mqttData = new();
private Dictionary<string, DateTime> lastUpdateTime = new();
public event Action<string, StudioEntityWithState<object>> onAPIDataLoaded;
public event Action<string, Dictionary<string, Dictionary<string, string>>, TimeSpan> onMQTTDataLoaded;
private Dictionary<string, List<Topic>> topicTable = new();
public Dictionary<string, List<Topic>> TopciTable { get { return topicTable; } }
public void ConnectMQTT(string domain, string port, List<Util.Topic> topics)
{
this.repository = new StudioRepoistory();
repository.OnTopicList += OnTopicList;
var conntedInfo = $"MQTT Domain : {domain} , MQTTPORT :{port}";
if (!topicTable.ContainsKey(conntedInfo))
topicTable.Add(conntedInfo, new());
topicTable[conntedInfo] = topics;
repository.MQTTCreateConnect(domain, port);
repository.MQTTConnect(conntedInfo);
}
private void OnTopicList(string type, Dictionary<string, Dictionary<string, string>> entities)
{
DateTime now = DateTime.Now;
TimeSpan elapsedTime;
// elapsed 계산
if (lastUpdateTime.ContainsKey(type))
{
DateTime last = lastUpdateTime[type];
elapsedTime = now - last;
}
else
{
elapsedTime = TimeSpan.Zero;
}
lastUpdateTime[type] = now;
foreach (var idKey in entities)
{
UpdateTopicData(type, idKey.Key, idKey.Value);
}
if (!mqttData.ContainsKey(type))
{
mqttData.Add(type, (entities, elapsedTime));
}
else
{
mqttData[type] = (entities, elapsedTime);
}
}
private void UpdateTopicData(string type, string id, Dictionary<string, string> entity)
{
if (!updateTime.ContainsKey(type))
updateTime.Add(type, 0.5f);
var time = updateTime[type];
var task = Task.Factory.StartNew(async () =>
{
int delayTime = (int)(time * 1000);
await Task.Delay(delayTime);
DispatchMachineEvent(type, id, entity);
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}
private Dictionary<string, Dictionary<string, Dictionary<string, string>>> timelineEntity = new();
public bool TryGetRecentTimelineEntity(string type, string id, out Dictionary<string, string> result)
{
result = null;
if (!timelineEntity.ContainsKey(type))
{
timelineEntity[type] = new Dictionary<string, Dictionary<string, string>>();
return false;
}
if (!timelineEntity[type].ContainsKey(id))
{
timelineEntity[type][id] = new();
return false;
}
result = timelineEntity[type][id];
return result != null;
}
public void AddTypeIdListener(string type, string id = null, EventHandler<StudioServiceIdEventArgs> listener = null)
{
if (!listenerIdMap.ContainsKey(type))
{
listenerIdMap.Add(type, new());
}
if (id == null)
return;
if (!listenerIdMap[type].ContainsKey(id))
{
listenerIdMap[type][id] = listener;
}
else
{
listenerIdMap[type][id] += listener;
}
}
public void AddTypeListener(string type, EventHandler<StudioServiceTypeEventArgs> listener = null)
{
listenerTypeMap[type] = listener;
}
public void RemoveTypeListener(string type)
{
if (!listenerTypeMap.ContainsKey(type))
return;
listenerTypeMap.Remove(type);
}
public void RemoveTypeIdListener(string type, string id)
{
if (!listenerIdMap.ContainsKey(type))
return;
if (!listenerIdMap[type].ContainsKey(id))
return;
listenerIdMap[type].Remove(id);
}
public async Task LoadBaseData(string url)
{
DateTime startTime = DateTime.Now;
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
StudioEntityWithState<object> data = await repository.BaseInfo(url);
sw.Stop();
DateTime endTime = DateTime.Now;
var t = sw.ElapsedMilliseconds;
if (data.State.Equals(APIState.Loaded))
{
if (!apiData.ContainsKey(url))
apiData.Add(url, new());
data.lastRequestTime = startTime;
data.lastResponseTime = endTime;
data.elapsedTime = sw.Elapsed;
CheckAPIBoarder(data.Entity.ToString(), data.ReqSize, url, t);
var entity = UpdateEntity(url, data.Entity.ToString());
StudioEntityWithState<List<Dictionary<string, string>>> convertData = new(data.State, entity, data.ReqSize, data.Message);
apiData[url] = convertData;
}
else if (data.State == APIState.Error)
{
//TODO 에러메세지
Debug.Log($"APIState : Error , Message :{data.Message}");
}
onAPIDataLoaded?.Invoke(url, data); // 나중에 클래스 밖으로 이동 필요
}
private void CheckAPIBoarder(string data, int reqSize, string url, long time)
{
var byteSize = System.Text.Encoding.Default.GetBytes(data).Length;
if (maxElapsedTime < time)
{
maxElapsedTime = time;
maxResponseTimeApi = url;
}
if (maxReponesSize < byteSize)
{
maxReponesSize = byteSize;
maxResponseApi = url;
}
if (maxReuqestSize < reqSize)
{
maxReuqestSize = reqSize;
maxReuqestApi = url;
}
totalRequestPacket += reqSize;
totalResponsePacket += byteSize;
totalElapsedTime += time;
}
public APITotalBoardEntity GetAPIStatusBoarder()
{
var boardEntity = new APITotalBoardEntity();
boardEntity.TotalRequestPacketSize = totalRequestPacket;
boardEntity.AverageRequestPacketSize = totalRequestPacket / apiData.Count;
boardEntity.TotalResponsePacketSize = totalResponsePacket;
boardEntity.AverageResponsePacketSize = totalResponsePacket / apiData.Count;
boardEntity.MaximumResponseTime = maxElapsedTime;
boardEntity.AverageResponseTime = totalElapsedTime / apiData.Count;
boardEntity.MaximumRequestPacketAPI = maxReuqestApi;
boardEntity.MaximumResponsePacketAPI = maxResponseApi;
boardEntity.MaximumResponseTimeAPI = maxResponseTimeApi;
return boardEntity;
}
public List<Dictionary<string, string>> UpdateEntity(string type, string data)
{
//Dcitionary 형으로 바꿈
JObject json = JObject.Parse(data);
foreach (JProperty prop in json.Children())
{
string key = prop.Name.ToString();
string value = prop.Value.ToString();
Debug.Log($"kEY : {key}, Value:{value}");
}
var datas = json["rows"].ToString();
var list = new List<Dictionary<string, string>>();
JArray jarray = JArray.Parse(datas);
foreach (JObject obj in jarray.Children())
{
Dictionary<string, string> keyvalue = new();
string id = string.Empty;
foreach (JProperty prop in obj.Children())
{
string key = prop.Name.ToString();
string value = prop.Value.ToString();
keyvalue.Add(key, value);
}
list.Add(keyvalue);
}
return list;
}
private void DispatchMachineEvent(string type, string id, Dictionary<string, string> entity)
{
if (!listenerIdMap.ContainsKey(type))
{
Debug.Log($"Key Value is missing!!!! -Key:{type}-");
}
if (listenerIdMap[type].ContainsKey(id))
{
listenerIdMap[type][id].Invoke(this, new StudioServiceIdEventArgs(type, entity));
}
else
{
if (!listenerTypeMap.ContainsKey(type))
return;
var data = new Dictionary<string, Dictionary<string, string>>();
data.Add(id, entity);
if (entity == null)
return;
DispatchTypeMachineEvent(type, data);
}
}
private void DispatchTypeMachineEvent(string type, Dictionary<string, Dictionary<string, string>> entities)
{
if (listenerTypeMap.ContainsKey(type))
{
listenerTypeMap[type].Invoke(this, new(type, entities));
}
}
}
}