97 lines
3.2 KiB
C#
97 lines
3.2 KiB
C#
|
|
using System.IO;
|
|||
|
|
using System.Text.Json;
|
|||
|
|
using Microsoft.Extensions.Options;
|
|||
|
|
using SSPCTester.Devices.Config;
|
|||
|
|
|
|||
|
|
namespace SSPCTester.UI.Services;
|
|||
|
|
|
|||
|
|
/// <summary>将机器本地硬件配置保存到 LocalAppData,不修改发布目录。</summary>
|
|||
|
|
public sealed class UserSettingsStore : IDisposable
|
|||
|
|
{
|
|||
|
|
private static readonly JsonSerializerOptions JsonOptions = new()
|
|||
|
|
{
|
|||
|
|
WriteIndented = true,
|
|||
|
|
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
private readonly DeviceOptions _devices;
|
|||
|
|
private readonly StorageOptions _storage;
|
|||
|
|
private readonly ReportOptions _report;
|
|||
|
|
private readonly SemaphoreSlim _writeLock = new(1, 1);
|
|||
|
|
private readonly object _timerLock = new();
|
|||
|
|
private Timer? _timer;
|
|||
|
|
|
|||
|
|
public UserSettingsStore(
|
|||
|
|
IOptions<DeviceOptions> devices,
|
|||
|
|
IOptions<StorageOptions> storage,
|
|||
|
|
IOptions<ReportOptions> report)
|
|||
|
|
{
|
|||
|
|
_devices = devices.Value;
|
|||
|
|
_storage = storage.Value;
|
|||
|
|
_report = report.Value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static string SettingsPath => Path.Combine(
|
|||
|
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
|||
|
|
"SSPCTester", "settings.json");
|
|||
|
|
|
|||
|
|
public void ScheduleSave()
|
|||
|
|
{
|
|||
|
|
lock (_timerLock)
|
|||
|
|
{
|
|||
|
|
_timer?.Dispose();
|
|||
|
|
_timer = new Timer(async _ =>
|
|||
|
|
{
|
|||
|
|
try { await SaveAsync().ConfigureAwait(false); }
|
|||
|
|
catch { /* 后续显式保存或退出时会再次尝试 */ }
|
|||
|
|
}, null, 600, Timeout.Infinite);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public async Task SaveAsync()
|
|||
|
|
{
|
|||
|
|
await _writeLock.WaitAsync().ConfigureAwait(false);
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
string path = SettingsPath;
|
|||
|
|
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
|
|||
|
|
string temp = path + ".tmp";
|
|||
|
|
_devices.Daq.Channels = DaqChannelOptionsNormalizer.Normalize(_devices.Daq.Channels);
|
|||
|
|
var model = new
|
|||
|
|
{
|
|||
|
|
Devices = _devices,
|
|||
|
|
Storage = _storage,
|
|||
|
|
Report = _report
|
|||
|
|
};
|
|||
|
|
await File.WriteAllTextAsync(temp, JsonSerializer.Serialize(model, JsonOptions)).ConfigureAwait(false);
|
|||
|
|
File.Move(temp, path, true);
|
|||
|
|
WriteDaqSaveTrace(path);
|
|||
|
|
}
|
|||
|
|
finally { _writeLock.Release(); }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void WriteDaqSaveTrace(string settingsPath)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
string logDir = Path.Combine(
|
|||
|
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
|||
|
|
"SSPCTester", "Logs");
|
|||
|
|
Directory.CreateDirectory(logDir);
|
|||
|
|
string channels = string.Join(", ", _devices.Daq.Channels
|
|||
|
|
.OrderBy(x => x.PhysicalChannel)
|
|||
|
|
.Select(x => $"CH{x.PhysicalChannel}:{x.Role}/{x.IsCalibrated}"));
|
|||
|
|
File.AppendAllText(
|
|||
|
|
Path.Combine(logDir, "settings-save.log"),
|
|||
|
|
$"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} {settingsPath} Pre={_devices.Daq.PreTriggerMs:R} Post={_devices.Daq.PostTriggerMs:R} Channels=[{channels}]{Environment.NewLine}");
|
|||
|
|
}
|
|||
|
|
catch { }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void Dispose()
|
|||
|
|
{
|
|||
|
|
lock (_timerLock) { _timer?.Dispose(); }
|
|||
|
|
_writeLock.Dispose();
|
|||
|
|
}
|
|||
|
|
}
|