playback 기능 추가
This commit is contained in:
43
Assets/Scripts/UVC/Factory/Playback/PlaybackCommand.cs
Normal file
43
Assets/Scripts/UVC/Factory/Playback/PlaybackCommand.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using UnityEngine;
|
||||
using UVC.Data;
|
||||
using UVC.Factory.Playback.UI;
|
||||
using UVC.UI.Commands;
|
||||
using UVC.UI.Loading;
|
||||
using UVC.UI.Modal;
|
||||
|
||||
namespace UVC.Factory.Playback
|
||||
{
|
||||
public class PlaybackCommand : ICommand
|
||||
{
|
||||
public async void Execute(object parameter = null)
|
||||
{
|
||||
|
||||
var modalContent = new ModalContent("Prefabs/Factory/Playback/UIPlaybackListModal")
|
||||
{
|
||||
Title = "Playback List",
|
||||
ConfirmButtonText = "Play",
|
||||
ShowCancelButton = false
|
||||
};
|
||||
|
||||
object result = await UVC.UI.Modal.Modal.Open<bool>(modalContent);
|
||||
|
||||
Debug.Log($"PlaybackCommand result:{result}");
|
||||
if (result != null)
|
||||
{
|
||||
UIPlaybackListItemData data = (UIPlaybackListItemData)result;
|
||||
Debug.Log($"PlaybackCommand data:{data}");
|
||||
UIPlayback.Instance.Show();
|
||||
DataRepository.Instance.MqttReceiver.Stop();
|
||||
await UIPlayback.Instance.SetData(data.date, data.time, data.sqlFileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
UILoading.Show();
|
||||
PlaybackService.Instance.Stop();
|
||||
UILoading.Hide();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 071f03b6e7e6d0342a5fe94183a1db70
|
||||
107
Assets/Scripts/UVC/Factory/Playback/PlaybackRepository.cs
Normal file
107
Assets/Scripts/UVC/Factory/Playback/PlaybackRepository.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
#nullable enable
|
||||
using Best.HTTP;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using SampleProject.Config;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UVC.Network;
|
||||
|
||||
namespace UVC.Factory.Playback
|
||||
{
|
||||
public class PlaybackRepository
|
||||
{
|
||||
|
||||
private PlaybackSQLiteService? sqliteService = null;
|
||||
|
||||
public async UniTask<Dictionary<string, Dictionary<string, string>>?> RequestPlaybackDateList()
|
||||
{
|
||||
|
||||
//local
|
||||
//string path = System.IO.Path.Combine(Application.streamingAssetsPath, "playbackList.json");
|
||||
//string json = System.IO.File.ReadAllText(path);
|
||||
|
||||
//HttpResponseModel<Dictionary<string, Dictionary<string, string>>> localData = JsonHelper.FromJson<HttpResponseModel<Dictionary<string, Dictionary<string, string>>>>(json);
|
||||
//return new EntityWithState<Dictionary<string, Dictionary<string, string>>>(APIState.Loaded, localData.data);
|
||||
|
||||
try
|
||||
{
|
||||
return await UniTask.RunOnThreadPool<Dictionary<string, Dictionary<string, string>>?>(async () =>
|
||||
{
|
||||
var response = await HttpRequester.RequestGet<HttpResponseModel<Dictionary<string, Dictionary<string, string>>>>($"{Constants.PlaybackDomain}/playback/list");
|
||||
if (response.message.ToLower() == "success")
|
||||
{
|
||||
return new Dictionary<string, Dictionary<string, string>>(response.data);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Log($"Exception {e.ToString()}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public HTTPRequest? DownloadPlaybackData(string fileName, string savePath, Action<long, long> OnProgress, Action OnComplete, Action<string> OnError)
|
||||
{
|
||||
try
|
||||
{
|
||||
return HttpRequester.Download($"{Constants.PlaybackDomain}/playback/{fileName}", savePath, OnComplete, OnProgress, OnError);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Log($"Exception {e.ToString()}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// selectTime보다 +- second 사이의 데이터 요청. selectTime, second 포함
|
||||
/// </summary>
|
||||
/// <param name="date"></param>
|
||||
/// <param name="sqlFileName"></param>
|
||||
/// <param name="selectTime">yyyy-MM-ddTHH:mm:ss.fffZ format string</param>
|
||||
/// <param name="second"></param>
|
||||
/// <param name="orderAsc">true: 오래된 시간이 먼저, false: 최근 시간이 먼저</param>
|
||||
/// <param name="limit"></param>
|
||||
/// <returns></returns>
|
||||
public async UniTask<List<PlaybackSQLiteDataEntity>> SelectBySecondAsync(string date, string sqlFileName, string selectTime, int second, bool orderAsc = true, int limit = 0)
|
||||
{
|
||||
validationSqliteService(date, sqlFileName);
|
||||
return await sqliteService!.SelectBySecond(selectTime, second, orderAsc, limit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// selectTime보다 +- second 사이의 데이터 요청. selectTime, second 포함
|
||||
/// </summary>
|
||||
/// <param name="date"></param>
|
||||
/// <param name="sqlFileName"></param>
|
||||
/// <param name="selectTime">yyyy-MM-ddTHH:mm:ss.fffZ format string</param>
|
||||
/// <param name="second"></param>
|
||||
/// <param name="orderAsc">true: 오래된 시간이 먼저, false: 최근 시간이 먼저</param>
|
||||
/// <param name="limit"></param>
|
||||
/// <returns></returns>
|
||||
public async UniTask<List<PlaybackSQLiteDataEntity>> SelectBySecondBaseInfo(string date, string sqlFileName, string selectTime, int second = 59, bool orderAsc = true, int limit = 1)
|
||||
{
|
||||
validationSqliteService(date, sqlFileName);
|
||||
return await sqliteService!.SelectBySecondBaseInfo(selectTime, second, orderAsc, limit);
|
||||
}
|
||||
|
||||
|
||||
private void validationSqliteService(string date, string sqlFileName)
|
||||
{
|
||||
if (sqliteService == null) sqliteService = new PlaybackSQLiteService();
|
||||
if (sqliteService.Connected)
|
||||
{
|
||||
if (sqliteService.Date != date || sqliteService.SqliteFileName != sqlFileName)
|
||||
{
|
||||
sqliteService.CloseDB();
|
||||
}
|
||||
}
|
||||
sqliteService.Connect(date, sqlFileName);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 131e22b5d8e3f214cafd3ad0848fce28
|
||||
164
Assets/Scripts/UVC/Factory/Playback/PlaybackSQLiteService.cs
Normal file
164
Assets/Scripts/UVC/Factory/Playback/PlaybackSQLiteService.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using SQLite4Unity3d;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UVC.Util;
|
||||
|
||||
namespace UVC.Factory
|
||||
{
|
||||
public class PlaybackSQLiteService
|
||||
{
|
||||
|
||||
//#region Singleton
|
||||
//private static readonly SQLiteService instance = new SQLiteService();
|
||||
//public static SQLiteService Instance => instance;
|
||||
//static SQLiteService() { }
|
||||
//#endregion
|
||||
|
||||
|
||||
private SQLiteConnection dbConnection;
|
||||
|
||||
public bool Connected { get => dbConnection != null; }
|
||||
|
||||
private string date;
|
||||
public string Date { get => date; }
|
||||
private string sqliteFileName;
|
||||
public string SqliteFileName { get => sqliteFileName; }
|
||||
|
||||
public void Connect(string date, string sqliteFileName)
|
||||
{
|
||||
this.date = date;
|
||||
this.sqliteFileName = sqliteFileName;
|
||||
dbConnection = new SQLiteConnection(Path.Combine(Application.streamingAssetsPath, "playback", date, sqliteFileName));
|
||||
}
|
||||
|
||||
public void CloseDB()
|
||||
{
|
||||
dbConnection.Close();
|
||||
dbConnection = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 추가하기
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="timeStamp">yyyy-MM-ddTHH:mm:ss.fffZ format string</param>
|
||||
/// <param name="temp"></param>
|
||||
/// <returns>데이터베이스에서 추가된 행 수</returns>
|
||||
public int Insert(string data, string timeStamp, string temp = null)
|
||||
{
|
||||
var query = $"INSERT INTO realTime (data, timestamp, temp) VALUES ('{data}', '{timeStamp}', " + (temp == null ? "null" : "'" + temp + "'") + ");";
|
||||
int changedRowLen = dbConnection.Execute(query);
|
||||
return changedRowLen;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// selectTime보다 +- second 사이의 데이터 요청. selectTime, second 포함
|
||||
/// second > 0 : selectTime <= data < selectTime + second
|
||||
/// second < 0 : selectTime + second < data <= selectTime
|
||||
/// </summary>
|
||||
/// <param name="selectTime">yyyy-MM-ddTHH:mm:ss.fffZ format string</param>
|
||||
/// <param name="second"></param>
|
||||
/// <param name="orderAsc">true: 오래된 시간이 먼저, false: 최근 시간이 먼저</param>
|
||||
/// <param name="limit"></param>
|
||||
/// <returns></returns>
|
||||
///
|
||||
readonly string[] queryParts =
|
||||
{
|
||||
"SELECT * FROM realTime WHERE ",
|
||||
"timestamp >= '",
|
||||
"' AND timestamp < '",
|
||||
"timestamp <= '",
|
||||
"' AND timestamp > '",
|
||||
" ORDER BY timestamp ",
|
||||
" LIMIT ",
|
||||
};
|
||||
public async UniTask<List<PlaybackSQLiteDataEntity>> SelectBySecond(string selectTime, int second, bool orderAsc = true, int limit = 0)
|
||||
{
|
||||
List<PlaybackSQLiteDataEntity> result = await UniTask.RunOnThreadPool(() =>
|
||||
{
|
||||
DateTime date = DateTimeUtil.UtcParse(selectTime).AddSeconds(second);
|
||||
string targetTime = DateTimeUtil.FormatTime(date);
|
||||
//Debug.Log($"SelectBySecondBaseInfo {selectTime} {second} {targetTime} {date}");
|
||||
queryBuilder.Append(queryParts[0]);
|
||||
//second가 selectTime 보다 더 미래면
|
||||
if (second > 0)
|
||||
{
|
||||
queryBuilder.Append($"{queryParts[1]}{selectTime}{queryParts[2]}{targetTime}'");
|
||||
}
|
||||
else
|
||||
{
|
||||
//second가 selectTime 보다 더 과거면
|
||||
queryBuilder.Append($"{queryParts[3]}{selectTime}{queryParts[4]}{targetTime}'");
|
||||
}
|
||||
queryBuilder.Append($"{queryParts[5]}{(orderAsc ? "asc" : "desc")}");
|
||||
if (limit > 0)
|
||||
queryBuilder.Append($"{queryParts[6]}{limit}");
|
||||
|
||||
queryBuilder.Append(";");
|
||||
//Debug.Log($"SelectBySecond {query}");
|
||||
var query = queryBuilder.ToString();
|
||||
queryBuilder.Clear();
|
||||
return dbConnection.Query<PlaybackSQLiteDataEntity>(query);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// selectTime보다 +- second 사이의 데이터 요청. selectTime, second 포함
|
||||
/// second > 0 : selectTime <= data < selectTime + second
|
||||
/// second < 0 : selectTime + second < data <= selectTime
|
||||
/// </summary>
|
||||
/// <param name="selectTime">yyyy-MM-ddTHH:mm:ss.fffZ format string</param>
|
||||
/// <param name="second"></param>
|
||||
/// <param name="orderAsc">true: 오래된 시간이 먼저, false: 최근 시간이 먼저</param>
|
||||
/// <param name="limit"></param>
|
||||
/// <returns></returns>
|
||||
///
|
||||
|
||||
StringBuilder queryBuilder = new();
|
||||
public async UniTask<List<PlaybackSQLiteDataEntity>> SelectBySecondBaseInfo(string selectTime, int second, bool orderAsc = false, int limit = 1)
|
||||
{
|
||||
List<PlaybackSQLiteDataEntity> result = await UniTask.RunOnThreadPool(() =>
|
||||
{
|
||||
DateTime date = DateTimeUtil.UtcParse(selectTime).AddSeconds(second);
|
||||
string targetTime = DateTimeUtil.FormatTime(date);
|
||||
//Debug.Log($"SelectBySecondBaseInfo {selectTime} {second} {targetTime} {date}");
|
||||
queryBuilder.Append($"SELECT * FROM baseInfo WHERE ");
|
||||
//second가 selectTime 보다 더 미래면
|
||||
if (second > 0)
|
||||
{
|
||||
queryBuilder.Append($"timestamp >= '{selectTime}' AND timestamp < '{targetTime}'");
|
||||
}
|
||||
else
|
||||
{
|
||||
//second가 selectTime 보다 더 과거면
|
||||
queryBuilder.Append($"timestamp <= '{selectTime}' AND timestamp > '{targetTime}'");
|
||||
}
|
||||
queryBuilder.Append($" ORDER BY timestamp {(orderAsc ? "asc" : "desc")}");
|
||||
if (limit > 0) queryBuilder.Append($" LIMIT {limit}");
|
||||
queryBuilder.Append(";");
|
||||
//Debug.Log($"SelectBySecondBaseInfo {query}");
|
||||
var query = queryBuilder.ToString();
|
||||
queryBuilder.Clear();
|
||||
return dbConnection.Query<PlaybackSQLiteDataEntity>(query);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[System.Serializable]
|
||||
public class PlaybackSQLiteDataEntity
|
||||
{
|
||||
public string data { get; set; }
|
||||
[PrimaryKey]
|
||||
public string timestamp { get; set; }
|
||||
public DateTime timestampHungary { get => DateTimeUtil.UtcStringToKoreaDateTime(timestamp); }
|
||||
public string temp { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 10c4a6d7e00d6e04eb5efd504572f689
|
||||
207
Assets/Scripts/UVC/Factory/Playback/PlaybackService.cs
Normal file
207
Assets/Scripts/UVC/Factory/Playback/PlaybackService.cs
Normal file
@@ -0,0 +1,207 @@
|
||||
#nullable enable
|
||||
using Best.HTTP;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UVC.Data;
|
||||
using UVC.Data.Core;
|
||||
using UVC.Data.Http;
|
||||
using UVC.Json;
|
||||
using UVC.Util;
|
||||
namespace UVC.Factory.Playback
|
||||
{
|
||||
public class PlaybackService
|
||||
{
|
||||
#region Singleton
|
||||
private static readonly PlaybackService instance = new PlaybackService(new PlaybackRepository());
|
||||
public static PlaybackService Instance => instance;
|
||||
static PlaybackService() { }
|
||||
#endregion
|
||||
|
||||
private readonly PlaybackRepository repository;
|
||||
|
||||
private string date;
|
||||
private string time;
|
||||
private string fileName;
|
||||
|
||||
public Action OnStopPlayback;
|
||||
|
||||
|
||||
public PlaybackService(PlaybackRepository repository)
|
||||
{
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public async UniTask<Dictionary<string, Dictionary<string, string>>?> RequestDataAsync()
|
||||
{
|
||||
Dictionary<string, Dictionary<string, string>>? data = await repository.RequestPlaybackDateList();
|
||||
return data;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
HttpRequestConfig httpRequestConfig = new HttpRequestConfig("");
|
||||
httpRequestConfig.setSplitResponseByKey(true);
|
||||
httpRequestConfig.AddSplitConfig("AGV", DataMapperValidator.Get("AGV"));
|
||||
httpRequestConfig.AddSplitConfig("ALARM", DataMapperValidator.Get("ALARM"));
|
||||
foreach (var item in list)
|
||||
{
|
||||
await HttpDataProcessor.ProcessSplitResponse(httpRequestConfig, item.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <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)
|
||||
{
|
||||
HttpRequestConfig httpRequestConfig = new HttpRequestConfig("");
|
||||
httpRequestConfig.setSplitResponseByKey(true);
|
||||
httpRequestConfig.AddSplitConfig("AGV", DataMapperValidator.Get("AGV"));
|
||||
httpRequestConfig.AddSplitConfig("ALARM", DataMapperValidator.Get("ALARM"));
|
||||
foreach (var item in list)
|
||||
{
|
||||
await HttpDataProcessor.ProcessSplitResponse(httpRequestConfig, item.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
OnStopPlayback?.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 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);
|
||||
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(".");
|
||||
string utcZipFilePath = Path.Combine(tempPath, utcFileName);
|
||||
string utcSqlFilePath = Path.Combine(tempPath, utcFileNameArr[0] + ".sqlite");
|
||||
if (!Directory.Exists(playbackPath)) Directory.CreateDirectory(playbackPath);
|
||||
if (!Directory.Exists(datePath)) Directory.CreateDirectory(datePath);
|
||||
if (!Directory.Exists(utcDatePath)) Directory.CreateDirectory(utcDatePath);
|
||||
if (!Directory.Exists(tempPath)) Directory.CreateDirectory(tempPath);
|
||||
if (File.Exists(sqlFilePath))
|
||||
{
|
||||
Debug.Log($"ONREADY SQP FILE");
|
||||
if (OnProgress != null) OnProgress.Invoke(100, 100, 1.0f);
|
||||
if (OnComplete != null) OnComplete.Invoke(null);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
long downloadTotal = 0;
|
||||
return repository.DownloadPlaybackData(utcFileName, utcZipFilePath,
|
||||
(long progress, long length) =>
|
||||
{
|
||||
float percent = (float)progress / (float)length;
|
||||
Debug.Log($"DownloadPlaybackData OnProgress:{percent}");
|
||||
if (OnProgress != null) OnProgress.Invoke(progress, length, percent / 2);
|
||||
downloadTotal = length;
|
||||
}, async () =>
|
||||
{
|
||||
await UniTask.Delay(500);
|
||||
Debug.Log($"DownloadPlaybackData OnComplete");
|
||||
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)
|
||||
{
|
||||
OnProgress.Invoke(downloadTotal + read, downloadTotal + total, 0.5f + percent / 2);
|
||||
if (0.5f + percent / 2 > 100f)
|
||||
{
|
||||
Debug.Log($" DownloadReadData :{downloadTotal + read} , DownloadTotalData :{downloadTotal + total} ,DownloadPlaybackData OnProgress:{percent}");
|
||||
}
|
||||
}
|
||||
});
|
||||
Debug.Log($"zipper1 errorMessage:{errorMessage} utcSqlFilePath:{utcSqlFilePath} sqlFilePath:{sqlFilePath} utcZipFilePath:{utcZipFilePath}");
|
||||
|
||||
////파일 접근 문제면 2회 0.5초 후에 다시 실행.
|
||||
//if (errorMessage == "Could not open input(7z) file")
|
||||
//{
|
||||
// await UniTask.Delay(500);
|
||||
// errorMessage = await zipper.Decompress(utcZipFilePath, tempPath, (long read, long total, float percent) =>
|
||||
// {
|
||||
// if (OnProgress != null) OnProgress.Invoke(downloadTotal + read, downloadTotal + total, 0.5f + percent / 2);
|
||||
// });
|
||||
// Debug.Log($"zipper2 errorMessage:{errorMessage} utcSqlFilePath:{utcSqlFilePath} sqlFilePath:{sqlFilePath} utcZipFilePath:{utcZipFilePath}");
|
||||
//}
|
||||
|
||||
//if (errorMessage == "Could not open input(7z) file")
|
||||
//{
|
||||
// await UniTask.Delay(500);
|
||||
// errorMessage = await zipper.Decompress(utcZipFilePath, tempPath, (long read, long total, float percent) =>
|
||||
// {
|
||||
// if (OnProgress != null) OnProgress.Invoke(downloadTotal + read, downloadTotal + total, 0.5f + percent / 2);
|
||||
// });
|
||||
// Debug.Log($"zipper3 errorMessage:{errorMessage} utcSqlFilePath:{utcSqlFilePath} sqlFilePath:{sqlFilePath} utcZipFilePath:{utcZipFilePath}");
|
||||
//}
|
||||
|
||||
//압축해제 한 파일 이동
|
||||
if (File.Exists(utcSqlFilePath))
|
||||
{
|
||||
//동일한 파일명이 있을경우 제거후 다시
|
||||
File.Copy(utcSqlFilePath, sqlFilePath);
|
||||
File.Delete(utcSqlFilePath);
|
||||
}
|
||||
|
||||
//zip 파일 삭제
|
||||
File.Delete(utcZipFilePath);
|
||||
if (OnComplete != null) OnComplete.Invoke(errorMessage);
|
||||
}
|
||||
},
|
||||
(string? error) =>
|
||||
{
|
||||
Debug.Log($"DownloadPlaybackData OnError:{error}");
|
||||
if (OnComplete != null) OnComplete.Invoke(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: deed16ffd893d334b937460dfaa00de3
|
||||
8
Assets/Scripts/UVC/Factory/Playback/UI.meta
Normal file
8
Assets/Scripts/UVC/Factory/Playback/UI.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 866839c3051b5dc46bb738a62bef8fb3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
265
Assets/Scripts/UVC/Factory/Playback/UI/UIPlayback.cs
Normal file
265
Assets/Scripts/UVC/Factory/Playback/UI/UIPlayback.cs
Normal file
@@ -0,0 +1,265 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using System;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UVC.Extension;
|
||||
using UVC.UI;
|
||||
using UVC.UI.Loading;
|
||||
|
||||
namespace UVC.Factory.Playback.UI
|
||||
{
|
||||
public class UIPlayback : MonoBehaviour
|
||||
{
|
||||
|
||||
private static UIPlayback instance;
|
||||
public static UIPlayback Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null) instance = CreateUIPlayBack();
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
private static UIPlayback CreateUIPlayBack()
|
||||
{
|
||||
GameObject prefab = Resources.Load<GameObject>("Prefabs/Factory/Playback/UIPlayback");
|
||||
GameObject go = GameObject.Instantiate(prefab);
|
||||
return go.GetComponent<UIPlayback>();
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("종료 버튼")]
|
||||
private Button exitButton;
|
||||
[SerializeField]
|
||||
[Tooltip("종료 버튼")]
|
||||
private TextMeshProUGUI dateTimeTxt0;
|
||||
[SerializeField]
|
||||
[Tooltip("종료 버튼")]
|
||||
private TextMeshProUGUI dateTimeTxt1;
|
||||
[SerializeField]
|
||||
[Tooltip("play 버튼")]
|
||||
private Button playButton;
|
||||
[SerializeField]
|
||||
[Tooltip("play 버튼 이미지")]
|
||||
private Image playButtonImage;
|
||||
[SerializeField]
|
||||
[Tooltip("play 버튼 이미지 Sprite")]
|
||||
private Sprite playButtonImagePlay;
|
||||
[SerializeField]
|
||||
[Tooltip("play 버튼 Puase 이미지 Sprite")]
|
||||
private Sprite playButtonImagePause;
|
||||
[SerializeField]
|
||||
[Tooltip("Speed Slider")]
|
||||
private UISliderWithLabel sliderSpeed;
|
||||
[SerializeField]
|
||||
[Tooltip("투명 조절 Slider")]
|
||||
private SliderWithEvent opacitySlider;
|
||||
[SerializeField]
|
||||
[Tooltip("Progress Bar")]
|
||||
private UIPlaybackProgressBar progressBar;
|
||||
[SerializeField]
|
||||
private CanvasGroup canvasGroup;
|
||||
|
||||
private bool isPlaying = false;
|
||||
private bool preparingData = false;
|
||||
|
||||
private string date;
|
||||
private string time;
|
||||
private string fileName;
|
||||
|
||||
private bool isTick = false;
|
||||
private bool IsTick
|
||||
{
|
||||
get => isTick;
|
||||
set
|
||||
{
|
||||
if (isTick != value)
|
||||
{
|
||||
var temp = isTick;
|
||||
isTick = value;
|
||||
if (!temp && value) OnTimer().Forget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
exitButton.onClick.AddListener(OnClickExit);
|
||||
playButton.onClick.AddListener(OnClickPlay);
|
||||
|
||||
progressBar.OnChangeValue += OnChangeProgress;
|
||||
sliderSpeed.OnChangeValue += OnChangeSpeed;
|
||||
opacitySlider.onValueChanged.AddListener(OnValueChangedOpcity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
exitButton.onClick.RemoveListener(OnClickExit);
|
||||
playButton.onClick.RemoveListener(OnClickPlay);
|
||||
progressBar.OnChangeValue = null;
|
||||
sliderSpeed.OnChangeValue = null;
|
||||
opacitySlider.onValueChanged.RemoveListener(OnValueChangedOpcity);
|
||||
if (isPlaying) IsTick = false;
|
||||
}
|
||||
|
||||
public void Show()
|
||||
{
|
||||
if (playButton == null) Init();
|
||||
gameObject.SetActive(true);
|
||||
if (transform.parent == null)
|
||||
{
|
||||
var canvases = GameObject.FindObjectsByType<Canvas>(FindObjectsSortMode.None);
|
||||
foreach (var canvas in canvases)
|
||||
{
|
||||
if (canvas.name == "ModalCanvas")
|
||||
{
|
||||
transform.SetParent(canvas.transform, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
Time.timeScale = 1;
|
||||
IsTick = false;
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void OnClickExit()
|
||||
{
|
||||
UILoading.Show();
|
||||
isPlaying = false;
|
||||
UpdatePlayState();
|
||||
Hide();
|
||||
PlaybackService.Instance.Stop();
|
||||
UILoading.Hide();
|
||||
}
|
||||
|
||||
private void OnClickPlay()
|
||||
{
|
||||
isPlaying = !isPlaying;
|
||||
UpdatePlayState();
|
||||
}
|
||||
|
||||
private void OnChangeProgress(int newValue)
|
||||
{
|
||||
ChangePlayTime().Forget();
|
||||
}
|
||||
|
||||
private void OnChangeSpeed(int newValue)
|
||||
{
|
||||
if (isPlaying)
|
||||
{
|
||||
if (Time.timeScale != sliderSpeed.Value) Time.timeScale = sliderSpeed.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
Time.timeScale = 1;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnValueChangedOpcity(float newValue)
|
||||
{
|
||||
canvasGroup.alpha = opacitySlider.value;
|
||||
}
|
||||
|
||||
|
||||
private async UniTaskVoid ChangePlayTime()
|
||||
{
|
||||
|
||||
bool tempIsPlaing = isPlaying;
|
||||
isPlaying = false;
|
||||
int newSecond = (int)progressBar.Value;
|
||||
if (newSecond == progressBar.MaxValue)
|
||||
{
|
||||
newSecond -= 60;
|
||||
progressBar.Value = newSecond;
|
||||
}
|
||||
preparingData = true;
|
||||
progressBar.Interactable = !preparingData;
|
||||
IsTick = false;
|
||||
UILoading.Show();
|
||||
UpdatePlayState();
|
||||
await UniTask.WaitForSeconds(0.5f);
|
||||
int minute = (int)newSecond / 60;
|
||||
int seconds = (int)newSecond % 60;
|
||||
await PlaybackService.Instance.DispatchBaseInfoData(date, time, fileName, minute.ToString("00"), seconds.ToString("00"));
|
||||
preparingData = false;
|
||||
progressBar.Interactable = !preparingData;
|
||||
if (isPlaying != tempIsPlaing)
|
||||
{
|
||||
isPlaying = tempIsPlaing;
|
||||
UpdatePlayState();
|
||||
}
|
||||
UILoading.Hide();
|
||||
await UniTask.WaitForSeconds(0.5f);
|
||||
}
|
||||
|
||||
public async UniTask SetData(string date, string time, string fileName)
|
||||
{
|
||||
Init();
|
||||
this.date = date;
|
||||
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;
|
||||
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()
|
||||
{
|
||||
playButton.enabled = false;
|
||||
progressBar.enabled = false;
|
||||
playButtonImage.sprite = isPlaying ? playButtonImagePause : playButtonImagePlay;
|
||||
IsTick = isPlaying;
|
||||
progressBar.enabled = true;
|
||||
playButton.enabled = true;
|
||||
OnChangeSpeed(sliderSpeed.Value);
|
||||
}
|
||||
|
||||
|
||||
private async UniTaskVoid OnTimer()
|
||||
{
|
||||
if (progressBar.Value == progressBar.MaxValue)
|
||||
{
|
||||
if (isPlaying) OnClickPlay();
|
||||
return;
|
||||
}
|
||||
progressBar.Value += 1;
|
||||
//PlaybackService.Instance.DispatchingTimelineEvent = false;
|
||||
|
||||
PlaybackService.Instance.DispatchRealTimeData(progressBar.Value, sliderSpeed.Value).Forget();
|
||||
|
||||
if (isTick)
|
||||
{
|
||||
//PlaybackService.Instance.DispatchingTimelineEvent = true;
|
||||
await UniTask.Delay(TimeSpan.FromMilliseconds(1000 / sliderSpeed.Value));
|
||||
OnTimer().Forget();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ca072637b87cb24eba398b90a1d49e9
|
||||
311
Assets/Scripts/UVC/Factory/Playback/UI/UIPlaybackListItem.cs
Normal file
311
Assets/Scripts/UVC/Factory/Playback/UI/UIPlaybackListItem.cs
Normal file
@@ -0,0 +1,311 @@
|
||||
using Best.HTTP;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
using UVC.Extension;
|
||||
using UVC.Pool;
|
||||
using UVC.UI.Modal;
|
||||
using UVC.Util;
|
||||
|
||||
namespace UVC.Factory.Playback.UI
|
||||
{
|
||||
|
||||
public enum UIPlaybackListItemStatus
|
||||
{
|
||||
Default,
|
||||
Downloading,
|
||||
Downloaded
|
||||
}
|
||||
|
||||
public class UIPlaybackListItemData
|
||||
{
|
||||
public string date = "";
|
||||
public string time = "";
|
||||
public string zipFileName = "";
|
||||
public string sqlFileName = "";
|
||||
public UIPlaybackListItemStatus status = UIPlaybackListItemStatus.Default;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"date:{date}, time:{time}, zipFileName:{zipFileName}, sqlFileName:{sqlFileName}, status:{status}";
|
||||
}
|
||||
}
|
||||
|
||||
public class UIPlaybackListItem : UnityEngine.MonoBehaviour, IPointerClickHandler
|
||||
{
|
||||
|
||||
protected static ItemPool<UIPlaybackListItem> pool;
|
||||
|
||||
public static readonly string PrefabPath = "Prefabs/Factory/Playback/UIPlaybackListItem";
|
||||
|
||||
public static UIPlaybackListItem CreateFromPool(Transform parent)
|
||||
{
|
||||
if (pool == null)
|
||||
{
|
||||
GameObject prefab = Resources.Load<GameObject>(PrefabPath);
|
||||
pool = new ItemPool<UIPlaybackListItem>(prefab, parent, null);
|
||||
}
|
||||
return pool.GetItem(true, parent);
|
||||
}
|
||||
|
||||
public static List<UIPlaybackListItem> ActiveItems
|
||||
{
|
||||
get
|
||||
{
|
||||
if (pool != null) return (List<UIPlaybackListItem>)pool.ActiveItems;
|
||||
return new List<UIPlaybackListItem>();
|
||||
}
|
||||
}
|
||||
|
||||
public static List<UIPlaybackListItem> DownloadingItems
|
||||
{
|
||||
get
|
||||
{
|
||||
return ActiveItems.Where(x => x.Status == UIPlaybackListItemStatus.Downloading).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public static void ReleaseAll()
|
||||
{
|
||||
if (pool != null)
|
||||
{
|
||||
foreach (var item in pool.ActiveItems) item.Release();
|
||||
pool.ReturnAll();
|
||||
}
|
||||
}
|
||||
|
||||
public static void ClearAll()
|
||||
{
|
||||
if (pool != null)
|
||||
{
|
||||
foreach (var item in pool.ActiveItems) item.Release();
|
||||
pool.ReturnAll();
|
||||
pool.ClearRecycledItems();
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Image loadingImage;
|
||||
[SerializeField]
|
||||
private Image selectedImage;
|
||||
[SerializeField]
|
||||
private Button downloadButton;
|
||||
[SerializeField]
|
||||
private Button stopButton;
|
||||
[SerializeField]
|
||||
private Button deleteButton;
|
||||
[SerializeField]
|
||||
private TextMeshProUGUI downloadText;
|
||||
[SerializeField]
|
||||
private TextMeshProUGUI text;
|
||||
|
||||
public Action<UIPlaybackListItemData, bool> OnSelect { get; set; }
|
||||
public Action<UIPlaybackListItemData, UIPlaybackListItemStatus> OnChangeStatus { get; set; }
|
||||
|
||||
private UIPlaybackListItemData data;
|
||||
public UIPlaybackListItemData Data { get => data; }
|
||||
|
||||
|
||||
public UIPlaybackListItemStatus Status
|
||||
{
|
||||
get => data.status;
|
||||
set
|
||||
{
|
||||
if (data.status != value)
|
||||
{
|
||||
data.status = value;
|
||||
ChangeStatus();
|
||||
if (OnChangeStatus != null) OnChangeStatus.Invoke(Data, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool selected = false;
|
||||
|
||||
public bool Selected
|
||||
{
|
||||
get => selected;
|
||||
set
|
||||
{
|
||||
if (selected != value)
|
||||
{
|
||||
selected = value;
|
||||
selectedImage.gameObject.SetActive(selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private HTTPRequest? downloadRequest;
|
||||
|
||||
|
||||
public void Init(UIPlaybackListItemData data)
|
||||
{
|
||||
transform.localScale = Vector3.one;
|
||||
selected = false;
|
||||
selectedImage.gameObject.SetActive(selected);
|
||||
|
||||
this.data = data;
|
||||
text.text = $"{data.time} ~ {int.Parse(data.time) + 1} Hour";
|
||||
downloadButton.onClick.AddListener(onClickDownload);
|
||||
stopButton.onClick.AddListener(onClickStop);
|
||||
deleteButton.onClick.AddListener(onClickDelete);
|
||||
ChangeStatus();
|
||||
}
|
||||
|
||||
private void onClickDownload()
|
||||
{
|
||||
if (Status == UIPlaybackListItemStatus.Downloading) return;
|
||||
|
||||
long freeSize = DriveUtil.GetDriveGigaBytes();
|
||||
|
||||
Debug.Log($"여유 공간 freeSize:{freeSize} {10 * (DownloadingItems.Count + 1)} {freeSize < 10 * (DownloadingItems.Count + 1)}");
|
||||
if (freeSize < 10 * (DownloadingItems.Count + 1))
|
||||
{
|
||||
Toast.Show("Playback 하나당 10기가바이트의 용량이 필요합니다. 용량을 확보하고 다운로드 하세요.\nEach Playback requires a capacity of 10 gigabytes. Secure the capacity and download it.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (DownloadingItems.Count >= 2)
|
||||
{
|
||||
Toast.Show("한번에 총 2개 아이템만 다운로드가 가능합니다.\nOnly 2 items can be downloaded at a time.");
|
||||
//await UIAlertManager.Instance.ShowAlert("Downloading", "한번에 총 2개 아이템만 다운로드가 가능합니다.\nOnly 3 items can be downloaded at a time.");
|
||||
return;
|
||||
}
|
||||
|
||||
Status = UIPlaybackListItemStatus.Downloading;
|
||||
downloadRequest = PlaybackService.Instance.ReadyData(data.date, data.time, data.zipFileName,
|
||||
(long read, long total, float percent) =>
|
||||
{
|
||||
Debug.Log($"ReadyData {percent}");
|
||||
downloadText.text = $"{read.ToSizeSuffix()}/{total.ToSizeSuffix()} ({(int)(percent * 100)}%)";
|
||||
loadingImage.fillAmount = percent;
|
||||
},
|
||||
(string errorMessage) =>
|
||||
{
|
||||
Debug.Log($"Complete Data errorMessage:{errorMessage}");
|
||||
if (downloadRequest != null) downloadRequest.Clear();
|
||||
downloadRequest = null;
|
||||
if (errorMessage != null)
|
||||
{
|
||||
if (errorMessage.ToLower().Contains("abort")) errorMessage = "다운로드를 중지 했습니다.\nThe download has been stopped.";
|
||||
Toast.Show(errorMessage);
|
||||
//await UIAlertManager.Instance.ShowAlert("Error", errorMessage);
|
||||
deleteFile();
|
||||
Status = UIPlaybackListItemStatus.Default;
|
||||
}
|
||||
else
|
||||
{
|
||||
Status = UIPlaybackListItemStatus.Downloaded;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void deleteFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
string playbackPath = Path.Combine(Application.streamingAssetsPath, "playback");
|
||||
string tempPath = Path.Combine(playbackPath, "temp");
|
||||
string datePath = Path.Combine(playbackPath, data.date);
|
||||
var fileNameArr = data.zipFileName.Split('.');
|
||||
DateTime utcDateTime = DateTimeUtil.Parse(fileNameArr[0], "yyyy-MM-dd_H");
|
||||
string utcSqlFileName = utcDateTime.ToString("yyyy-MM-dd_H") + "." + fileNameArr[1];
|
||||
string utcZipFileName = utcSqlFileName + "." + fileNameArr[2];
|
||||
string utcZipFilePath = Path.Combine(datePath, utcZipFileName);
|
||||
string utcSqlFilePath = Path.Combine(datePath, utcSqlFileName);
|
||||
System.GC.Collect();
|
||||
System.GC.WaitForPendingFinalizers();
|
||||
if (File.Exists(utcZipFilePath)) File.Delete(utcZipFilePath);
|
||||
if (File.Exists(utcSqlFilePath)) File.Delete(utcSqlFilePath);
|
||||
// if (Directory.Exists(tempPath)) Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void onClickStop()
|
||||
{
|
||||
if (downloadRequest != null && Status == UIPlaybackListItemStatus.Downloading)
|
||||
{
|
||||
downloadRequest.Abort();
|
||||
downloadRequest.Clear();
|
||||
downloadRequest = null;
|
||||
deleteFile();
|
||||
Status = UIPlaybackListItemStatus.Default;
|
||||
}
|
||||
}
|
||||
|
||||
private void onClickDelete()
|
||||
{
|
||||
deleteFile();
|
||||
Status = UIPlaybackListItemStatus.Default;
|
||||
}
|
||||
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
Selected = !selected;
|
||||
if (OnSelect != null) OnSelect.Invoke(data, Selected);
|
||||
}
|
||||
|
||||
public void Release()
|
||||
{
|
||||
OnDestroy();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
downloadButton.onClick.RemoveListener(onClickDownload);
|
||||
stopButton.onClick.RemoveListener(onClickStop);
|
||||
deleteButton.onClick.RemoveListener(onClickDelete);
|
||||
OnSelect = null;
|
||||
OnChangeStatus = null;
|
||||
transform.SetParent(null);
|
||||
}
|
||||
|
||||
private void ChangeStatus()
|
||||
{
|
||||
downloadText.text = "";
|
||||
if (data.status == UIPlaybackListItemStatus.Default)
|
||||
{
|
||||
loadingImage.gameObject.SetActive(false);
|
||||
downloadButton.gameObject.SetActive(true);
|
||||
stopButton.gameObject.SetActive(false);
|
||||
deleteButton.gameObject.SetActive(false);
|
||||
downloadText.gameObject.SetActive(false);
|
||||
}
|
||||
else if (data.status == UIPlaybackListItemStatus.Downloading)
|
||||
{
|
||||
loadingImage.gameObject.SetActive(true);
|
||||
loadingImage.SetAlpha(0.2f);
|
||||
loadingImage.fillAmount = 0;
|
||||
downloadButton.gameObject.SetActive(false);
|
||||
stopButton.gameObject.SetActive(true);
|
||||
deleteButton.gameObject.SetActive(false);
|
||||
downloadText.gameObject.SetActive(true);
|
||||
}
|
||||
else if (data.status == UIPlaybackListItemStatus.Downloaded)
|
||||
{
|
||||
loadingImage.gameObject.SetActive(true);
|
||||
loadingImage.SetAlpha(1f);
|
||||
loadingImage.fillAmount = 1;
|
||||
downloadButton.gameObject.SetActive(false);
|
||||
stopButton.gameObject.SetActive(false);
|
||||
deleteButton.gameObject.SetActive(true);
|
||||
downloadText.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e157d53f2eb114b4b94e5b0d1c7eeaa9
|
||||
319
Assets/Scripts/UVC/Factory/Playback/UI/UIPlaybackListModal.cs
Normal file
319
Assets/Scripts/UVC/Factory/Playback/UI/UIPlaybackListModal.cs
Normal file
@@ -0,0 +1,319 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UVC.Extension;
|
||||
using UVC.Log;
|
||||
using UVC.UI;
|
||||
using UVC.UI.Modal;
|
||||
using UVC.Util;
|
||||
|
||||
namespace UVC.Factory.Playback.UI
|
||||
{
|
||||
[RequireComponent(typeof(CanvasGroup))]
|
||||
public class UIPlaybackListModal : ModalView
|
||||
{
|
||||
public static new readonly string PrefabPath = "Prefabs/Factory/Playback/UIPlaybackListModal";
|
||||
|
||||
public static UIPlaybackListModal CreateFromPrefab(Transform parent = null)
|
||||
{
|
||||
GameObject prefab = Resources.Load(PrefabPath, typeof(GameObject)) as GameObject;
|
||||
GameObject go = UnityEngine.Object.Instantiate(prefab);
|
||||
UIPlaybackListModal modal = go.GetComponent<UIPlaybackListModal>();
|
||||
return modal;
|
||||
}
|
||||
|
||||
private Dictionary<string, List<UIPlaybackListItemData>> data;
|
||||
|
||||
public bool IsOkable => (selectedItem != null && selectedItem.status == UIPlaybackListItemStatus.Downloaded && UIPlaybackListItem.DownloadingItems.Count == 0);
|
||||
|
||||
private UIPlaybackListItemData? selectedItem;
|
||||
|
||||
[SerializeField]
|
||||
private TMP_Dropdown dropdownDate;
|
||||
[SerializeField]
|
||||
private ScrollRect scrollRectTime;
|
||||
|
||||
|
||||
public void Init()
|
||||
{
|
||||
initContent();
|
||||
}
|
||||
|
||||
|
||||
public override async UniTask OnOpen(ModalContent content)
|
||||
{
|
||||
await base.OnOpen(content); // 부모의 OnOpen을 먼저 호출해서 기본 UI를 설정해요.
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override object GetResult()
|
||||
{
|
||||
if (data != null) data.Clear();
|
||||
data = null;
|
||||
return selectedItem;
|
||||
}
|
||||
|
||||
public override async UniTask OnClose(ModalContent content)
|
||||
{
|
||||
|
||||
|
||||
await base.OnClose(content);
|
||||
}
|
||||
|
||||
|
||||
private async void initContent()
|
||||
{
|
||||
confirmButton.interactable = false;
|
||||
|
||||
Dictionary<string, Dictionary<string, string>> data = await PlaybackService.Instance.RequestDataAsync();
|
||||
dropdownDate.onValueChanged.AddListener(DropDownDateChanged);
|
||||
LocalSetData();
|
||||
if (data != null) SetData(data);
|
||||
}
|
||||
|
||||
|
||||
protected void OnDestroy()
|
||||
{
|
||||
dropdownDate.onValueChanged.RemoveListener(DropDownDateChanged);
|
||||
UIPlaybackListItem.ClearAll();
|
||||
}
|
||||
|
||||
public void LocalSetData()
|
||||
{
|
||||
Dictionary<string, List<UIPlaybackListItemData>> newData = new Dictionary<string, List<UIPlaybackListItemData>>();
|
||||
var dateList = new List<TMP_Dropdown.OptionData>();
|
||||
|
||||
string playbackPath = Path.Combine(Application.streamingAssetsPath, "playback");
|
||||
DirectoryInfo di = new DirectoryInfo(playbackPath);
|
||||
if (di.Exists)
|
||||
{
|
||||
var childDi = di.EnumerateDirectories();
|
||||
foreach (var child in childDi)
|
||||
{
|
||||
string date = child.Name;
|
||||
//Debug.Log($"date:{date}");
|
||||
var childFiles = child.EnumerateFiles();
|
||||
if (childFiles.Count() > 0 && !newData.ContainsKey(date)) newData[date] = new List<UIPlaybackListItemData>();
|
||||
foreach (var childFile in childFiles)
|
||||
{
|
||||
//남은게 있다면 삭제
|
||||
if (childFile.Extension == ".zip" || childFile.Extension == ".7z")
|
||||
{
|
||||
try
|
||||
{
|
||||
Debug.Log($"delete zip file:{childFile.FullName}");
|
||||
childFile.Delete();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
}
|
||||
if (childFile.Extension != ".sqlite") continue;
|
||||
var sqlFileName = childFile.Name;
|
||||
string time = sqlFileName.Split("_")[1].Replace(".sqlite", "");
|
||||
newData[date].Add(new UIPlaybackListItemData()
|
||||
{
|
||||
date = date,
|
||||
time = time,
|
||||
zipFileName = $"{sqlFileName}.7z",
|
||||
sqlFileName = sqlFileName,
|
||||
status = UIPlaybackListItemStatus.Downloaded,
|
||||
});
|
||||
//fileName.Split(".")[0] + ".sqlite"
|
||||
}
|
||||
|
||||
if (newData.ContainsKey(date) && newData[date].Count() > 0) dateList.Add(new TMP_Dropdown.OptionData(date));
|
||||
}
|
||||
}
|
||||
|
||||
this.data = newData;
|
||||
dateList.Sort((b, a) => a.text.CompareTo(b.text));
|
||||
dropdownDate.options = dateList;
|
||||
dropdownDate.value = -1;
|
||||
}
|
||||
|
||||
public void SetData(Dictionary<string, Dictionary<string, string>> data)
|
||||
{
|
||||
//{
|
||||
// "2024-12-05": {
|
||||
// "0": "2024-12-05_0.sqlite.7z",
|
||||
// "1": "2024-12-05_1.sqlite.7z",
|
||||
// "2": "2024-12-05_2.sqlite.7z",
|
||||
// }
|
||||
//}
|
||||
Dictionary<string, Dictionary<string, string>> dataList = new Dictionary<string, Dictionary<string, string>>();
|
||||
|
||||
//헝가리 시간으로 변경
|
||||
foreach (var keyPair in data)
|
||||
{
|
||||
if (!dataList.ContainsKey(keyPair.Key)) dataList[keyPair.Key] = new Dictionary<string, string>();
|
||||
foreach (var keyPair2 in keyPair.Value)
|
||||
{
|
||||
var arr = keyPair2.Value.Split(".");
|
||||
string timeStr = arr[0];
|
||||
DateTime dateTime = DateTimeUtil.Parse(timeStr, "yyyy-MM-dd_H");
|
||||
string dateStr = dateTime.ToString("yyyy-MM-dd");
|
||||
string dateKey = dateTime.Hour.ToString();
|
||||
string dateValue = dateTime.ToString("yyyy-MM-dd_H") + "." + arr[1] + "." + arr[2];
|
||||
ULog.Debug($"dateTime:{dateTime}, timeStr:{timeStr} dateValue:{dateValue}");
|
||||
if (!dataList.ContainsKey(dateStr)) dataList[dateStr] = new Dictionary<string, string>();
|
||||
if (!dataList[dateStr].ContainsKey(dateKey)) dataList[dateStr][dateKey] = dateValue;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in dataList.ToList())
|
||||
{
|
||||
if (item.Key.Count() == 0) dataList.Remove(item.Key);
|
||||
}
|
||||
|
||||
|
||||
Dictionary<string, List<UIPlaybackListItemData>> newData = new Dictionary<string, List<UIPlaybackListItemData>>();
|
||||
|
||||
var dateList = new List<TMP_Dropdown.OptionData>();
|
||||
|
||||
//로컬에 저장 되 있는데 sqlite 파일 찾아서 추가
|
||||
string playbackPath = Path.Combine(Application.streamingAssetsPath, "playback");
|
||||
DirectoryInfo di = new DirectoryInfo(playbackPath);
|
||||
if (di.Exists)
|
||||
{
|
||||
var childDi = di.EnumerateDirectories();
|
||||
foreach (var child in childDi)
|
||||
{
|
||||
string date = child.Name;
|
||||
//Debug.Log($"date:{date}");
|
||||
var childFiles = child.EnumerateFiles();
|
||||
if (childFiles.Count() > 0 && !newData.ContainsKey(date)) newData[date] = new List<UIPlaybackListItemData>();
|
||||
foreach (var childFile in childFiles)
|
||||
{
|
||||
//남은게 있다면 삭제
|
||||
if (childFile.Extension == ".zip" || childFile.Extension == ".7z")
|
||||
{
|
||||
try
|
||||
{
|
||||
Debug.Log($"delete zip file:{childFile.FullName}");
|
||||
childFile.Delete();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
}
|
||||
if (childFile.Extension != ".sqlite") continue;
|
||||
var sqlFileName = childFile.Name;
|
||||
string time = sqlFileName.Split("_")[1].Replace(".sqlite", "");
|
||||
newData[date].Add(new UIPlaybackListItemData()
|
||||
{
|
||||
date = date,
|
||||
time = time,
|
||||
zipFileName = $"{sqlFileName}.7z",
|
||||
sqlFileName = sqlFileName,
|
||||
status = UIPlaybackListItemStatus.Downloaded,
|
||||
});
|
||||
//fileName.Split(".")[0] + ".sqlite"
|
||||
}
|
||||
|
||||
if (newData.ContainsKey(date) && newData[date].Count() > 0) dateList.Add(new TMP_Dropdown.OptionData(date));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
foreach (var keyPair in dataList)
|
||||
{
|
||||
string date = keyPair.Key;
|
||||
if (dateList.FindIndex(o => o.text == date) == -1) dateList.Add(new TMP_Dropdown.OptionData(date));
|
||||
if (!newData.ContainsKey(date)) newData[date] = new List<UIPlaybackListItemData>();
|
||||
foreach (var keyPair2 in keyPair.Value)
|
||||
{
|
||||
var zipFileName = keyPair2.Value;
|
||||
var sqlFileName = zipFileName.Replace(".zip", "").Replace(".7z", "");
|
||||
if (newData[date].FindIndex(item => item.sqlFileName == sqlFileName) == -1)
|
||||
{
|
||||
newData[date].Add(new UIPlaybackListItemData()
|
||||
{
|
||||
date = date,
|
||||
time = keyPair2.Key,
|
||||
zipFileName = zipFileName,
|
||||
sqlFileName = sqlFileName,
|
||||
status = UIPlaybackListItemStatus.Default,
|
||||
});
|
||||
}
|
||||
}
|
||||
//내림차순 정리
|
||||
if (newData.ContainsKey(date)) newData[date].Sort((a, b) => int.Parse(a.time) - int.Parse(b.time));
|
||||
}
|
||||
|
||||
this.data = newData;
|
||||
dateList.Sort((b, a) => a.text.CompareTo(b.text));
|
||||
dropdownDate.options = dateList;
|
||||
dropdownDate.value = -1;
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void DropDownDateChanged(int value)
|
||||
{
|
||||
|
||||
//dropdownDatePlaceHolder.gameObject.SetActive(value == -1);
|
||||
string key = dropdownDate.options[dropdownDate.value].text;
|
||||
Debug.Log($"DropDownDateChanged dropdownDate.value:{dropdownDate.value} value:{value} key:{key}");
|
||||
if (data.ContainsKey(key))
|
||||
{
|
||||
if (UIPlaybackListItem.ActiveItems.Count > 0) UIPlaybackListItem.ReleaseAll();
|
||||
List<UIPlaybackListItemData> itemList = data[key];
|
||||
foreach (var itemData in itemList)
|
||||
{
|
||||
UIPlaybackListItem item = UIPlaybackListItem.CreateFromPool(scrollRectTime.content);
|
||||
item.Init(itemData);
|
||||
item.OnSelect += OnItemSelect;
|
||||
item.OnChangeStatus += OnItemChangeStatus;
|
||||
}
|
||||
selectedItem = null;
|
||||
updateButtonStatus();
|
||||
}
|
||||
//scroll move to top
|
||||
scrollRectTime.normalizedPosition = new Vector2(0, 1);
|
||||
|
||||
}
|
||||
|
||||
private void OnItemSelect(UIPlaybackListItemData data, bool selected)
|
||||
{
|
||||
if (selected)
|
||||
{
|
||||
List<UIPlaybackListItem> itemList = UIPlaybackListItem.ActiveItems;
|
||||
foreach (var item in itemList)
|
||||
{
|
||||
if (item.Data.sqlFileName != data.sqlFileName)
|
||||
{
|
||||
item.Selected = false;
|
||||
}
|
||||
}
|
||||
selectedItem = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedItem = null;
|
||||
}
|
||||
updateButtonStatus();
|
||||
}
|
||||
|
||||
private void OnItemChangeStatus(UIPlaybackListItemData data, UIPlaybackListItemStatus status)
|
||||
{
|
||||
bool enable = UIPlaybackListItem.DownloadingItems.Count == 0;
|
||||
dropdownDate.interactable = enable;
|
||||
}
|
||||
|
||||
private void updateButtonStatus()
|
||||
{
|
||||
confirmButton.interactable = IsOkable;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7fd94cb2e9eab84ea0e85244ba1a656
|
||||
142
Assets/Scripts/UVC/Factory/Playback/UI/UIPlaybackProgressBar.cs
Normal file
142
Assets/Scripts/UVC/Factory/Playback/UI/UIPlaybackProgressBar.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UVC.Extension;
|
||||
using UVC.UI;
|
||||
|
||||
namespace UVC.Factory.Playback.UI
|
||||
{
|
||||
public class UIPlaybackProgressBar : MonoBehaviour
|
||||
{
|
||||
private TextMeshProUGUI playTimeTxt;
|
||||
private TextMeshProUGUI totalTimeTxt;
|
||||
private SliderWithEvent progressBar;
|
||||
private float progressBarPrevValue = 0;
|
||||
|
||||
private CanvasGroup canvasGroup;
|
||||
private int time;
|
||||
|
||||
public Action<int> OnChangeValue { get; set; }
|
||||
|
||||
public int Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return (int)progressBar.value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (progressBar.value != value)
|
||||
{
|
||||
progressBar.value = value;
|
||||
UpdateTimeText();
|
||||
progressBarPrevValue = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int MaxValue
|
||||
{
|
||||
get => (int)progressBar.maxValue;
|
||||
}
|
||||
|
||||
public bool Interactable
|
||||
{
|
||||
get => canvasGroup.interactable;
|
||||
set => canvasGroup.interactable = value;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
progressBar.onValueChanged.RemoveListener(OnChangeSlider);
|
||||
progressBar.OnClickAction = null;
|
||||
progressBar.OnDragAction = null;
|
||||
progressBar.OnEndDragAction = null;
|
||||
}
|
||||
|
||||
public void Init(int time)
|
||||
{
|
||||
this.time = time;
|
||||
canvasGroup = GetComponent<CanvasGroup>();
|
||||
|
||||
playTimeTxt = transform.FindChildren("PlayTimeTxt").GetComponent<TextMeshProUGUI>();
|
||||
totalTimeTxt = transform.FindChildren("TotalTimeTxt").GetComponent<TextMeshProUGUI>();
|
||||
progressBar = GetComponentInChildren<SliderWithEvent>();
|
||||
|
||||
progressBar.onValueChanged.AddListener(OnChangeSlider);
|
||||
progressBar.OnClickAction += OnClickProgressBar;
|
||||
progressBar.OnDragAction += OnDragProgressBar;
|
||||
progressBar.OnEndDragAction += OnEndDragProgressBar;
|
||||
|
||||
playTimeTxt.text = $"{time.ToString("00")}:00:00";
|
||||
totalTimeTxt.text = $"{(time + 1).ToString("00")}:00:00";
|
||||
progressBar.value = 0;
|
||||
Interactable = true;
|
||||
}
|
||||
|
||||
|
||||
private void OnChangeSlider(float newValue)
|
||||
{
|
||||
if (!Interactable)
|
||||
{
|
||||
progressBar.value = progressBarPrevValue;
|
||||
UpdateTimeText();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClickProgressBar()
|
||||
{
|
||||
float snapedValue = SnapProgressBarValue();
|
||||
progressBar.value = snapedValue;
|
||||
UpdateTimeText();
|
||||
if (progressBarPrevValue != snapedValue)
|
||||
{
|
||||
progressBarPrevValue = snapedValue;
|
||||
if (OnChangeValue != null) OnChangeValue.Invoke((int)snapedValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDragProgressBar()
|
||||
{
|
||||
float snapedValue = SnapProgressBarValue();
|
||||
progressBar.value = snapedValue;
|
||||
UpdateTimeText();
|
||||
}
|
||||
|
||||
private void OnEndDragProgressBar()
|
||||
{
|
||||
float snapedValue = SnapProgressBarValue();
|
||||
progressBar.value = snapedValue;
|
||||
UpdateTimeText();
|
||||
if (progressBarPrevValue != snapedValue)
|
||||
{
|
||||
progressBarPrevValue = snapedValue;
|
||||
if (OnChangeValue != null) OnChangeValue.Invoke((int)snapedValue);
|
||||
}
|
||||
}
|
||||
|
||||
private float SnapProgressBarValue()
|
||||
{
|
||||
float value = progressBar.value;
|
||||
float interval = 60f;
|
||||
value = Mathf.Round(value / interval) * interval;
|
||||
return value;
|
||||
}
|
||||
|
||||
private void UpdateTimeText()
|
||||
{
|
||||
int minute = (int)progressBar.value / 60;
|
||||
int second = (int)progressBar.value % 60;
|
||||
string timeStr = time.ToString("00");
|
||||
if (progressBar.value == 3600)
|
||||
{
|
||||
timeStr = (time + 1).ToString("00");
|
||||
minute = 0;
|
||||
}
|
||||
playTimeTxt.text = $"{timeStr}:{minute.ToString("00")}:{second.ToString("00")}";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bce52072cdea6144b98e9297b89d6558
|
||||
Reference in New Issue
Block a user