using Aurora; using Aurora.Api; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data; using System.Globalization; using System.IO; using System.Linq; using System.Reactive.Linq; using System.Text; using System.Threading.Tasks; using WeldingCommon.Data; using WeldingCommon.Devices; using WeldingCommon.Settings; using WeldingCommon.Utils; using WeldingCommon.Utils.Diff; namespace WeldingCommon.Recipes { public class RecipeChangeHistoryHandler { protected Logger Log = new Logger("RchHandler", "RecipeChangeHistory_Evnet"); protected IAuroraEnvironment _aurora; protected IRecipeEntry _recipeEntry; protected SystemParameter _settingParameter; protected readonly object _historyLock = new object(); public ObservableCollection HistoryDatas { get; set; } = new ObservableCollection(); private string _currentDateKey; // yyyyMMdd private string GetDateKey(DateTime t) => t.ToString("yyyyMMdd", CultureInfo.InvariantCulture); private string GetDailyFilePath(string dateKey) { var dir = _settingParameter.RecipeHistoryFolderPath; if (!string.IsNullOrEmpty(dir)) Directory.CreateDirectory(dir); return Path.Combine(dir, $"RecipeInfo_{dateKey}.ndjson"); } public void RegistryDeviceHandler(IAuroraEnvironment aurora) { _aurora = aurora; _recipeEntry = aurora.RecipeManager; _settingParameter = _aurora.SettingEntry[CommonSettingIds.SystemParameter] as SystemParameter; EnsureDailyContext(DateTime.Now); } public void AppendChangedItem(string recipeName, ObservableCollection diffs) { lock (_historyLock) { var now = DateTime.Now; EnsureDailyContext(now); var item = new RecipeHistory { Timestamp = now, RecipeName = recipeName, Changes = diffs.Select(d => d.ToString()).ToList() }; HistoryDatas.Add(item); AppendNdjsonLine(GetDailyFilePath(_currentDateKey), item); } } private void EnsureDailyContext(DateTime today) { var key = GetDateKey(today); if (_currentDateKey == key) return; _currentDateKey = key; HistoryDatas.Clear(); var path = GetDailyFilePath(key); LoadNdjson(path, HistoryDatas); } private void AppendNdjsonLine(string path, RecipeHistory item) { var json = JsonConvert.SerializeObject(item); using (var fs = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite)) { using (var sw = new StreamWriter(fs, new UTF8Encoding(encoderShouldEmitUTF8Identifier: true))) { sw.WriteLine(json); } } } private void LoadNdjson(string path, ObservableCollection target) { if (!File.Exists(path)) return; using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { using (var sr = new StreamReader(fs, new UTF8Encoding(encoderShouldEmitUTF8Identifier: true))) { string line; while ((line = sr.ReadLine()) != null) { if (string.IsNullOrWhiteSpace(line)) continue; try { var item = JsonConvert.DeserializeObject(line); if (item != null) target.Add(item); } catch { } } } } } public ObservableCollection FromStartToEndHistory(DateTime start, DateTime end) { if (end < start) { var tmp = start; start = end; end = tmp; } var result = new List(); var day = start.Date; var lastDay = end.Date; lock (_historyLock) { while (day <= lastDay) { var key = GetDateKey(day); var path = GetDailyFilePath(key); if (File.Exists(path)) { using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) using (var sr = new StreamReader(fs, new UTF8Encoding(encoderShouldEmitUTF8Identifier: true))) { string line; while ((line = sr.ReadLine()) != null) { if (string.IsNullOrWhiteSpace(line)) continue; try { var item = JsonConvert.DeserializeObject(line); if (item == null) continue; //if (item.Timestamp >= start && item.Timestamp <= end) result.Add(item); } catch { } } } } day = day.AddDays(1); } var ordered = result.OrderByDescending(x => x.Timestamp); return new ObservableCollection(ordered); } } } }