playback 버그 수정
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
#nullable enable
|
||||
#nullable enable
|
||||
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
@@ -52,8 +52,8 @@ namespace UVC.Data.Http
|
||||
///
|
||||
/// // 단일 요청 설정 및 등록
|
||||
/// var singleRequest = new HttpRequestConfig("https://api.example.com/data")
|
||||
/// .setDataMapper(dataMapper)
|
||||
/// .setSuccessHandler(data => {
|
||||
/// .SetDataMapper(dataMapper)
|
||||
/// .SetSuccessHandler(data => {
|
||||
/// // 데이터 처리 로직
|
||||
/// ULog.Debug($"데이터 수신: {data?.ToString() ?? "null"}");
|
||||
/// });
|
||||
@@ -61,12 +61,12 @@ namespace UVC.Data.Http
|
||||
///
|
||||
/// // 반복 요청 설정 및 등록
|
||||
/// var repeatingRequest = new HttpRequestConfig("https://api.example.com/status")
|
||||
/// .setDataMapper(dataMapper)
|
||||
/// .setSuccessHandler(data => {
|
||||
/// .SetDataMapper(dataMapper)
|
||||
/// .SetSuccessHandler(data => {
|
||||
/// // 상태 데이터 처리
|
||||
/// ULog.Debug($"상태 업데이트: {data?.ToString() ?? "null"}");
|
||||
/// })
|
||||
/// .setRepeat(true, 0, 5000); // 5초마다 무한 반복
|
||||
/// .SetRepeat(true, 0, 5000); // 5초마다 무한 반복
|
||||
/// httpFetcher.Add("statusMonitor", repeatingRequest);
|
||||
///
|
||||
/// // 응답 분할 요청 설정 (예: 응답이 {"AGV": [...], "ALARM": [...]} 형태)
|
||||
@@ -77,7 +77,7 @@ namespace UVC.Data.Http
|
||||
/// var alarmMapper = new DataMapper(alarmMask);
|
||||
///
|
||||
/// var splitRequest = new HttpRequestConfig("https://api.example.com/baseinfo")
|
||||
/// .setSplitResponseByKey(true) // 응답을 키별로 분할
|
||||
/// .SetSplitResponseByKey(true) // 응답을 키별로 분할
|
||||
/// .AddSplitConfig("AGV", agvMapper) // "AGV" 키에 대한 매퍼 설정
|
||||
/// .AddSplitConfig("ALARM", alarmMapper); // "ALARM" 키에 대한 매퍼 설정
|
||||
/// httpFetcher.Add("baseInfo", splitRequest);
|
||||
|
||||
@@ -207,7 +207,7 @@ namespace UVC.Data.Http
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// 분할된 데이터를 `subKey`를 키로 사용하여 DataRepository에 저장합니다.
|
||||
var repoObject = DataRepository.Instance.AddOrUpdateData(subKey, subMappedObject, info.UpdatedDataOnly);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#nullable enable
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -18,19 +18,19 @@ namespace UVC.Data.Http
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var config = new HttpRequestConfig("https://api.example.com/data", "GET")
|
||||
/// .setDataMapper(new DataMapper(dataMask))
|
||||
/// .setSuccessHandler(data => Console.WriteLine(data)) // 성공 핸들러 예시
|
||||
/// .setFailHandler(errorData => Console.Error.WriteLine(errorData)) // 실패 핸들러 예시
|
||||
/// .setRetry(5, 2000)
|
||||
/// .setRepeat(true, 10, 5000);
|
||||
/// .SetDataMapper(new DataMapper(dataMask))
|
||||
/// .SetSuccessHandler(data => Console.WriteLine(data)) // 성공 핸들러 예시
|
||||
/// .SetFailHandler(errorData => Console.Error.WriteLine(errorData)) // 실패 핸들러 예시
|
||||
/// .SetRetry(5, 2000)
|
||||
/// .SetRepeat(true, 10, 5000);
|
||||
///
|
||||
/// // 응답을 키별로 분할하는 설정
|
||||
/// // 응답이 {"AGV": [...], "ALARM": [...]} 형태일 때 사용
|
||||
/// var splitConfig = new HttpRequestConfig("https://api.example.com/alldata")
|
||||
/// .setSplitResponseByKey(true) // 이 옵션을 활성화
|
||||
/// .SetSplitResponseByKey(true) // 이 옵션을 활성화
|
||||
/// .AddSplitConfig("AGV", new DataMapper(agvMask), agvValidator)
|
||||
/// .AddSplitConfig("ALARM", new DataMapper(alarmMask))
|
||||
/// .setSuccessHandler(splitData => {
|
||||
/// .SetSuccessHandler(splitData => {
|
||||
/// // HttpDataFetcher 구현에 따라, 분할된 각 데이터가 처리된 후 이 핸들러가 호출될 수 있습니다.
|
||||
/// // 이 경우 핸들러의 IDataObject는 null일 수 있습니다.
|
||||
/// Console.WriteLine("Split data processing completed.");
|
||||
@@ -204,7 +204,7 @@ namespace UVC.Data.Http
|
||||
/// </summary>
|
||||
/// <param name="dataMapper">사용할 데이터 매퍼 객체</param>
|
||||
/// <returns>현재 HttpRequestConfig 인스턴스 (메서드 체이닝용)</returns>
|
||||
public HttpRequestConfig setDataMapper(DataMapper dataMapper)
|
||||
public HttpRequestConfig SetDataMapper(DataMapper dataMapper)
|
||||
{
|
||||
_dataMapper = dataMapper;
|
||||
return this;
|
||||
@@ -242,18 +242,18 @@ namespace UVC.Data.Http
|
||||
///
|
||||
/// // 3. 검사기를 HTTP 파이프라인에 설정
|
||||
/// var pipelineInfo = new HttpRequestConfig("https://api.example.com/users", "get")
|
||||
/// .setDataMapper(userDataMapper)
|
||||
/// .setValidator(validator)
|
||||
/// .setSuccessHandler(userData => {
|
||||
/// .SetDataMapper(userDataMapper)
|
||||
/// .SetValidator(validator)
|
||||
/// .SetSuccessHandler(userData => {
|
||||
/// // 여기에 도달하는 사용자 데이터는 모두 이메일이 유효하고 18세 이상입니다.
|
||||
/// Console.WriteLine($"유효한 사용자: {userData["name"]}, {userData["email"]}");
|
||||
/// })
|
||||
/// .setFailHandler(errorMsg => {
|
||||
/// .SetFailHandler(errorMsg => {
|
||||
/// Console.WriteLine($"요청 실패: {errorMsg}");
|
||||
/// });
|
||||
/// </code>
|
||||
/// </example>
|
||||
public HttpRequestConfig setValidator(DataValidator validator)
|
||||
public HttpRequestConfig SetValidator(DataValidator validator)
|
||||
{
|
||||
this._validator = validator;
|
||||
return this;
|
||||
@@ -281,7 +281,7 @@ namespace UVC.Data.Http
|
||||
/// </summary>
|
||||
/// <param name="responseMask">HTTP response에 적용할 <see cref="HttpResponseMask"/>입니다.</param>
|
||||
/// <returns>지정된 response 마스크가 적용된 업데이트된 <see cref="HttpRequestConfig"/> 인스턴스입니다.</returns>
|
||||
public HttpRequestConfig setResponseMask(HttpResponseMask responseMask)
|
||||
public HttpRequestConfig SetResponseMask(HttpResponseMask responseMask)
|
||||
{
|
||||
_responseMask = responseMask;
|
||||
return this;
|
||||
@@ -294,7 +294,7 @@ namespace UVC.Data.Http
|
||||
/// </summary>
|
||||
/// <param name="handler">응답 데이터를 처리할 콜백 함수</param>
|
||||
/// <returns>현재 HttpRequestConfig 인스턴스 (메서드 체이닝용)</returns>
|
||||
public HttpRequestConfig setSuccessHandler(Action<IDataObject?>? handler)
|
||||
public HttpRequestConfig SetSuccessHandler(Action<IDataObject?>? handler)
|
||||
{
|
||||
_successhandler = handler;
|
||||
return this;
|
||||
@@ -306,7 +306,7 @@ namespace UVC.Data.Http
|
||||
/// </summary>
|
||||
/// <param name="handler">실패 정보를 처리할 콜백 함수입니다. 실패 시 관련 데이터를 인자로 받습니다.</param>
|
||||
/// <returns>현재 HttpRequestConfig 인스턴스 (메서드 체이닝용)</returns>
|
||||
public HttpRequestConfig setFailHandler(Action<string>? handler)
|
||||
public HttpRequestConfig SetFailHandler(Action<string>? handler)
|
||||
{
|
||||
_failhandler = handler;
|
||||
return this;
|
||||
@@ -318,7 +318,7 @@ namespace UVC.Data.Http
|
||||
/// <param name="maxRetryCount">최대 재시도 횟수 (기본값: 3)</param>
|
||||
/// <param name="retryDelay">재시도 간 대기 시간(밀리초) (기본값: 1000)</param>
|
||||
/// <returns>현재 HttpRequestConfig 인스턴스 (메서드 체이닝용)</returns>
|
||||
public HttpRequestConfig setRetry(int maxRetryCount = 3, int retryDelay = 1000)
|
||||
public HttpRequestConfig SetRetry(int maxRetryCount = 3, int retryDelay = 1000)
|
||||
{
|
||||
_maxRetryCount = maxRetryCount;
|
||||
_retryDelay = retryDelay;
|
||||
@@ -341,16 +341,16 @@ namespace UVC.Data.Http
|
||||
/// <code>
|
||||
/// // 5초마다 10번 반복 요청, 변경된 데이터만 처리
|
||||
/// var pipelineInfo = new HttpRequestConfig("https://api.example.com/data", "GET")
|
||||
/// .setHandler(data => ProcessData(data))
|
||||
/// .setRepeat(true, 10, 5000, true);
|
||||
/// .SetHandler(data => ProcessData(data))
|
||||
/// .SetRepeat(true, 10, 5000, true);
|
||||
///
|
||||
/// // 3초마다 무한 반복, 모든 응답 데이터 처리
|
||||
/// var pipelineInfo = new HttpRequestConfig("https://api.example.com/status", "GET")
|
||||
/// .setHandler(data => UpdateStatus(data))
|
||||
/// .setRepeat(true, 0, 3000, false);
|
||||
/// .SetHandler(data => UpdateStatus(data))
|
||||
/// .SetRepeat(true, 0, 3000, false);
|
||||
/// </code>
|
||||
/// </example>
|
||||
public HttpRequestConfig setRepeat(bool repeat, int count = 0, int interval = 1000, bool updatedDataOnly = true)
|
||||
public HttpRequestConfig SetRepeat(bool repeat, int count = 0, int interval = 1000, bool updatedDataOnly = true)
|
||||
{
|
||||
_repeat = repeat;
|
||||
_repeatCount = count;
|
||||
@@ -366,11 +366,20 @@ namespace UVC.Data.Http
|
||||
/// </summary>
|
||||
/// <param name="split">분할 처리 여부</param>
|
||||
/// <returns>현재 HttpRequestConfig 인스턴스 (메서드 체이닝용)</returns>
|
||||
public HttpRequestConfig setSplitResponseByKey(bool split)
|
||||
public HttpRequestConfig SetSplitResponseByKey(bool split)
|
||||
{
|
||||
_splitResponseByKey = split;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 업데이트된 데이터만 처리할지 여부를 나타내는 값을 설정합니다.
|
||||
/// </summary>
|
||||
/// <param name="v">부울 값. <see langword="true"/>는 업데이트된 데이터만 처리하도록 지정합니다. 그렇지 않으면
|
||||
/// <see langword="false"/>입니다.</param>
|
||||
public void SetUpdatedDataOnly(bool v)
|
||||
{
|
||||
_updatedDataOnly = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +47,8 @@ namespace UVC.Data.Mqtt
|
||||
///
|
||||
/// // 4. MqttSubscriptionConfig 생성 및 설정
|
||||
/// var pipelineInfo = new MqttSubscriptionConfig("sensor/+/data")
|
||||
/// .setDataMapper(dataMapper)
|
||||
/// .setHandler(dataHandler);
|
||||
/// .SetDataMapper(dataMapper)
|
||||
/// .SetHandler(dataHandler);
|
||||
///
|
||||
/// // 5. MqttDataReceiver 인스턴스 생성
|
||||
/// var mqttReceiver = new MqttDataReceiver("mqtt.eclipseprojects.io", 1883);
|
||||
@@ -62,7 +62,7 @@ namespace UVC.Data.Mqtt
|
||||
/// // ... 애플리케이션 로직 수행 ...
|
||||
///
|
||||
/// // 8. 파이프라인 중지 및 리소스 해제
|
||||
/// mqttReceiver.Stop();
|
||||
/// mqttReceiver.Exit();
|
||||
/// mqttReceiver.Dispose();
|
||||
/// </code>
|
||||
/// </example>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#nullable enable
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using UVC.Data.Core;
|
||||
@@ -22,8 +22,8 @@ namespace UVC.Data.Mqtt
|
||||
/// dataMask["timestamp"] = DateTime.Now;
|
||||
///
|
||||
/// var config = new MqttSubscriptionConfig("device/status")
|
||||
/// .setDataMapper(new DataMapper(dataMask))
|
||||
/// .setHandler(data => Console.WriteLine(data));
|
||||
/// .SetDataMapper(new DataMapper(dataMask))
|
||||
/// .SetHandler(data => Console.WriteLine(data));
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class MqttSubscriptionConfig
|
||||
@@ -113,7 +113,7 @@ namespace UVC.Data.Mqtt
|
||||
/// 핸들러는 메시지가 수신되고 DataMapper에 의해 변환된 후 호출됩니다.
|
||||
/// UpdatedDataOnly 속성이 true인 경우, 데이터가 변경된 경우에만 호출됩니다.
|
||||
/// </remarks>
|
||||
public MqttSubscriptionConfig setHandler(Action<IDataObject?> handler)
|
||||
public MqttSubscriptionConfig SetHandler(Action<IDataObject?> handler)
|
||||
{
|
||||
_handler = handler;
|
||||
return this;
|
||||
@@ -137,8 +137,8 @@ namespace UVC.Data.Mqtt
|
||||
/// dataMask["timestamp"] = ""; // 문자열 타입 지정
|
||||
///
|
||||
/// var pipelineInfo = new MqttSubscriptionConfig("sensor/data")
|
||||
/// .setDataMapper(new DataMapper(dataMask))
|
||||
/// .setHandler(data => ProcessSensorData(data));
|
||||
/// .SetDataMapper(new DataMapper(dataMask))
|
||||
/// .SetHandler(data => ProcessSensorData(data));
|
||||
/// </code>
|
||||
/// </example>
|
||||
public MqttSubscriptionConfig setDataMapper(DataMapper dataMapper)
|
||||
@@ -177,9 +177,9 @@ namespace UVC.Data.Mqtt
|
||||
///
|
||||
/// // Validator를 파이프라인에 설정
|
||||
/// var pipelineInfo = new MqttSubscriptionConfig("sensors/data")
|
||||
/// .setDataMapper(dataMapper)
|
||||
/// .setValidator(_validator)
|
||||
/// .setHandler(data => {
|
||||
/// .SetDataMapper(dataMapper)
|
||||
/// .SetValidator(_validator)
|
||||
/// .SetHandler(data => {
|
||||
/// // 여기서 처리되는 데이터는 모두 유효성 검사를 통과한 데이터
|
||||
/// Console.WriteLine($"유효한 센서 데이터: {data["deviceId"]} - {data["temperature"]}°C");
|
||||
/// });
|
||||
|
||||
@@ -40,9 +40,9 @@ namespace UVC.Data.Mqtt
|
||||
|
||||
/// <summary>
|
||||
/// 스레드를 안전하게 종료시키기 위한 CancellationTokenSource 입니다.
|
||||
/// Stop() 메서드가 호출되면 취소 신호를 보냅니다.
|
||||
/// Exit() 메서드가 호출되면 취소 신호를 보냅니다.
|
||||
/// </summary>
|
||||
private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
||||
private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
private readonly Dictionary<string, List<MqttDataPacket>> topicBuffers = new Dictionary<string, List<MqttDataPacket>>();
|
||||
private readonly Dictionary<string, Action<string, List<MqttDataPacket>>> listeners = new Dictionary<string, Action<string, List<MqttDataPacket>>>();
|
||||
@@ -101,6 +101,11 @@ namespace UVC.Data.Mqtt
|
||||
public void Start()
|
||||
{
|
||||
if (isRunning) return;
|
||||
if (cancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
cancellationTokenSource.Dispose();
|
||||
cancellationTokenSource = new CancellationTokenSource();
|
||||
}
|
||||
isRunning = true;
|
||||
workerThread = new Thread(Run);
|
||||
workerThread.IsBackground = true; // 메인 앱 종료 시 스레드 자동 종료
|
||||
@@ -228,7 +233,7 @@ namespace UVC.Data.Mqtt
|
||||
// 다음 전파 주기까지 대기합니다.
|
||||
if (cancellationToken.WaitHandle.WaitOne(propagationIntervalMs))
|
||||
{
|
||||
break; // Stop() 호출 시 루프 종료
|
||||
break; // Exit() 호출 시 루프 종료
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException) { break; }
|
||||
@@ -236,7 +241,7 @@ namespace UVC.Data.Mqtt
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 직접 전파 모드의 메인 루프입니다. Stop()이 호출될 때까지 스레드를 대기시킵니다.
|
||||
/// 직접 전파 모드의 메인 루프입니다. Exit()이 호출될 때까지 스레드를 대기시킵니다.
|
||||
/// </summary>
|
||||
private void RunDirectPropagationLoop()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UVC.Core;
|
||||
@@ -103,6 +103,8 @@ namespace UVC.Factory
|
||||
|
||||
private Coroutine focusCoroutine; // 현재 실행 중인 포커싱 코루틴을 저장할 변수
|
||||
|
||||
public bool Enable = false; // 카메라 컨트롤 활성화 여부
|
||||
|
||||
void Start()
|
||||
{
|
||||
// 스크립트 시작 시, 회전의 기준이 되는 중심점을 카메라 앞쪽으로 초기화합니다.
|
||||
@@ -207,6 +209,7 @@ namespace UVC.Factory
|
||||
// 이를 통해 카메라의 떨림이나 끊김 현상을 줄일 수 있습니다.
|
||||
void LateUpdate()
|
||||
{
|
||||
if (!Enable) return; // 카메라 컨트롤이 비활성화된 경우, 업데이트를 건너뜁니다.
|
||||
HandlePanning();
|
||||
HandleRotation();
|
||||
HandleZoom();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UVC.Data.Core;
|
||||
using UVC.Factory.Playback;
|
||||
|
||||
namespace UVC.Factory.Component
|
||||
{
|
||||
@@ -26,17 +27,32 @@ namespace UVC.Factory.Component
|
||||
|
||||
// 움직임과 회전의 부드러움을 조절할 속도 변수입니다.
|
||||
// Unity 인스펙터 창에서 실시간으로 값을 조절하며 최적의 움직임을 찾을 수 있습니다.
|
||||
[Tooltip("목표 지점까지의 이동 속도를 조절합니다.")]
|
||||
public float moveSpeed = 1.0f;
|
||||
[Tooltip("목표 지점까지 도달하는 데 걸리는 시간(초)입니다. 작을수록 빠릅니다.")]
|
||||
[SerializeField]
|
||||
private float moveSpeed = 0.9f;
|
||||
|
||||
[Tooltip("목표 방향까지의 회전 속도를 조절합니다.")]
|
||||
public float rotationSpeed = 2.0f;
|
||||
[Tooltip("목표 방향까지 도달하는 데 걸리는 시간(초)입니다. 작을수록 빠릅니다.")]
|
||||
[SerializeField]
|
||||
private float rotationSpeed = 0.5f;
|
||||
|
||||
[Tooltip("이 거리(미터)를 초과하면 보간 없이 즉시 위치를 변경합니다.")]
|
||||
public float teleportDistanceThreshold = 5.0f; // 5미터 이상 차이나면 순간이동
|
||||
[SerializeField]
|
||||
private float teleportDistanceThreshold = 2.0f; // 1미터 이상 차이나면 순간이동
|
||||
|
||||
[Tooltip("이 각도를 초과하면 보간 없이 즉시 회전각을 변경합니다.")]
|
||||
[SerializeField]
|
||||
private float teleportRotationThreshold = 45.0f; // 5도 이상 차이나면 순간이동
|
||||
|
||||
|
||||
private Renderer renderer;
|
||||
private Renderer? renderer;
|
||||
|
||||
private bool isRed = false;
|
||||
|
||||
private float timeScale = 1.0f; // 현재 시간 스케일, 기본값은 1.0f (실시간)
|
||||
|
||||
// SmoothDamp 함수가 사용하는 현재 속도 값입니다. 0으로 초기화해야 합니다.
|
||||
private Vector3 velocity = Vector3.zero;
|
||||
private float angularVelocity = 0;
|
||||
|
||||
/// <summary>
|
||||
/// AGV 객체가 생성될 때 처음 한 번 호출되는 초기화 메서드입니다.
|
||||
@@ -61,6 +77,18 @@ namespace UVC.Factory.Component
|
||||
"JOB_ID",
|
||||
"TIMESTAMP",
|
||||
};
|
||||
PlaybackService.Instance.OnChangeTimeScale += OnChangeTimeScaleHandler;
|
||||
}
|
||||
|
||||
private void OnChangeTimeScaleHandler(float timeScale)
|
||||
{
|
||||
this.timeScale = timeScale;
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
PlaybackService.Instance.OnChangeTimeScale -= OnChangeTimeScaleHandler;
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -118,8 +146,6 @@ namespace UVC.Factory.Component
|
||||
|
||||
bool changed = false;
|
||||
|
||||
bool isTeleport = false;
|
||||
|
||||
float? newX = newData.GetFloat("X");
|
||||
float? newY = newData.GetFloat("Y");
|
||||
float x = data.GetFloat("X").Value;
|
||||
@@ -137,7 +163,7 @@ namespace UVC.Factory.Component
|
||||
if (distanceToTarget > teleportDistanceThreshold)
|
||||
{
|
||||
transform.position = newTargetPosition;
|
||||
isTeleport = true; // 순간이동이 발생했음을 표시합니다.
|
||||
velocity = Vector3.zero; // 순간이동 후 속도 초기화
|
||||
}
|
||||
|
||||
// 새로운 목표 지점을 설정합니다.
|
||||
@@ -152,8 +178,18 @@ namespace UVC.Factory.Component
|
||||
{
|
||||
Quaternion newTargetRotation = Quaternion.Euler(0, newDegree.Value, 0);
|
||||
|
||||
// 거리가 설정된 임계값을 초과하면, 보간을 건너뛰고 즉시 위치/회전을 설정합니다
|
||||
if (isTeleport) transform.rotation = newTargetRotation;
|
||||
float distanceToTargetRotation = Quaternion.Angle(transform.rotation, newTargetRotation);
|
||||
|
||||
// 현재 회전과 새로운 목표 회전 사이의 각도 차이를 계산합니다.
|
||||
if (distanceToTargetRotation > 0)
|
||||
{
|
||||
// 각도 차이가 설정된 임계값을 초과하면, 보간을 건너뛰고 즉시 회전을 설정합니다.
|
||||
if (distanceToTargetRotation > teleportRotationThreshold)
|
||||
{
|
||||
transform.rotation = newTargetRotation;
|
||||
angularVelocity = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
// 새로운 목표 지점을 설정합니다.
|
||||
// (순간이동을 했든 안 했든, 다음 프레임부터의 보간을 위해 목표 지점은 항상 갱신되어야 합니다.)
|
||||
@@ -174,7 +210,9 @@ namespace UVC.Factory.Component
|
||||
|
||||
private void ChangeColor(Color color)
|
||||
{
|
||||
renderer.material.color = color;
|
||||
if(color == Color.red && isRed && renderer != null) return; // 이미 빨간색이면 변경하지 않음
|
||||
isRed = color == Color.red;
|
||||
if(renderer != null) renderer!.material.color = color;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -183,36 +221,41 @@ namespace UVC.Factory.Component
|
||||
/// </summary>
|
||||
void Update()
|
||||
{
|
||||
// 현재 위치가 목표 위치와 다를 경우에만 이동 로직을 실행합니다.
|
||||
if (transform.position != targetPosition)
|
||||
bool isMoving = Vector3.SqrMagnitude(transform.position - targetPosition) > 0.001f;
|
||||
bool isRotating = Quaternion.Angle(transform.rotation, targetRotation) > 0.01f;
|
||||
|
||||
// 이동과 회전이 모두 끝났으면 더 이상 계산할 필요가 없습니다.
|
||||
if (!isMoving && !isRotating)
|
||||
{
|
||||
// 목표 지점과의 거리가 매우 가까우면 (0.01미터 미만) 그냥 목표 위치로 설정하여 미세한 떨림을 방지합니다.
|
||||
if (Vector3.Distance(transform.position, targetPosition) < 0.01f)
|
||||
{
|
||||
// 현재 위치와 목표 위치 사이의 거리가 임계값을 초과하면 순간이동합니다.
|
||||
transform.position = targetPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Vector3.Lerp를 사용하여 현재 위치에서 목표 위치로 부드럽게 이동시킵니다.
|
||||
// Time.deltaTime * moveSpeed는 프레임 속도에 관계없이 일정한 속도를 보장합니다.
|
||||
transform.position = Vector3.Lerp(transform.position, targetPosition, Time.deltaTime * moveSpeed);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 현재 회전이 목표 회전과 다를 경우에만 회전 로직을 실행합니다.
|
||||
if (transform.rotation != targetRotation)
|
||||
float dampedTime = Time.deltaTime * timeScale;
|
||||
if (dampedTime <= 0) return;
|
||||
|
||||
// 위치 업데이트
|
||||
if (isMoving)
|
||||
{
|
||||
// 목표 회전과의 각도 차이가 매우 작으면 (0.1도 미만) 그냥 목표 회전으로 설정합니다.
|
||||
if (Quaternion.Angle(transform.rotation, targetRotation) < 0.01f)
|
||||
{
|
||||
transform.rotation = targetRotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Quaternion.Slerp를 사용하여 현재 회전에서 목표 회전으로 부드럽게 회전시킵니다.
|
||||
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * rotationSpeed);
|
||||
}
|
||||
transform.position = Vector3.SmoothDamp(transform.position, targetPosition, ref velocity, moveSpeed, Mathf.Infinity, dampedTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.position = targetPosition;
|
||||
velocity = Vector3.zero;
|
||||
}
|
||||
|
||||
// 회전 업데이트
|
||||
if (isRotating)
|
||||
{
|
||||
float currentAngle = transform.eulerAngles.y;
|
||||
float targetAngle = targetRotation.eulerAngles.y;
|
||||
float newAngle = Mathf.SmoothDampAngle(currentAngle, targetAngle, ref angularVelocity, rotationSpeed, Mathf.Infinity, dampedTime);
|
||||
transform.rotation = Quaternion.Euler(0, newAngle, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.rotation = targetRotation;
|
||||
angularVelocity = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,8 @@ namespace UVC.Factory.Component
|
||||
|
||||
private bool created = false;
|
||||
|
||||
public bool Created => created;
|
||||
|
||||
/// <summary>
|
||||
/// AGVManager의 초기화 메서드입니다.
|
||||
/// Awake 메서드에서 호출되며, MonoBehaviour가 생성될 때 한 번만 실행됩니다.
|
||||
@@ -168,7 +170,7 @@ namespace UVC.Factory.Component
|
||||
var RemovedItems = arr.RemovedItems;
|
||||
var ModifiedList = arr.ModifiedList;
|
||||
|
||||
//Debug.Log($"AGVManager received data: count:{arr.Count}, Added={AddedItems.Count}, Removed={RemovedItems.Count}, Modified={ModifiedList.Count}");
|
||||
Debug.Log($"AGVManager received data: count:{arr.Count}, Added={AddedItems.Count}, Removed={RemovedItems.Count}, Modified={ModifiedList.Count}");
|
||||
|
||||
// 새로 추가된 AGV 처리
|
||||
foreach (var item in AddedItems.ToList())
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#nullable enable
|
||||
using UnityEngine;
|
||||
using UVC.Data;
|
||||
using UVC.Factory.Playback.UI;
|
||||
@@ -9,9 +10,9 @@ namespace UVC.Factory.Playback
|
||||
{
|
||||
public class PlaybackCommand : ICommand
|
||||
{
|
||||
public async void Execute(object parameter = null)
|
||||
public async void Execute(object? parameter = null)
|
||||
{
|
||||
|
||||
FactoryCameraController.Instance.Enable = false;
|
||||
var modalContent = new ModalContent(UIPlaybackListModal.PrefabPath)
|
||||
{
|
||||
Title = "Playback List",
|
||||
@@ -19,21 +20,24 @@ namespace UVC.Factory.Playback
|
||||
ShowCancelButton = false
|
||||
};
|
||||
|
||||
object result = await UVC.UI.Modal.Modal.Open<bool>(modalContent);
|
||||
UIPlaybackListItemData? result = await UVC.UI.Modal.Modal.Open<UIPlaybackListItemData>(modalContent);
|
||||
|
||||
Debug.Log($"PlaybackCommand result:{result}");
|
||||
Debug.Log($"PlaybackCommand result==null:{result==null}");
|
||||
if (result != null)
|
||||
{
|
||||
UIPlaybackListItemData data = (UIPlaybackListItemData)result;
|
||||
UILoading.Show();
|
||||
UIPlaybackListItemData data = result;
|
||||
Debug.Log($"PlaybackCommand data:{data}");
|
||||
UIPlayback.Instance.Show();
|
||||
DataRepository.Instance.MqttReceiver.Stop();
|
||||
await UIPlayback.Instance.SetData(data.date, data.time, data.sqlFileName);
|
||||
await PlaybackService.Instance.StartAsync(data);
|
||||
FactoryCameraController.Instance.Enable = true;
|
||||
UILoading.Hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
UILoading.Show();
|
||||
PlaybackService.Instance.Stop();
|
||||
PlaybackService.Instance.Exit();
|
||||
FactoryCameraController.Instance.Enable = true;
|
||||
UILoading.Hide();
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace UVC.Factory.Playback
|
||||
{
|
||||
try
|
||||
{
|
||||
return HttpRequester.Download($"{Constants.PlaybackDomain}/playback/{fileName}", savePath, OnComplete, OnProgress, OnError);
|
||||
return HttpRequester.Download($"{URLList.Get("playbackFile")}/{fileName}", savePath, OnComplete, OnProgress, OnError);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using SQLite4Unity3d;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UVC.Factory.Playback;
|
||||
using UVC.Util;
|
||||
|
||||
namespace UVC.Factory
|
||||
@@ -32,7 +33,7 @@ namespace UVC.Factory
|
||||
{
|
||||
this.date = date;
|
||||
this.sqliteFileName = sqliteFileName;
|
||||
dbConnection = new SQLiteConnection(Path.Combine(Application.streamingAssetsPath, "playback", date, sqliteFileName));
|
||||
dbConnection = new SQLiteConnection(Path.Combine(PlaybackService.PlaybackFolderPath, date, sqliteFileName));
|
||||
}
|
||||
|
||||
public void CloseDB()
|
||||
@@ -78,6 +79,7 @@ namespace UVC.Factory
|
||||
};
|
||||
public async UniTask<List<PlaybackSQLiteDataEntity>> SelectBySecond(string selectTime, int second, bool orderAsc = true, int limit = 0)
|
||||
{
|
||||
bool isMainThread = PlayerLoopHelper.IsMainThread;
|
||||
List<PlaybackSQLiteDataEntity> result = await UniTask.RunOnThreadPool(() =>
|
||||
{
|
||||
DateTime date = DateTimeUtil.UtcParse(selectTime).AddSeconds(second);
|
||||
@@ -104,6 +106,7 @@ namespace UVC.Factory
|
||||
queryBuilder.Clear();
|
||||
return dbConnection.Query<PlaybackSQLiteDataEntity>(query);
|
||||
});
|
||||
if (!isMainThread) await UniTask.SwitchToThreadPool();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -122,6 +125,7 @@ namespace UVC.Factory
|
||||
StringBuilder queryBuilder = new();
|
||||
public async UniTask<List<PlaybackSQLiteDataEntity>> SelectBySecondBaseInfo(string selectTime, int second, bool orderAsc = false, int limit = 1)
|
||||
{
|
||||
bool isMainThread = PlayerLoopHelper.IsMainThread;
|
||||
List<PlaybackSQLiteDataEntity> result = await UniTask.RunOnThreadPool(() =>
|
||||
{
|
||||
DateTime date = DateTimeUtil.UtcParse(selectTime).AddSeconds(second);
|
||||
@@ -146,7 +150,7 @@ namespace UVC.Factory
|
||||
queryBuilder.Clear();
|
||||
return dbConnection.Query<PlaybackSQLiteDataEntity>(query);
|
||||
});
|
||||
|
||||
if (!isMainThread) await UniTask.SwitchToThreadPool();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#nullable enable
|
||||
#nullable enable
|
||||
using Best.HTTP;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UVC.Data;
|
||||
using UVC.Data.Core;
|
||||
using UVC.Data.Http;
|
||||
using UVC.Json;
|
||||
using UVC.Factory.Playback.UI;
|
||||
using UVC.Util;
|
||||
namespace UVC.Factory.Playback
|
||||
{
|
||||
@@ -20,14 +20,33 @@ namespace UVC.Factory.Playback
|
||||
static PlaybackService() { }
|
||||
#endregion
|
||||
|
||||
public static readonly string PlaybackFolderPath = Path.Combine(Application.persistentDataPath, "playback");//streamingAssetsPath, "playback"); appData 폴더로 변경
|
||||
|
||||
private readonly PlaybackRepository repository;
|
||||
|
||||
private string date;
|
||||
private string time;
|
||||
private string fileName;
|
||||
|
||||
public Action OnStopPlayback;
|
||||
public Action OnExitPlayback;
|
||||
|
||||
private float timeScale = 1.0f;
|
||||
public float TimeScale
|
||||
{
|
||||
get => timeScale;
|
||||
internal set
|
||||
{
|
||||
if (value < 1f) value = 1f;
|
||||
if (timeScale != value)
|
||||
{
|
||||
timeScale = value;
|
||||
//Time.timeScale = timeScale;
|
||||
OnChangeTimeScale?.Invoke(timeScale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Action<float> OnChangeTimeScale;
|
||||
|
||||
public PlaybackService(PlaybackRepository repository)
|
||||
{
|
||||
@@ -42,28 +61,29 @@ namespace UVC.Factory.Playback
|
||||
|
||||
public async UniTask DispatchBaseInfoData(string date, string time, string fileName, string minute = "00", string second = "00")
|
||||
{
|
||||
Debug.Log($"DispatchBaseInfoData {date} {time} {minute} {second} {fileName}");
|
||||
|
||||
//헝가리 시간임
|
||||
this.date = date;
|
||||
this.time = time;
|
||||
this.fileName = fileName;
|
||||
DateTime dateTime = DateTimeUtil.UtcParse($"{date}T{int.Parse(time).ToString("00")}:{minute}:{second}.000Z");
|
||||
string formatTime = DateTimeUtil.FormatTime(dateTime);
|
||||
//baseInfo 가져오기
|
||||
|
||||
List<PlaybackSQLiteDataEntity> list = await repository.SelectBySecondBaseInfo(date, fileName, formatTime);
|
||||
if (list.Count > 0)
|
||||
await UniTask.RunOnThreadPool(async () =>
|
||||
{
|
||||
HttpRequestConfig httpRequestConfig = new HttpRequestConfig("");
|
||||
httpRequestConfig.setSplitResponseByKey(true);
|
||||
httpRequestConfig.AddSplitConfig("AGV", DataMapperValidator.Get("AGV"));
|
||||
httpRequestConfig.AddSplitConfig("ALARM", DataMapperValidator.Get("ALARM"));
|
||||
foreach (var item in list)
|
||||
//헝가리 시간임
|
||||
this.date = date;
|
||||
this.time = time;
|
||||
this.fileName = fileName;
|
||||
DateTime dateTime = DateTimeUtil.UtcParse($"{date}T{int.Parse(time).ToString("00")}:{minute}:{second}.000Z");
|
||||
string formatTime = DateTimeUtil.FormatTime(dateTime);
|
||||
//baseInfo 가져오기
|
||||
List <PlaybackSQLiteDataEntity> list = await repository.SelectBySecondBaseInfo(date, fileName, formatTime);
|
||||
if (list.Count > 0)
|
||||
{
|
||||
HttpDataProcessor.ProcessSplitResponse(httpRequestConfig, item.data);
|
||||
HttpRequestConfig httpRequestConfig = new HttpRequestConfig("");
|
||||
httpRequestConfig.SetUpdatedDataOnly(true);
|
||||
httpRequestConfig.SetSplitResponseByKey(true);
|
||||
httpRequestConfig.AddSplitConfig("AGV", DataMapperValidator.Get("AGV"));
|
||||
httpRequestConfig.AddSplitConfig("ALARM", DataMapperValidator.Get("ALARM"));
|
||||
foreach (var item in list)
|
||||
{
|
||||
HttpDataProcessor.ProcessSplitResponse(httpRequestConfig, item.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -72,45 +92,55 @@ namespace UVC.Factory.Playback
|
||||
/// <param name="second">0 ~ 3600</param>
|
||||
public async UniTask DispatchRealTimeData(int second, int speed)
|
||||
{
|
||||
int newSecond = second;
|
||||
if (newSecond > 36000) newSecond = 36000;
|
||||
//utc 시간으로 변환
|
||||
DateTime dateTime = DateTimeUtil.UtcParse($"{date}T{int.Parse(time).ToString("00")}:00:00.000Z").AddSeconds(newSecond);//.Add(-DateTimeUtil.UtcKoreaGap);
|
||||
string formatTime = DateTimeUtil.FormatTime(dateTime);
|
||||
|
||||
List<PlaybackSQLiteDataEntity> list = await repository.SelectBySecondAsync(date, fileName, formatTime, 1);
|
||||
if (list.Count > 0)
|
||||
await UniTask.RunOnThreadPool(async () =>
|
||||
{
|
||||
HttpRequestConfig httpRequestConfig = new HttpRequestConfig("");
|
||||
httpRequestConfig.setSplitResponseByKey(true);
|
||||
httpRequestConfig.AddSplitConfig("AGV", DataMapperValidator.Get("AGV"));
|
||||
httpRequestConfig.AddSplitConfig("ALARM", DataMapperValidator.Get("ALARM"));
|
||||
foreach (var item in list)
|
||||
int newSecond = second;
|
||||
if (newSecond > 36000) newSecond = 36000;
|
||||
//utc 시간으로 변환
|
||||
DateTime dateTime = DateTimeUtil.UtcParse($"{date}T{int.Parse(time).ToString("00")}:00:00.000Z").AddSeconds(newSecond);//.Add(-DateTimeUtil.UtcKoreaGap);
|
||||
string formatTime = DateTimeUtil.FormatTime(dateTime);
|
||||
|
||||
List<PlaybackSQLiteDataEntity> list = await repository.SelectBySecondAsync(date, fileName, formatTime, 1);
|
||||
//Debug.Log($"DispatchRealTimeData {date} {time} {formatTime} {newSecond} {list.Count}");
|
||||
if (list.Count > 0)
|
||||
{
|
||||
HttpDataProcessor.ProcessSplitResponse(httpRequestConfig, item.data);
|
||||
HttpRequestConfig httpRequestConfig = new HttpRequestConfig("");
|
||||
httpRequestConfig.SetUpdatedDataOnly(true);
|
||||
httpRequestConfig.SetSplitResponseByKey(true);
|
||||
httpRequestConfig.AddSplitConfig("AGV", DataMapperValidator.Get("AGV"));
|
||||
httpRequestConfig.AddSplitConfig("ALARM", DataMapperValidator.Get("ALARM"));
|
||||
foreach (var item in list)
|
||||
{
|
||||
HttpDataProcessor.ProcessSplitResponse(httpRequestConfig, item.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public async Task StartAsync(UIPlaybackListItemData data)
|
||||
{
|
||||
timeScale = 1.0f; //기본 시간 스케일 설정
|
||||
UIPlayback.Instance.Show();
|
||||
await UIPlayback.Instance.SetData(data.date, data.time, data.sqlFileName);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
public void Exit()
|
||||
{
|
||||
OnStopPlayback?.Invoke();
|
||||
OnExitPlayback?.Invoke();
|
||||
}
|
||||
|
||||
|
||||
public HTTPRequest? ReadyData(string date, string time, string fileName, Action<long, long, float> OnProgress, Action<string> OnComplete)
|
||||
{
|
||||
//date : "2024-12-05"
|
||||
//fileName : "2024-12-05_0.sqlite.7z"
|
||||
string playbackPath = Path.Combine(Application.streamingAssetsPath, "playback");
|
||||
string playbackPath = PlaybackService.PlaybackFolderPath;
|
||||
string tempPath = Path.Combine(playbackPath, "temp");//한국 시간으로 변경하기 때문에 임시 폴더 만들어서 압축 해제 후 이동
|
||||
string datePath = Path.Combine(playbackPath, date);
|
||||
var fileNameArr = fileName.Split(".");
|
||||
string zipFilePath = Path.Combine(datePath, fileName);
|
||||
string sqlFilePath = Path.Combine(datePath, fileNameArr[0] + ".sqlite");
|
||||
|
||||
DateTime utcDateTime = DateTimeUtil.Parse(fileNameArr[0], "yyyy-MM-dd_H").Add(-DateTimeUtil.UtcKoreaGap);
|
||||
DateTime utcDateTime = DateTimeUtil.Parse(fileNameArr[0], "yyyy-MM-dd_H");//.Add(-DateTimeUtil.UtcKoreaGap);
|
||||
string utcDatePath = Path.Combine(playbackPath, utcDateTime.ToString("yyyy-MM-dd"));
|
||||
string utcFileName = utcDateTime.ToString("yyyy-MM-dd_H") + "." + fileNameArr[1] + "." + fileNameArr[2];
|
||||
var utcFileNameArr = utcFileName.Split(".");
|
||||
@@ -144,15 +174,22 @@ namespace UVC.Factory.Playback
|
||||
if (File.Exists(utcZipFilePath))
|
||||
{
|
||||
if (OnProgress != null) OnProgress.Invoke(50, 100, 0.5f);
|
||||
//압축해제 후
|
||||
//압축해제 후
|
||||
var zipper = new Zipper();
|
||||
|
||||
string errorMessage = await zipper.Decompress(utcZipFilePath, tempPath, (long read, long total, float percent) =>
|
||||
{
|
||||
if (OnProgress != null)
|
||||
{
|
||||
bool isComplte = false;
|
||||
float percentRate = 0.5f + percent / 2;
|
||||
if (percentRate > 0.99)
|
||||
{
|
||||
percentRate = 0.99f;
|
||||
isComplte = true;
|
||||
}
|
||||
OnProgress.Invoke(downloadTotal + read, downloadTotal + total, 0.5f + percent / 2);
|
||||
if (0.5f + percent / 2 > 100f)
|
||||
if (isComplte)
|
||||
{
|
||||
Debug.Log($" DownloadReadData :{downloadTotal + read} , DownloadTotalData :{downloadTotal + total} ,DownloadPlaybackData OnProgress:{percent}");
|
||||
}
|
||||
@@ -181,16 +218,20 @@ namespace UVC.Factory.Playback
|
||||
// Debug.Log($"zipper3 errorMessage:{errorMessage} utcSqlFilePath:{utcSqlFilePath} sqlFilePath:{sqlFilePath} utcZipFilePath:{utcZipFilePath}");
|
||||
//}
|
||||
|
||||
//압축해제 한 파일 이동
|
||||
if (File.Exists(utcSqlFilePath))
|
||||
await UniTask.RunOnThreadPool(() =>
|
||||
{
|
||||
//동일한 파일명이 있을경우 제거후 다시
|
||||
File.Copy(utcSqlFilePath, sqlFilePath);
|
||||
File.Delete(utcSqlFilePath);
|
||||
}
|
||||
//압축해제 한 파일 이동
|
||||
if (File.Exists(utcSqlFilePath))
|
||||
{
|
||||
//동일한 파일명이 있을경우 제거후 다시
|
||||
File.Copy(utcSqlFilePath, sqlFilePath);
|
||||
File.Delete(utcSqlFilePath);
|
||||
}
|
||||
|
||||
//zip 파일 삭제
|
||||
File.Delete(utcZipFilePath);
|
||||
});
|
||||
|
||||
//zip 파일 삭제
|
||||
File.Delete(utcZipFilePath);
|
||||
if (OnComplete != null) OnComplete.Invoke(errorMessage);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -61,6 +61,8 @@ namespace UVC.Factory.Playback.UI
|
||||
private UIPlaybackProgressBar progressBar;
|
||||
[SerializeField]
|
||||
private CanvasGroup canvasGroup;
|
||||
[SerializeField]
|
||||
private UIDragger uiDragger;
|
||||
|
||||
private bool isPlaying = false;
|
||||
private bool preparingData = false;
|
||||
@@ -118,6 +120,7 @@ namespace UVC.Factory.Playback.UI
|
||||
if (canvas.name == "ModalCanvas")
|
||||
{
|
||||
transform.SetParent(canvas.transform, false);
|
||||
uiDragger.SetDragArea(canvas.transform as RectTransform);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -126,7 +129,7 @@ namespace UVC.Factory.Playback.UI
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
Time.timeScale = 1;
|
||||
UpdateTimeScale(1);
|
||||
IsTick = false;
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
@@ -137,8 +140,8 @@ namespace UVC.Factory.Playback.UI
|
||||
isPlaying = false;
|
||||
UpdatePlayState();
|
||||
Hide();
|
||||
PlaybackService.Instance.Stop();
|
||||
UILoading.Hide();
|
||||
PlaybackService.Instance.Exit();
|
||||
|
||||
}
|
||||
|
||||
private void OnClickPlay()
|
||||
@@ -156,11 +159,12 @@ namespace UVC.Factory.Playback.UI
|
||||
{
|
||||
if (isPlaying)
|
||||
{
|
||||
if (Time.timeScale != sliderSpeed.Value) Time.timeScale = sliderSpeed.Value;
|
||||
//if (Time.timeScale != sliderSpeed.Value) UpdateTimeScale(sliderSpeed.Value);
|
||||
UpdateTimeScale(sliderSpeed.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
Time.timeScale = 1;
|
||||
UpdateTimeScale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,24 +212,23 @@ namespace UVC.Factory.Playback.UI
|
||||
this.time = time;
|
||||
this.fileName = fileName;
|
||||
Debug.Log($"UIPlayback SetData {date} {time}");
|
||||
|
||||
int timeInt = int.Parse(time);
|
||||
dateTimeTxt0.text = dateTimeTxt1.text = date.Substring(2).Replace("-", ".");
|
||||
progressBar.Init(timeInt);
|
||||
sliderSpeed.Init();
|
||||
Time.timeScale = 1;
|
||||
|
||||
UpdateTimeScale(1);
|
||||
canvasGroup.alpha = opacitySlider.value = 1;
|
||||
preparingData = true;
|
||||
progressBar.Interactable = !preparingData;
|
||||
isPlaying = false;
|
||||
|
||||
UpdatePlayState();
|
||||
//UILoading.Show();
|
||||
await UniTask.WaitForSeconds(0.5f);
|
||||
|
||||
await PlaybackService.Instance.DispatchBaseInfoData(date, time, fileName);
|
||||
preparingData = false;
|
||||
progressBar.Interactable = !preparingData;
|
||||
|
||||
//UILoading.Hide();
|
||||
await UniTask.WaitForSeconds(0.5f);
|
||||
}
|
||||
|
||||
private void UpdatePlayState()
|
||||
@@ -249,7 +252,6 @@ namespace UVC.Factory.Playback.UI
|
||||
}
|
||||
progressBar.Value += 1;
|
||||
//PlaybackService.Instance.DispatchingTimelineEvent = false;
|
||||
|
||||
PlaybackService.Instance.DispatchRealTimeData(progressBar.Value, sliderSpeed.Value).Forget();
|
||||
|
||||
if (isTick)
|
||||
@@ -260,6 +262,11 @@ namespace UVC.Factory.Playback.UI
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTimeScale(float timeScale)
|
||||
{
|
||||
PlaybackService.Instance.TimeScale = timeScale;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Best.HTTP;
|
||||
using Best.HTTP;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -210,7 +210,7 @@ namespace UVC.Factory.Playback.UI
|
||||
{
|
||||
try
|
||||
{
|
||||
string playbackPath = Path.Combine(Application.streamingAssetsPath, "playback");
|
||||
string playbackPath = PlaybackService.PlaybackFolderPath;
|
||||
string tempPath = Path.Combine(playbackPath, "temp");
|
||||
string datePath = Path.Combine(playbackPath, data.date);
|
||||
var fileNameArr = data.zipFileName.Split('.');
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#nullable enable
|
||||
using Cysharp.Threading.Tasks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -19,23 +20,24 @@ namespace UVC.Factory.Playback.UI
|
||||
|
||||
public static UIPlaybackListModal CreateFromPrefab(Transform parent = null)
|
||||
{
|
||||
GameObject prefab = Resources.Load(PrefabPath, typeof(GameObject)) as GameObject;
|
||||
GameObject prefab = Resources.Load<GameObject>(PrefabPath);
|
||||
GameObject go = UnityEngine.Object.Instantiate(prefab);
|
||||
UIPlaybackListModal modal = go.GetComponent<UIPlaybackListModal>();
|
||||
return modal;
|
||||
}
|
||||
|
||||
private Dictionary<string, List<UIPlaybackListItemData>> data;
|
||||
private Dictionary<string, List<UIPlaybackListItemData>>? data;
|
||||
|
||||
public bool IsOkable => (selectedItem != null && selectedItem.status == UIPlaybackListItemStatus.Downloaded && UIPlaybackListItem.DownloadingItems.Count == 0);
|
||||
|
||||
private UIPlaybackListItemData? selectedItem;
|
||||
private UIPlaybackListItemData? selectedItem = null;
|
||||
|
||||
[SerializeField]
|
||||
private TMP_Dropdown dropdownDate;
|
||||
[SerializeField]
|
||||
private ScrollRect scrollRectTime;
|
||||
|
||||
private UIPlaybackListItemData? resultData = null;
|
||||
|
||||
public override async UniTask OnOpen(ModalContent content)
|
||||
{
|
||||
@@ -44,17 +46,15 @@ namespace UVC.Factory.Playback.UI
|
||||
initContent();
|
||||
}
|
||||
|
||||
public override object GetResult()
|
||||
public override object? GetResult()
|
||||
{
|
||||
if (data != null) data.Clear();
|
||||
data = null;
|
||||
return selectedItem;
|
||||
return resultData;
|
||||
}
|
||||
|
||||
public override async UniTask OnClose(ModalContent content)
|
||||
{
|
||||
|
||||
|
||||
await base.OnClose(content);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace UVC.Factory.Playback.UI
|
||||
{
|
||||
confirmButton.interactable = false;
|
||||
|
||||
Dictionary<string, Dictionary<string, string>> data = await PlaybackService.Instance.RequestDataAsync();
|
||||
Dictionary<string, Dictionary<string, string>>? data = await PlaybackService.Instance.RequestDataAsync();
|
||||
dropdownDate.onValueChanged.AddListener(DropDownDateChanged);
|
||||
LocalSetData();
|
||||
if (data != null) SetData(data);
|
||||
@@ -81,7 +81,7 @@ namespace UVC.Factory.Playback.UI
|
||||
Dictionary<string, List<UIPlaybackListItemData>> newData = new Dictionary<string, List<UIPlaybackListItemData>>();
|
||||
var dateList = new List<TMP_Dropdown.OptionData>();
|
||||
|
||||
string playbackPath = Path.Combine(Application.streamingAssetsPath, "playback");
|
||||
string playbackPath = PlaybackService.PlaybackFolderPath;
|
||||
DirectoryInfo di = new DirectoryInfo(playbackPath);
|
||||
if (di.Exists)
|
||||
{
|
||||
@@ -171,7 +171,7 @@ namespace UVC.Factory.Playback.UI
|
||||
var dateList = new List<TMP_Dropdown.OptionData>();
|
||||
|
||||
//로컬에 저장 되 있는데 sqlite 파일 찾아서 추가
|
||||
string playbackPath = Path.Combine(Application.streamingAssetsPath, "playback");
|
||||
string playbackPath = PlaybackService.PlaybackFolderPath;
|
||||
DirectoryInfo di = new DirectoryInfo(playbackPath);
|
||||
if (di.Exists)
|
||||
{
|
||||
@@ -307,5 +307,10 @@ namespace UVC.Factory.Playback.UI
|
||||
confirmButton.interactable = IsOkable;
|
||||
}
|
||||
|
||||
public override void OnConfirmButtonClicked()
|
||||
{
|
||||
resultData = selectedItem;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,8 +505,11 @@ namespace UVC.Network
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ULog.Error(req.State.ToString(), new Exception(resp.Message));
|
||||
OnError?.Invoke(req.State.ToString());
|
||||
string detailedError = req.Exception != null ? req.Exception.ToString() : (resp != null ? resp.Message : "Unknown error");
|
||||
string errorMsgDefault = $"Request failed! State: {req.State}, URL: {req.CurrentUri}, Error: {detailedError}";
|
||||
ULog.Error(errorMsgDefault, req.Exception);
|
||||
//ULog.Error(req.State.ToString(), req.Exception != null ? req.Exception: new Exception(resp?.Message));
|
||||
OnError?.Invoke(errorMsgDefault);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -250,8 +250,8 @@ namespace UVC.Tests.Data
|
||||
|
||||
// HttpRequestConfig 설정
|
||||
var info = new HttpRequestConfig("http://test.com")
|
||||
.setDataMapper(dataMapper)
|
||||
.setSuccessHandler((data) =>
|
||||
.SetDataMapper(dataMapper)
|
||||
.SetSuccessHandler((data) =>
|
||||
{
|
||||
Debug.Log("핸들러 호출됨");
|
||||
handlerCalled = true;
|
||||
@@ -308,8 +308,8 @@ namespace UVC.Tests.Data
|
||||
|
||||
// HttpRequestConfig 설정
|
||||
var info = new HttpRequestConfig("http://test.com")
|
||||
.setDataMapper(dataMapper)
|
||||
.setSuccessHandler((data) =>
|
||||
.SetDataMapper(dataMapper)
|
||||
.SetSuccessHandler((data) =>
|
||||
{
|
||||
handlerCalled = true;
|
||||
receivedData = data;
|
||||
@@ -385,8 +385,8 @@ namespace UVC.Tests.Data
|
||||
|
||||
// HttpRequestConfig 설정
|
||||
var info = new HttpRequestConfig(agvUrl, "get")
|
||||
.setDataMapper(dataMapper)
|
||||
.setSuccessHandler((data) =>
|
||||
.SetDataMapper(dataMapper)
|
||||
.SetSuccessHandler((data) =>
|
||||
{
|
||||
handlerCalled = true;
|
||||
receivedData = data;
|
||||
@@ -439,8 +439,8 @@ namespace UVC.Tests.Data
|
||||
|
||||
// HttpRequestConfig 설정
|
||||
var info = new HttpRequestConfig(alarmUrl, "get")
|
||||
.setDataMapper(dataMapper)
|
||||
.setSuccessHandler((data) =>
|
||||
.SetDataMapper(dataMapper)
|
||||
.SetSuccessHandler((data) =>
|
||||
{
|
||||
handlerCalled = true;
|
||||
receivedData = data;
|
||||
@@ -522,8 +522,8 @@ namespace UVC.Tests.Data
|
||||
{
|
||||
string key = item.Key;
|
||||
var info = new HttpRequestConfig(item.Value, "get")
|
||||
.setDataMapper(new DataMapper(dataMasks[key]))
|
||||
.setSuccessHandler((data) =>
|
||||
.SetDataMapper(new DataMapper(dataMasks[key]))
|
||||
.SetSuccessHandler((data) =>
|
||||
{
|
||||
handlerCallCount++;
|
||||
results[key] = data;
|
||||
@@ -573,8 +573,8 @@ namespace UVC.Tests.Data
|
||||
|
||||
// HttpRequestConfig 설정
|
||||
var info = new HttpRequestConfig(testUrl, "get")
|
||||
.setDataMapper(dataMapper)
|
||||
.setSuccessHandler((data) =>
|
||||
.SetDataMapper(dataMapper)
|
||||
.SetSuccessHandler((data) =>
|
||||
{
|
||||
handlerCalled = true;
|
||||
receivedData = data;
|
||||
@@ -651,8 +651,8 @@ namespace UVC.Tests.Data
|
||||
|
||||
// HttpRequestConfig 설정
|
||||
var info = new HttpRequestConfig(baseInfoUrl, "get")
|
||||
.setDataMapper(dataMapper)
|
||||
.setSuccessHandler((data) =>
|
||||
.SetDataMapper(dataMapper)
|
||||
.SetSuccessHandler((data) =>
|
||||
{
|
||||
handlerCalled = true;
|
||||
receivedData = data;
|
||||
@@ -737,8 +737,8 @@ namespace UVC.Tests.Data
|
||||
|
||||
// 반복 실행 설정을 포함한 HttpRequestConfig 생성
|
||||
var info = new HttpRequestConfig(testUrl, "get")
|
||||
.setDataMapper(dataMapper)
|
||||
.setSuccessHandler(async (data) =>
|
||||
.SetDataMapper(dataMapper)
|
||||
.SetSuccessHandler(async (data) =>
|
||||
{
|
||||
handlerCallCount++;
|
||||
if (data is DataObject dataObject)
|
||||
@@ -756,7 +756,7 @@ namespace UVC.Tests.Data
|
||||
MockHttpRequester.SetResponse(testUrl, mockResponses[handlerCallCount]);
|
||||
}
|
||||
})
|
||||
.setRepeat(true, expectedCallCount, repeatInterval, false);
|
||||
.SetRepeat(true, expectedCallCount, repeatInterval, false);
|
||||
|
||||
pipeLine.UseMockup = true;
|
||||
pipeLine.Add("repeatTest", info);
|
||||
@@ -811,9 +811,9 @@ namespace UVC.Tests.Data
|
||||
|
||||
// 무한 반복 설정을 포함한 HttpRequestConfig 생성
|
||||
var info = new HttpRequestConfig(testUrl, "get")
|
||||
.setDataMapper(dataMapper)
|
||||
.setSuccessHandler((data) => { handlerCallCount++; })
|
||||
.setRepeat(true, 0, repeatInterval, false); // 무한 반복 (repeatCount = 0)
|
||||
.SetDataMapper(dataMapper)
|
||||
.SetSuccessHandler((data) => { handlerCallCount++; })
|
||||
.SetRepeat(true, 0, repeatInterval, false); // 무한 반복 (repeatCount = 0)
|
||||
|
||||
pipeLine.UseMockup = true;
|
||||
pipeLine.Add("infiniteRepeatTest", info);
|
||||
@@ -879,14 +879,14 @@ namespace UVC.Tests.Data
|
||||
|
||||
// 두 개의 반복 요청 설정
|
||||
var info1 = new HttpRequestConfig(testUrl1, "get")
|
||||
.setDataMapper(dataMapper)
|
||||
.setSuccessHandler((data) => { handlerCallCount1++; })
|
||||
.setRepeat(true, 0, repeatInterval1, false);
|
||||
.SetDataMapper(dataMapper)
|
||||
.SetSuccessHandler((data) => { handlerCallCount1++; })
|
||||
.SetRepeat(true, 0, repeatInterval1, false);
|
||||
|
||||
var info2 = new HttpRequestConfig(testUrl2, "get")
|
||||
.setDataMapper(dataMapper)
|
||||
.setSuccessHandler((data) => { handlerCallCount2++; })
|
||||
.setRepeat(true, 0, repeatInterval2, false);
|
||||
.SetDataMapper(dataMapper)
|
||||
.SetSuccessHandler((data) => { handlerCallCount2++; })
|
||||
.SetRepeat(true, 0, repeatInterval2, false);
|
||||
|
||||
pipeLine.UseMockup = true;
|
||||
pipeLine.Add("repeatTest1", info1);
|
||||
@@ -961,13 +961,13 @@ namespace UVC.Tests.Data
|
||||
|
||||
// 반복 횟수가 지정된 HttpRequestConfig 생성
|
||||
var info = new HttpRequestConfig(testUrl, "get")
|
||||
.setDataMapper(dataMapper)
|
||||
.setSuccessHandler((data) =>
|
||||
.SetDataMapper(dataMapper)
|
||||
.SetSuccessHandler((data) =>
|
||||
{
|
||||
handlerCallCount++;
|
||||
receivedData.Add(data);
|
||||
})
|
||||
.setRepeat(true, repeatCount, repeatInterval, false);
|
||||
.SetRepeat(true, repeatCount, repeatInterval, false);
|
||||
|
||||
pipeLine.UseMockup = true;
|
||||
pipeLine.Add("countedRepeatTest", info);
|
||||
@@ -1162,14 +1162,14 @@ namespace UVC.Tests.Data
|
||||
});
|
||||
|
||||
var info = new HttpRequestConfig(testUrl)
|
||||
.setDataMapper(dataMapper)
|
||||
.setValidator(validator)
|
||||
.setSuccessHandler(data =>
|
||||
.SetDataMapper(dataMapper)
|
||||
.SetValidator(validator)
|
||||
.SetSuccessHandler(data =>
|
||||
{
|
||||
handlerCalled = true;
|
||||
receivedData = data;
|
||||
})
|
||||
.setFailHandler((message) =>
|
||||
.SetFailHandler((message) =>
|
||||
{
|
||||
Debug.LogError("Fail message: " + message);
|
||||
});
|
||||
@@ -1212,9 +1212,9 @@ namespace UVC.Tests.Data
|
||||
validator.AddValidator("status", value => value is string s && s == "active");
|
||||
|
||||
var info = new HttpRequestConfig(testUrl)
|
||||
.setDataMapper(dataMapper)
|
||||
.setValidator(validator)
|
||||
.setSuccessHandler(data =>
|
||||
.SetDataMapper(dataMapper)
|
||||
.SetValidator(validator)
|
||||
.SetSuccessHandler(data =>
|
||||
{
|
||||
handlerCalled = true; // 이 핸들러는 호출되지 않아야 함
|
||||
});
|
||||
@@ -1266,9 +1266,9 @@ namespace UVC.Tests.Data
|
||||
});
|
||||
|
||||
var info = new HttpRequestConfig(testUrl)
|
||||
.setDataMapper(dataMapper)
|
||||
.setValidator(validator)
|
||||
.setSuccessHandler(data =>
|
||||
.SetDataMapper(dataMapper)
|
||||
.SetValidator(validator)
|
||||
.SetSuccessHandler(data =>
|
||||
{
|
||||
handlerCalled = true;
|
||||
receivedData = data;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#nullable enable
|
||||
#nullable enable
|
||||
|
||||
using Cysharp.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
@@ -284,7 +284,7 @@ namespace UVC.Tests.Data
|
||||
|
||||
var pipelineInfo = new MqttSubscriptionConfig(topic, updatedDataOnly)
|
||||
.setDataMapper(new DataMapper(dataMasks[topic]))
|
||||
.setHandler(handlers[topic].HandleData);
|
||||
.SetHandler(handlers[topic].HandleData);
|
||||
|
||||
mqttReceiver.Add(pipelineInfo);
|
||||
}
|
||||
@@ -315,7 +315,7 @@ namespace UVC.Tests.Data
|
||||
// AGV 토픽만 등록
|
||||
var agvInfo = new MqttSubscriptionConfig("AGV", true)
|
||||
.setDataMapper(new DataMapper(dataMasks["AGV"]))
|
||||
.setHandler(handlers["AGV"].HandleData);
|
||||
.SetHandler(handlers["AGV"].HandleData);
|
||||
|
||||
mqttReceiver.Add(agvInfo);
|
||||
mqttReceiver.Start();
|
||||
@@ -350,7 +350,7 @@ namespace UVC.Tests.Data
|
||||
// UpdatedDataOnly가 true인 AGV 토픽 추가
|
||||
var agvInfo = new MqttSubscriptionConfig("AGV", true)
|
||||
.setDataMapper(new DataMapper(dataMasks["AGV"]))
|
||||
.setHandler(handlers["AGV"].HandleData);
|
||||
.SetHandler(handlers["AGV"].HandleData);
|
||||
|
||||
testPipeLine.Add(agvInfo);
|
||||
|
||||
@@ -382,7 +382,7 @@ namespace UVC.Tests.Data
|
||||
bool updatedDataOnly = topic != "ALL";
|
||||
var pipelineInfo = new MqttSubscriptionConfig(topic, updatedDataOnly)
|
||||
.setDataMapper(new DataMapper(dataMasks[topic]))
|
||||
.setHandler(handlers[topic].HandleData);
|
||||
.SetHandler(handlers[topic].HandleData);
|
||||
|
||||
testPipeLine.Add(pipelineInfo);
|
||||
}
|
||||
@@ -410,7 +410,7 @@ namespace UVC.Tests.Data
|
||||
|
||||
var pipelineInfo = new MqttSubscriptionConfig("AGV", true)
|
||||
.setDataMapper(new DataMapper(dataMasks["AGV"]))
|
||||
.setHandler(handlers["AGV"].HandleData);
|
||||
.SetHandler(handlers["AGV"].HandleData);
|
||||
|
||||
testPipeLine.Add(pipelineInfo);
|
||||
|
||||
@@ -434,7 +434,7 @@ namespace UVC.Tests.Data
|
||||
|
||||
var pipelineInfo = new MqttSubscriptionConfig("AGV", true)
|
||||
.setDataMapper(new DataMapper(dataMasks["AGV"]))
|
||||
.setHandler(handlers["AGV"].HandleData);
|
||||
.SetHandler(handlers["AGV"].HandleData);
|
||||
|
||||
testPipeLine.Add(pipelineInfo);
|
||||
|
||||
@@ -453,7 +453,7 @@ namespace UVC.Tests.Data
|
||||
|
||||
var pipelineInfo = new MqttSubscriptionConfig("AGV", true)
|
||||
.setDataMapper(new DataMapper(dataMasks["AGV"]))
|
||||
.setHandler(handlers["AGV"].HandleData);
|
||||
.SetHandler(handlers["AGV"].HandleData);
|
||||
|
||||
testPipeLine.Add(pipelineInfo);
|
||||
|
||||
@@ -484,7 +484,7 @@ namespace UVC.Tests.Data
|
||||
// 4. UpdatedDataOnly=true로 토픽 등록
|
||||
var pipelineInfo = new MqttSubscriptionConfig("AGV", true)
|
||||
.setDataMapper(new DataMapper(dataMask))
|
||||
.setHandler(handler.HandleData);
|
||||
.SetHandler(handler.HandleData);
|
||||
|
||||
pipeline.Add(pipelineInfo);
|
||||
|
||||
@@ -599,7 +599,7 @@ namespace UVC.Tests.Data
|
||||
var pipelineInfo = new MqttSubscriptionConfig("test_topic")
|
||||
.setDataMapper(dataMapper)
|
||||
.setValidator(validator)
|
||||
.setHandler(handler.HandleData);
|
||||
.SetHandler(handler.HandleData);
|
||||
|
||||
testPipeLine.Add(pipelineInfo);
|
||||
|
||||
@@ -638,7 +638,7 @@ namespace UVC.Tests.Data
|
||||
var pipelineInfo = new MqttSubscriptionConfig("test_topic")
|
||||
.setDataMapper(dataMapper)
|
||||
.setValidator(validator)
|
||||
.setHandler(handler.HandleData);
|
||||
.SetHandler(handler.HandleData);
|
||||
|
||||
testPipeLine.Add(pipelineInfo);
|
||||
|
||||
@@ -675,7 +675,7 @@ namespace UVC.Tests.Data
|
||||
var pipelineInfo = new MqttSubscriptionConfig("test_topic")
|
||||
.setDataMapper(dataMapper)
|
||||
.setValidator(validator)
|
||||
.setHandler(handler.HandleData);
|
||||
.SetHandler(handler.HandleData);
|
||||
|
||||
testPipeLine.Add(pipelineInfo);
|
||||
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UVC.UI.Commands
|
||||
{
|
||||
public class ActionCommand : ICommand
|
||||
{
|
||||
private readonly Action _action;
|
||||
private readonly Action<object> _actionWithParam;
|
||||
private readonly Action? _action;
|
||||
private readonly Action<object?>? _actionWithParam;
|
||||
|
||||
public ActionCommand(Action action)
|
||||
{
|
||||
_action = action ?? throw new ArgumentNullException(nameof(action));
|
||||
}
|
||||
|
||||
public ActionCommand(Action<object> actionWithParam)
|
||||
public ActionCommand(Action<object?> actionWithParam)
|
||||
{
|
||||
_actionWithParam = actionWithParam ?? throw new ArgumentNullException(nameof(actionWithParam));
|
||||
}
|
||||
|
||||
public void Execute(object parameter = null)
|
||||
public void Execute(object? parameter = null)
|
||||
{
|
||||
_action?.Invoke();
|
||||
_actionWithParam?.Invoke(parameter);
|
||||
@@ -31,7 +32,7 @@ namespace UVC.UI.Commands
|
||||
// 또는, ICommand<T> 인터페이스를 고려할 수도 있습니다 (아래 2번 방법).
|
||||
public class ActionCommand<T> : ICommand<T> // ICommand<T>를 구현
|
||||
{
|
||||
private readonly Action<T> _action;
|
||||
private readonly Action<T>? _action;
|
||||
private readonly T _defaultParameter;
|
||||
private bool _useDefaultParameterForParameterlessExecute;
|
||||
|
||||
@@ -39,7 +40,7 @@ namespace UVC.UI.Commands
|
||||
{
|
||||
_action = action ?? throw new ArgumentNullException(nameof(action));
|
||||
_useDefaultParameterForParameterlessExecute = true; // 기본적으로 default(T) 사용
|
||||
_defaultParameter = default(T);
|
||||
_defaultParameter = default(T)!;
|
||||
}
|
||||
|
||||
public ActionCommand(Action<T> action, T defaultParameter, bool useDefaultForParameterless = true)
|
||||
@@ -52,7 +53,7 @@ namespace UVC.UI.Commands
|
||||
// ICommand<T>의 Start(T parameter) 구현
|
||||
public void Execute(T parameter)
|
||||
{
|
||||
_action.Invoke(parameter);
|
||||
_action?.Invoke(parameter);
|
||||
}
|
||||
|
||||
// ICommand<T> 인터페이스에 의해 추가된 파라미터 없는 Start()
|
||||
@@ -75,7 +76,7 @@ namespace UVC.UI.Commands
|
||||
|
||||
|
||||
// ICommand의 Start(object parameter = null) 구현
|
||||
void ICommand.Execute(object parameter) // 명시적 인터페이스 구현
|
||||
void ICommand.Execute(object? parameter) // 명시적 인터페이스 구현
|
||||
{
|
||||
if (parameter is T typedParameter)
|
||||
{
|
||||
@@ -91,7 +92,7 @@ namespace UVC.UI.Commands
|
||||
else
|
||||
{
|
||||
// T가 참조 타입이면 default(T)는 null. 값 타입이면 0, false 등.
|
||||
Execute(default(T));
|
||||
Execute(default(T)!);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using UnityEngine;
|
||||
#nullable enable
|
||||
using UnityEngine;
|
||||
using UVC.Locale;
|
||||
using UVC.Log;
|
||||
|
||||
namespace UVC.UI.Commands
|
||||
{
|
||||
@@ -15,7 +15,7 @@ namespace UVC.UI.Commands
|
||||
_languageCode = languageCode;
|
||||
}
|
||||
|
||||
public void Execute(object parameter = null)
|
||||
public void Execute(object? parameter = null)
|
||||
{
|
||||
string targetLanguage = _languageCode;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using UVC.Log;
|
||||
#nullable enable
|
||||
using UVC.Log;
|
||||
|
||||
namespace UVC.UI.Commands
|
||||
{
|
||||
@@ -12,7 +13,7 @@ namespace UVC.UI.Commands
|
||||
_message = message;
|
||||
}
|
||||
|
||||
public void Execute(object parameter = null)
|
||||
public void Execute(object? parameter = null)
|
||||
{
|
||||
string finalMessage = _message;
|
||||
if (parameter != null)
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
namespace UVC.UI.Commands
|
||||
#nullable enable
|
||||
namespace UVC.UI.Commands
|
||||
{
|
||||
public interface ICommand
|
||||
{
|
||||
void Execute(object parameter = null);
|
||||
void Execute(object? parameter = null);
|
||||
}
|
||||
|
||||
public interface ICommand<T> : ICommand
|
||||
{
|
||||
void Execute(T parameter);
|
||||
|
||||
void Execute() => Execute(default(T)); // 기본 Start 구현 제공 가능
|
||||
void Execute()
|
||||
{
|
||||
// CS8604: T가 참조형이고 null이 허용되지 않을 때 경고가 발생하므로,
|
||||
// T가 null 허용 타입이거나 값 타입일 때만 default(T)를 전달합니다.
|
||||
// 그렇지 않으면, 명시적으로 default(T)를 전달하되, T가 null 허용 타입임을 명시합니다.
|
||||
Execute(default(T)!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using UnityEngine;
|
||||
#nullable enable
|
||||
using UnityEngine;
|
||||
|
||||
namespace UVC.UI.Commands.Mono
|
||||
{
|
||||
@@ -9,7 +10,7 @@ namespace UVC.UI.Commands.Mono
|
||||
{
|
||||
// MonoCommand는 MonoBehaviour를 상속받아 Unity의 생명주기를 활용할 수 있습니다.
|
||||
// ICommand 인터페이스를 구현하여 명령 패턴을 따릅니다.
|
||||
public virtual void Execute(object parameter = null)
|
||||
public virtual void Execute(object? parameter = null)
|
||||
{
|
||||
// 기본 실행 로직 (필요시 override 가능)
|
||||
Debug.Log("MonoCommand executed.");
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using UnityEngine;
|
||||
#nullable enable
|
||||
using UnityEngine;
|
||||
|
||||
namespace UVC.UI.Commands
|
||||
{
|
||||
// 애플리케이션 종료 커맨드
|
||||
public class QuitApplicationCommand : ICommand
|
||||
{
|
||||
public void Execute(object parameter = null)
|
||||
public void Execute(object? parameter = null)
|
||||
{
|
||||
// 파라미터는 여기서는 사용되지 않을 수 있음
|
||||
if (parameter != null)
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace UVC.UI.Loading
|
||||
private bool animatting = false;
|
||||
private Transform loadingImageTransform;
|
||||
|
||||
private float loadingSpeed = 1.5f;
|
||||
private float loadingSpeed = -1.5f;
|
||||
private float rotationSpeed = -1.0f;
|
||||
|
||||
private void Awake()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#nullable enable
|
||||
#nullable enable
|
||||
using Cysharp.Threading.Tasks;
|
||||
using System; // System.Type 사용을 위해 추가
|
||||
using System.Threading;
|
||||
@@ -234,19 +234,19 @@ namespace UVC.UI.Modal
|
||||
// 기존 리스너 제거 후 새 리스너 추가
|
||||
modalView.confirmButton.onClick.RemoveAllListeners();
|
||||
// content.ShowConfirmButton 여부는 ModalView.OnOpen에서 버튼 자체의 활성화로 처리
|
||||
modalView.confirmButton.onClick.AddListener(() => _ = HandleModalActionAsync(content, tcs, true, modalView));
|
||||
modalView.confirmButton.onClick.AddListener(() => _ = HandleModalActionAsync(content, tcs, true, modalView, "confirm"));
|
||||
}
|
||||
|
||||
if (modalView.cancelButton != null)
|
||||
{
|
||||
modalView.cancelButton.onClick.RemoveAllListeners();
|
||||
modalView.cancelButton.onClick.AddListener(() => _ = HandleModalActionAsync(content, tcs, false, modalView));
|
||||
modalView.cancelButton.onClick.AddListener(() => _ = HandleModalActionAsync(content, tcs, false, modalView, "close"));
|
||||
}
|
||||
|
||||
if (modalView.closeButton != null)
|
||||
{
|
||||
modalView.closeButton.onClick.RemoveAllListeners();
|
||||
modalView.closeButton.onClick.AddListener(() => _ = HandleModalActionAsync(content, tcs, false, modalView));
|
||||
modalView.closeButton.onClick.AddListener(() => _ = HandleModalActionAsync(content, tcs, false, modalView, "close"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +263,8 @@ namespace UVC.UI.Modal
|
||||
ModalContent content,
|
||||
UniTaskCompletionSource<T> tcs,
|
||||
bool isConfirmAction,
|
||||
ModalView modalViewContext)
|
||||
ModalView modalViewContext,
|
||||
string buttonType)
|
||||
{
|
||||
// 📜 이야기: 이 함수는 사용자가 버튼을 눌렀을 때 실행돼요.
|
||||
// 그런데 만약 이전에 처리하던 약속 증서(activeTcs)와 지금 받은 증서(tcs)가 다르거나,
|
||||
@@ -277,6 +278,19 @@ namespace UVC.UI.Modal
|
||||
// 📜 이야기: 사용자가 버튼을 하나 눌렀으니, 다른 버튼들은 잠깐 못 누르게 막아요. (실수로 두 번 누르는 것 방지)
|
||||
modalViewContext.SetAllButtonsInteractable(false);
|
||||
|
||||
if(buttonType == "confirm")
|
||||
{
|
||||
modalViewContext.OnConfirmButtonClicked();
|
||||
}
|
||||
else if (buttonType == "cancel")
|
||||
{
|
||||
modalViewContext.OnCancelButtonClicked();
|
||||
}
|
||||
else if (buttonType == "close")
|
||||
{
|
||||
modalViewContext.OnCloseButtonClicked();
|
||||
}
|
||||
|
||||
// 📜 이야기: 이제 모달을 닫고 뒷정리를 할 시간이에요!
|
||||
// CleanupCurrentModalResources 조수에게 "이 모달 뷰를 사용했고, 사용자는 '확인'(또는 '취소')을 눌렀어요" 라고 알려주며 뒷정리를 부탁해요.
|
||||
// 이 뒷정리 과정에서 '약속 증서'에 최종 결과가 기록될 거예요.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
#nullable enable
|
||||
using Cysharp.Threading.Tasks;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
@@ -260,7 +261,7 @@ namespace UVC.UI.Modal
|
||||
/// // Modal.Open<string>(...) 이렇게 호출하면, 입력된 문자열을 받을 수 있어요.
|
||||
/// </code>
|
||||
/// </example>
|
||||
public virtual object GetResult()
|
||||
public virtual object? GetResult()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -277,5 +278,25 @@ namespace UVC.UI.Modal
|
||||
if (closeButton != null) closeButton.interactable = interactable;
|
||||
}
|
||||
|
||||
public virtual void OnConfirmButtonClicked()
|
||||
{
|
||||
// 확인 버튼이 눌렸을 때 호출되는 메서드예요.
|
||||
// 기본적으로는 아무것도 하지 않지만, 필요하면 재정의해서 특별한 동작을 추가할 수 있어요.
|
||||
// 예: ULog.Debug("확인 버튼이 눌렸어요!");
|
||||
}
|
||||
|
||||
public virtual void OnCancelButtonClicked()
|
||||
{
|
||||
// 취소 버튼이 눌렸을 때 호출되는 메서드예요.
|
||||
// 기본적으로는 아무것도 하지 않지만, 필요하면 재정의해서 특별한 동작을 추가할 수 있어요.
|
||||
// 예: ULog.Debug("취소 버튼이 눌렸어요!");
|
||||
}
|
||||
|
||||
public virtual void OnCloseButtonClicked()
|
||||
{
|
||||
// 닫기 버튼이 눌렸을 때 호출되는 메서드예요.
|
||||
// 기본적으로는 아무것도 하지 않지만, 필요하면 재정의해서 특별한 동작을 추가할 수 있어요.
|
||||
// 예: ULog.Debug("닫기 버튼이 눌렸어요!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,13 +94,25 @@ namespace UVC.UI
|
||||
dragArea = GetComponentInParent<Canvas>()?.transform as RectTransform;
|
||||
if (dragArea == null)
|
||||
{
|
||||
Debug.LogError("<b>[UIDragger]</b> 드래그 영역(dragArea)으로 사용할 Canvas를 찾을 수 없습니다.", this);
|
||||
Debug.Log("<b>[UIDragger]</b> 드래그 영역(dragArea)으로 사용할 Canvas를 찾을 수 없습니다.", this);
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 드래그가 허용되는 영역을 설정합니다.
|
||||
/// </summary>
|
||||
/// <remarks>이 메서드를 활성화하면 지정된 영역 내에서 드래그 기능이 활성화됩니다.
|
||||
///</remarks>
|
||||
/// <param name="area">드래그 영역의 경계를 정의하는 <see cref="RectTransform"/>입니다. null일 수 없습니다.</param>
|
||||
public void SetDragArea(RectTransform area)
|
||||
{
|
||||
dragArea = area;
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 드래그가 시작될 때 호출됩니다. (IBeginDragHandler)
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user