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; } /// /// 데이터베이스 파일에 연결합니다. /// 예시: /// /// var service = new PlaybackSQLiteService(); /// service.Connect("2024-07-29", "sample.db"); /// /// /// 폴더명(날짜 등) /// SQLite 파일명 public void Connect(string date, string sqliteFileName) { this.date = date; this.sqliteFileName = sqliteFileName; dbConnection = new SQLiteConnection(Path.Combine(PlaybackService.PlaybackFolderPath, date, sqliteFileName)); } /// /// 데이터베이스 연결을 닫습니다. /// 예시: /// /// service.CloseDB(); /// /// public void CloseDB() { dbConnection.Close(); dbConnection = null; } /// /// realTime 테이블에 데이터를 추가합니다. /// 예시: /// /// int rows = service.Insert("센서값", "2024-07-29T12:00:00.000Z", "온도값"); /// /// /// 저장할 데이터(문자열) /// yyyy-MM-ddTHH:mm:ss.fffZ 형식의 시간 /// 임시 데이터(옵션) /// 추가된 행 수 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 ", }; /// /// 특정 시간(selectTime) 기준으로 ±second 범위의 데이터를 조회합니다. /// 예시: /// /// // 10초 뒤까지의 데이터 5개를 조회(오름차순) /// var list = await service.SelectBySecond("2024-07-29T12:00:00.000Z", 10, true, 5); /// /// /// 기준 시간(yyyy-MM-ddTHH:mm:ss.fffZ) /// ±초(양수: 미래, 음수: 과거) /// true: 오래된 시간부터, false: 최근 시간부터 /// 최대 조회 개수(0이면 제한 없음) /// 조회된 데이터 리스트 public async UniTask> 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(query); return list; #else bool isMainThread = PlayerLoopHelper.IsMainThread; List 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(query); }); if (!isMainThread) await UniTask.SwitchToThreadPool(); return result; #endif } StringBuilder queryBuilder = new(); /// baseInfo 테이블에서 특정 시간(selectTime) 기준으로 ±second 범위의 데이터를 조회합니다. /// 예시: /// /// // 5초 전까지의 데이터 1개를 조회(내림차순) /// var list = await service.SelectBySecondBaseInfo("2024-07-29T12:00:00.000Z", -5); /// /// /// 기준 시간(yyyy-MM-ddTHH:mm:ss.fffZ) /// ±초(양수: 미래, 음수: 과거) /// true: 오래된 시간부터, false: 최근 시간부터 /// 최대 조회 개수 /// 조회된 데이터 리스트 public async UniTask> 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(query); return list; #else bool isMainThread = PlayerLoopHelper.IsMainThread; List 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(query); }); if (!isMainThread) await UniTask.SwitchToThreadPool(); return result; #endif } } /// /// 데이터베이스에서 사용하는 데이터 구조체입니다. /// 예시: /// /// var entity = new PlaybackSQLiteDataEntity { /// data = "센서값", /// timestamp = "2024-07-29T12:00:00.000Z", /// temp = "임시값" /// }; /// /// [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; } } }