Files
EnglewoodLAB/Assets/Scripts/Factory/Playback/PlaybackSQLiteService.cs

250 lines
10 KiB
C#

using Cysharp.Threading.Tasks;
using SQLite4Unity3d;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UVC.Factory.Playback;
using UVC.Util;
namespace UVC.Factory
{
public class PlaybackSQLiteService
{
// SQLite 데이터베이스 연결 객체
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; }
/// <summary>
/// 데이터베이스 파일에 연결합니다.
/// 예시:
/// <code>
/// var service = new PlaybackSQLiteService();
/// service.Connect("2024-07-29", "sample.db");
/// </code>
/// </summary>
/// <param name="date">폴더명(날짜 등)</param>
/// <param name="sqliteFileName">SQLite 파일명</param>
public void Connect(string date, string sqliteFileName)
{
this.date = date;
this.sqliteFileName = sqliteFileName;
dbConnection = new SQLiteConnection(Path.Combine(PlaybackService.PlaybackFolderPath, date, sqliteFileName));
}
/// <summary>
/// 데이터베이스 연결을 닫습니다.
/// 예시:
/// <code>
/// service.CloseDB();
/// </code>
/// </summary>
public void CloseDB()
{
dbConnection.Close();
dbConnection = null;
}
/// <summary>
/// realTime 테이블에 데이터를 추가합니다.
/// 예시:
/// <code>
/// int rows = service.Insert("센서값", "2024-07-29T12:00:00.000Z", "온도값");
/// </code>
/// </summary>
/// <param name="data">저장할 데이터(문자열)</param>
/// <param name="timeStamp">yyyy-MM-ddTHH:mm:ss.fffZ 형식의 시간</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;
}
readonly string[] queryParts =
{
"SELECT * FROM realTime WHERE ",
"timestamp >= '",
"' AND timestamp < '",
"timestamp <= '",
"' AND timestamp > '",
" ORDER BY timestamp ",
" LIMIT ",
};
/// <summary>
/// 특정 시간(selectTime) 기준으로 ±second 범위의 데이터를 조회합니다.
/// 예시:
/// <code>
/// // 10초 뒤까지의 데이터 5개를 조회(오름차순)
/// var list = await service.SelectBySecond("2024-07-29T12:00:00.000Z", 10, true, 5);
/// </code>
/// </summary>
/// <param name="selectTime">기준 시간(yyyy-MM-ddTHH:mm:ss.fffZ)</param>
/// <param name="second">±초(양수: 미래, 음수: 과거)</param>
/// <param name="orderAsc">true: 오래된 시간부터, false: 최근 시간부터</param>
/// <param name="limit">최대 조회 개수(0이면 제한 없음)</param>
/// <returns>조회된 데이터 리스트</returns>
public async UniTask<List<PlaybackSQLiteDataEntity>> SelectBySecond(string selectTime, int second, bool orderAsc = true, int limit = 0)
{
#if UNITY_WEBGL && !UNITY_EDITOR
DateTime target = DateTimeUtil.UtcParse(selectTime).AddSeconds(second);
string targetTime = DateTimeUtil.FormatTime(target);
queryBuilder.Append(queryParts[0]);
if (second > 0)
{
queryBuilder.Append($"{queryParts[1]}{selectTime}{queryParts[2]}{targetTime}'");
}
else
{
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(";");
var query = queryBuilder.ToString();
queryBuilder.Clear();
// 동기 실행 (WebGL은 ThreadPool 사용 불가)
var list = dbConnection.Query<PlaybackSQLiteDataEntity>(query);
return list;
#else
bool isMainThread = PlayerLoopHelper.IsMainThread;
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);
});
if (!isMainThread) await UniTask.SwitchToThreadPool();
return result;
#endif
}
StringBuilder queryBuilder = new();
/// baseInfo 테이블에서 특정 시간(selectTime) 기준으로 ±second 범위의 데이터를 조회합니다.
/// 예시:
/// <code>
/// // 5초 전까지의 데이터 1개를 조회(내림차순)
/// var list = await service.SelectBySecondBaseInfo("2024-07-29T12:00:00.000Z", -5);
/// </code>
/// </summary>
/// <param name="selectTime">기준 시간(yyyy-MM-ddTHH:mm:ss.fffZ)</param>
/// <param name="second">±초(양수: 미래, 음수: 과거)</param>
/// <param name="orderAsc">true: 오래된 시간부터, false: 최근 시간부터</param>
/// <param name="limit">최대 조회 개수</param>
/// <returns>조회된 데이터 리스트</returns>
public async UniTask<List<PlaybackSQLiteDataEntity>> SelectBySecondBaseInfo(string selectTime, int second, bool orderAsc = false, int limit = 1)
{
#if UNITY_WEBGL && !UNITY_EDITOR
DateTime target = DateTimeUtil.UtcParse(selectTime).AddSeconds(second);
string targetTime = DateTimeUtil.FormatTime(target);
queryBuilder.Append("SELECT * FROM baseInfo WHERE ");
if (second > 0)
{
queryBuilder.Append($"timestamp >= '{selectTime}' AND timestamp < '{targetTime}'");
}
else
{
queryBuilder.Append($"timestamp <= '{selectTime}' AND timestamp > '{targetTime}'");
}
queryBuilder.Append($" ORDER BY timestamp {(orderAsc ? "asc" : "desc")}");
if (limit > 0) queryBuilder.Append($" LIMIT {limit}");
queryBuilder.Append(";");
var query = queryBuilder.ToString();
queryBuilder.Clear();
var list = dbConnection.Query<PlaybackSQLiteDataEntity>(query);
return list;
#else
bool isMainThread = PlayerLoopHelper.IsMainThread;
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);
});
if (!isMainThread) await UniTask.SwitchToThreadPool();
return result;
#endif
}
}
/// <summary>
/// 데이터베이스에서 사용하는 데이터 구조체입니다.
/// 예시:
/// <code>
/// var entity = new PlaybackSQLiteDataEntity {
/// data = "센서값",
/// timestamp = "2024-07-29T12:00:00.000Z",
/// temp = "임시값"
/// };
/// </code>
/// </summary>
[System.Serializable]
public class PlaybackSQLiteDataEntity
{
public string data { get; set; }
[PrimaryKey]
public string timestamp { get; set; }
// timestampHungary는 timestamp를 DateTime으로 변환한 값입니다.
public DateTime timestampHungary { get => DateTimeUtil.UtcStringToHungaryDateTime(timestamp); }
public string temp { get; set; }
}
}