250 lines
10 KiB
C#
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; }
|
|
}
|
|
}
|