데이터 즉시 업데이트 flag 추가
This commit is contained in:
@@ -376,11 +376,32 @@ namespace UVC.Data.Core
|
||||
/// <returns>업데이트된 속성의 총 개수입니다. 업데이트된 속성이 없으면 0을 반환합니다.</returns>
|
||||
public int UpdatedCount { get => addedList.Count + modifiedList.Count + removedList.Count; }
|
||||
|
||||
private bool isUpdateImmediately = false;
|
||||
/// <summary>
|
||||
/// 업데이트가 즉시 적용되어야 하는지 여부를 나타냅니다.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsUpdateImmediately
|
||||
{
|
||||
get => isUpdateImmediately;
|
||||
set
|
||||
{
|
||||
if (isUpdateImmediately == value) return;
|
||||
isUpdateImmediately = value;
|
||||
// 내부 DataObject에도 동일한 값을 설정합니다.
|
||||
foreach (var item in this)
|
||||
{
|
||||
item.IsUpdateImmediately = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 컬렉션의 모든 항목을 제거합니다.
|
||||
/// </summary>
|
||||
public new void Clear()
|
||||
{
|
||||
isUpdateImmediately = false;
|
||||
// 내부 리스트와 변경 추적 리스트를 모두 비웁니다.
|
||||
ReturnToDataObjectPool();
|
||||
base.Clear();
|
||||
|
||||
@@ -699,6 +699,7 @@ namespace UVC.Data.Core
|
||||
RemoveAll();
|
||||
Name = string.Empty;
|
||||
IdKey = null;
|
||||
isUpdateImmediately = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -899,6 +900,33 @@ namespace UVC.Data.Core
|
||||
/// <returns>업데이트된 속성의 총 개수입니다. 업데이트된 속성이 없으면 0을 반환합니다.</returns>
|
||||
public int UpdatedCount { get => changedProperies.Count; }
|
||||
|
||||
private bool isUpdateImmediately = false;
|
||||
/// <summary>
|
||||
/// 업데이트가 즉시 적용되어야 하는지 여부를 나타냅니다.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsUpdateImmediately
|
||||
{
|
||||
get => isUpdateImmediately;
|
||||
set
|
||||
{
|
||||
if (isUpdateImmediately == value) return;
|
||||
isUpdateImmediately = value;
|
||||
|
||||
foreach (var item in this)
|
||||
{
|
||||
if (item.Value is DataObject dataObject)
|
||||
{
|
||||
dataObject.IsUpdateImmediately = value;
|
||||
}
|
||||
else if (item.Value is DataArray dataArray)
|
||||
{
|
||||
dataArray.IsUpdateImmediately = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DataObject의 내용을 문자열로 반환합니다.
|
||||
/// 각 키-값 쌍이 "키:값" 형식으로 쉼표로 구분되어 표시됩니다.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace UVC.Data.Core
|
||||
namespace UVC.Data.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 서로 다른 JSON 데이터 구조 간에 매핑 기능을 제공하는 클래스입니다.
|
||||
@@ -31,7 +31,6 @@
|
||||
public IDataObject GetUpdatedObject(bool fromPool = true);
|
||||
|
||||
/// <summary>
|
||||
/// /// <summary>
|
||||
/// 업데이트 된 속성의 수.
|
||||
/// </summary>
|
||||
/// <returns>업데이트된 속성의 총 개수입니다. 업데이트된 속성이 없으면 0을 반환합니다.</returns>
|
||||
@@ -54,5 +53,11 @@
|
||||
/// 풀에서 재사용할 수 있도록 해야 합니다. 객체를 풀로 반환하기 전에 객체가 유효한 상태인지 확인하십시오.</remarks>
|
||||
public void ReturnToPool();
|
||||
|
||||
/// <summary>
|
||||
/// 업데이트가 즉시 적용되어야 하는지 여부를 나타냅니다.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsUpdateImmediately { get; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,16 @@ namespace UVC.Data
|
||||
{
|
||||
dataObject.MarkAllAsUpdated();
|
||||
var newData = dataObject.Clone(fromPool: false);
|
||||
//데이터 즉시 업데이트 여부 설정
|
||||
if (newData is DataObject dataObj)
|
||||
{
|
||||
dataObj.IsUpdateImmediately = dataObject.IsUpdateImmediately;
|
||||
}
|
||||
else if (newData is DataArray dataArray)
|
||||
{
|
||||
dataArray.IsUpdateImmediately = dataObject.IsUpdateImmediately;
|
||||
}
|
||||
|
||||
dataObjects.Add(key, newData);
|
||||
NotifyDataUpdate(key, newData.Clone(false));
|
||||
return dataObject;
|
||||
@@ -102,6 +112,15 @@ namespace UVC.Data
|
||||
{
|
||||
newDataObject = dataObject;
|
||||
}
|
||||
//데이터 즉시 업데이트 여부 설정
|
||||
if (newDataObject is DataObject dataObj)
|
||||
{
|
||||
dataObj.IsUpdateImmediately = dataObject.IsUpdateImmediately;
|
||||
}
|
||||
else if (newDataObject is DataArray dataArray)
|
||||
{
|
||||
dataArray.IsUpdateImmediately = dataObject.IsUpdateImmediately;
|
||||
}
|
||||
bool shouldInvoke = !updatedDataOnly || newDataObject.UpdatedCount > 0;
|
||||
if (shouldInvoke) NotifyDataUpdate(key, newDataObject == dataObject ? newDataObject.Clone(false) : newDataObject);
|
||||
return newDataObject;
|
||||
|
||||
@@ -163,7 +163,7 @@ namespace UVC.Data.Http
|
||||
/// "AGV"와 "ALARM"을 각각 별개의 데이터로 간주하고 처리합니다.
|
||||
/// 각 키에 대해 별도의 DataMapper나 Validator를 `HttpRequestConfig`에 설정할 수 있습니다.
|
||||
/// </remarks>
|
||||
public static void ProcessSplitResponse(HttpRequestConfig info, string jsonResponse, CancellationToken? cancellationToken = null)
|
||||
public static void ProcessSplitResponse(HttpRequestConfig info, string jsonResponse, CancellationToken? cancellationToken = null, bool isUpdateImmediately = false)
|
||||
{
|
||||
JObject responseObject = JObject.Parse(jsonResponse);
|
||||
// JSON 객체의 모든 프로퍼티(키-값 쌍)를 순회합니다.
|
||||
@@ -207,6 +207,15 @@ namespace UVC.Data.Http
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (subMappedObject is DataObject dataObj)
|
||||
{
|
||||
dataObj.IsUpdateImmediately = isUpdateImmediately;
|
||||
}
|
||||
else if (subMappedObject is DataArray dataArray)
|
||||
{
|
||||
dataArray.IsUpdateImmediately = isUpdateImmediately;
|
||||
}
|
||||
|
||||
// 분할된 데이터를 `subKey`를 키로 사용하여 DataRepository에 저장합니다.
|
||||
var repoObject = DataRepository.Instance.AddOrUpdateData(subKey, subMappedObject, info.UpdatedDataOnly);
|
||||
|
||||
@@ -102,6 +102,10 @@ namespace UVC.Data.Mqtt
|
||||
|
||||
public string LastMessage { get; private set; } = string.Empty;
|
||||
|
||||
//mqtt 연결 후 topic 별 첫 번째 메시지를 수신 했는지 여부를 저장하는 딕셔너리
|
||||
private ConcurrentDictionary<string, bool> firstMessageReceived = new ConcurrentDictionary<string, bool>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// MqttDataReceiver 인스턴스를 생성합니다.
|
||||
/// </summary>
|
||||
@@ -232,6 +236,14 @@ namespace UVC.Data.Mqtt
|
||||
// 토픽이 infoList와 readyHandlerList에 존재하고, 준비 상태가 true인 경우에만 처리합니다.
|
||||
if (configList.TryGetValue(topic, out var config))
|
||||
{
|
||||
bool isFirstMessage = false;
|
||||
// 첫 번째 메시지 수신 여부를 확인하고, 아직 수신하지 않았다면 처리합니다.
|
||||
if (!firstMessageReceived.ContainsKey(topic) || !firstMessageReceived[topic])
|
||||
{
|
||||
firstMessageReceived[topic] = true; // 첫 번째 메시지를 수신했음을 기록합니다.
|
||||
isFirstMessage = true; // 첫 번째 메시지로 처리합니다.
|
||||
}
|
||||
|
||||
IDataObject? mappedObject = null;
|
||||
message = message.Trim();
|
||||
LastMessage = message; // 마지막 메시지를 저장하여 나중에 사용할 수 있습니다.
|
||||
@@ -249,6 +261,17 @@ namespace UVC.Data.Mqtt
|
||||
}
|
||||
//Debug.Log($"OnTopicMessageLogic topic: {topic}, mappedObject == null: {mappedObject == null}, config.DataMapper:{config.DataMapper == null}, config.Validator:{config.Validator == null}");
|
||||
if (mappedObject == null) return;
|
||||
|
||||
//즉시 업데이트 여부를 설정합니다.
|
||||
if (mappedObject is DataObject dataObj)
|
||||
{
|
||||
dataObj.IsUpdateImmediately = isFirstMessage;
|
||||
}
|
||||
else if (mappedObject is DataArray dataArray)
|
||||
{
|
||||
dataArray.IsUpdateImmediately = isFirstMessage;
|
||||
}
|
||||
|
||||
// DataRepository는 내부적으로 데이터를 복사/업데이트하므로, mappedObject는 여기서 임시 객체가 됩니다.
|
||||
var repoObject = DataRepository.Instance.AddOrUpdateData(topic, mappedObject, config.UpdatedDataOnly);
|
||||
if (repoObject == mappedObject) repoObject = mappedObject.Clone(fromPool: false);
|
||||
@@ -293,6 +316,7 @@ namespace UVC.Data.Mqtt
|
||||
mqttWorker.RemoveListener(topic, OnTopicPacketMessage);
|
||||
}
|
||||
mqttWorker.Stop();
|
||||
firstMessageReceived.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -311,6 +335,7 @@ namespace UVC.Data.Mqtt
|
||||
if (!UseMockup) mqttWorker.Dispose();
|
||||
//else mockupMQTT?.Disconnect();
|
||||
configList.Clear();
|
||||
firstMessageReceived.Clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
#nullable enable
|
||||
using EPOOutline;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UVC.Data;
|
||||
using UVC.Data.Core;
|
||||
using UVC.Extention;
|
||||
using UVC.Factory.Playback;
|
||||
|
||||
namespace UVC.Factory.Component
|
||||
@@ -65,12 +63,6 @@ namespace UVC.Factory.Component
|
||||
private float agvFloorOffset = 200f; //agv의 층을 구별하기 위한 값
|
||||
private float agvZOffset = -197.5f; //층을 옮긴 agv의 위치를 잡아주기 위한 값
|
||||
|
||||
// true일 경우, UpdatePositionAndRotation에서 즉시 위치와 회전을 변경합니다.
|
||||
// 한번으로 않되서 3번 즉시 변경하도록 설정
|
||||
private bool needUpdateImmediately = false;
|
||||
private int maxImmediateUpdateCount = 3;
|
||||
private int immediateUpdateCount = 0;
|
||||
|
||||
/// <summary>
|
||||
/// AGV 객체가 생성될 때 처음 한 번 호출되는 초기화 메서드입니다.
|
||||
/// </summary>
|
||||
@@ -93,12 +85,12 @@ namespace UVC.Factory.Component
|
||||
public override void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
// 사용자가 AGV를 클릭했을 때 정보창에 표시될 데이터 항목과 순서를 정의합니다.
|
||||
if(dataDisplaySetting == null) dataDisplaySetting = UserSetting.Get("AGV");
|
||||
if (dataDisplaySetting == null) dataDisplaySetting = UserSetting.Get("AGV");
|
||||
base.OnPointerClick(eventData);
|
||||
}
|
||||
|
||||
private void OnChangeTimeScaleHandler(float timeScale)
|
||||
{
|
||||
{
|
||||
this.timeScale = timeScale;
|
||||
}
|
||||
|
||||
@@ -108,7 +100,7 @@ namespace UVC.Factory.Component
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 선택 된 효과로 외곽선을 표시합니다.
|
||||
@@ -126,17 +118,6 @@ namespace UVC.Factory.Component
|
||||
if (outlinable != null) outlinable.enabled = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 즉시 객체의 상태를 업데이트합니다.
|
||||
/// </summary>
|
||||
/// <param name="count">업데이트 횟수. 기본값은 1입니다.</param>
|
||||
public override void UpdateImmediately(int count = 3)
|
||||
{
|
||||
needUpdateImmediately = true; // 다음 데이터 업데이트에서 즉시 위치와 회전을 변경하도록 플래그를 설정합니다.
|
||||
immediateUpdateCount = 0;
|
||||
maxImmediateUpdateCount = count;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// AGVManager로부터 새로운 데이터를 받았을 때 호출되는 핵심 메서드입니다.
|
||||
@@ -149,25 +130,22 @@ namespace UVC.Factory.Component
|
||||
/// <param name="newData">AGV의 최신 정보가 담긴 데이터 객체입니다.</param>
|
||||
protected override void ProcessData(DataObject newData)
|
||||
{
|
||||
// 새 데이터로 위치와 회전을 즉시 설정합니다.
|
||||
UpdatePositionAndRotation(newData);
|
||||
if (newData.IsUpdateImmediately)
|
||||
{
|
||||
transform.position = targetPosition;
|
||||
transform.rotation = targetRotation;
|
||||
}
|
||||
|
||||
// 처음 데이터를 받는 경우 (data가 null일 때)
|
||||
if (data == null)
|
||||
{
|
||||
// 새 데이터로 위치와 회전을 즉시 설정합니다.
|
||||
UpdatePositionAndRotation(newData);
|
||||
// 받은 데이터를 내부 데이터 저장소에 저장합니다.
|
||||
data = newData;
|
||||
}
|
||||
else // 이미 데이터가 있는 경우 (업데이트)
|
||||
{
|
||||
// 새 데이터를 기반으로 목표 위치와 회전을 갱신합니다.
|
||||
UpdatePositionAndRotation(newData);
|
||||
if (needUpdateImmediately)
|
||||
{
|
||||
transform.position = targetPosition;
|
||||
transform.rotation = targetRotation;
|
||||
immediateUpdateCount++;
|
||||
if (immediateUpdateCount >= maxImmediateUpdateCount) needUpdateImmediately = false;
|
||||
}
|
||||
// 기존 데이터(data)에 새로운 데이터(newData)의 내용을 덮어씁니다.
|
||||
foreach (var keyValue in newData)
|
||||
{
|
||||
@@ -232,7 +210,7 @@ namespace UVC.Factory.Component
|
||||
{
|
||||
float x = currentX.Value;
|
||||
float y = currentY.Value;
|
||||
|
||||
|
||||
if ((newX.HasValue && x != newX) || (newY.HasValue && y != newY))
|
||||
{
|
||||
Vector3 newTargetPosition = transform.position;
|
||||
@@ -307,10 +285,10 @@ namespace UVC.Factory.Component
|
||||
|
||||
private void UpdateStats()
|
||||
{
|
||||
if(data == null) return;
|
||||
if (data == null) return;
|
||||
string? stats = data.GetString("VHL_STATE");
|
||||
if (stats == null) return;
|
||||
if(stats == "91" || stats == "92" || stats == "93" || stats == "94")//CHARGING
|
||||
if (stats == "91" || stats == "92" || stats == "93" || stats == "94")//CHARGING
|
||||
{
|
||||
ChangeColor(new Color(0.7f, 0.12f, 0.0f, 1.0f));//orange
|
||||
}
|
||||
@@ -321,7 +299,7 @@ namespace UVC.Factory.Component
|
||||
else
|
||||
{
|
||||
string? trayID = data.GetString("CARRIER_ID");
|
||||
if(string.IsNullOrEmpty(trayID))
|
||||
if (string.IsNullOrEmpty(trayID))
|
||||
{
|
||||
ChangeColor(Color.yellow); // 트레이 ID가 없으면 기본 색상으로 설정
|
||||
}
|
||||
@@ -334,9 +312,9 @@ namespace UVC.Factory.Component
|
||||
|
||||
private void ChangeColor(Color color)
|
||||
{
|
||||
if(color == Color.red && isRed && modelRenderer != null) return; // 이미 빨간색이면 변경하지 않음
|
||||
if (color == Color.red && isRed && modelRenderer != null) return; // 이미 빨간색이면 변경하지 않음
|
||||
isRed = color == Color.red;
|
||||
if(modelRenderer != null) modelRenderer!.material.color = color;
|
||||
if (modelRenderer != null) modelRenderer!.material.color = color;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -386,7 +364,6 @@ namespace UVC.Factory.Component
|
||||
public override void UnregisterFactoryObject()
|
||||
{
|
||||
base.UnregisterFactoryObject();
|
||||
needUpdateImmediately = false; // 파괴 시 즉시 위치와 회전 변경 플래그를 초기화합니다.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,17 +222,6 @@ namespace UVC.Factory.Component
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 즉시 객체의 상태를 업데이트합니다.
|
||||
/// </summary>
|
||||
public void UpdateImmediately(int count = 1)
|
||||
{
|
||||
if (pool == null) return;
|
||||
foreach (var agv in pool.ActiveItems.Values)
|
||||
{
|
||||
if (agv != null) agv.UpdateImmediately(count);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AGVManager가 파괴될 때 호출됩니다.
|
||||
|
||||
@@ -145,7 +145,7 @@ namespace UVC.Factory.Playback
|
||||
httpRequestConfig.AddSplitConfig("ALARM", DataMapperValidator.Get("ALARM"));
|
||||
foreach (var item in list)
|
||||
{
|
||||
HttpDataProcessor.ProcessSplitResponse(httpRequestConfig, item.data);
|
||||
HttpDataProcessor.ProcessSplitResponse(httpRequestConfig, item.data, null, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user