using System; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Text.Json; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using LiveChartsCore; using LiveChartsCore.Defaults; using LiveChartsCore.SkiaSharpView; using LiveChartsCore.SkiaSharpView.Painting; using SkiaSharp; namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModels { public partial class MainWindowViewModel : ViewModelBase { private bool isLoadingDeviceSettings; [ObservableProperty] private string testNumber = $"SLIP-{DateTime.Now:yyyyMMdd-HHmm}"; [ObservableProperty] private string operatorName = string.Empty; [ObservableProperty] private string methodName = "GB/T 3903.6-2024"; [ObservableProperty] private string reportName = string.Empty; [ObservableProperty] private string sampleFeature = "整鞋样品 / 瓷砖接触面 / 水平测试"; [ObservableProperty] private string currentStatus = "测试停止,请按“测试”键进行下次试验"; [ObservableProperty] private int uploadProgress = 68; [ObservableProperty] private string manualDistance = "0"; [ObservableProperty] private int selectedSampleIndex; [ObservableProperty] private bool isSettingsDialogOpen; [ObservableProperty] private string manualSpeed = "0.00"; [ObservableProperty] private string manualDisplacement = "0.00"; [ObservableProperty] private string testSpeed = "0.30"; [ObservableProperty] private string normalPressureCoefficient = "0.00"; [ObservableProperty] private string frictionCoefficient1 = "0.00"; [ObservableProperty] private string frictionCoefficient2 = "0.00"; public string TargetLoadText { get; } = "400 N"; public string ActualLoadText { get; } = "400 N"; public string TestSpeedText => $"{TestSpeed} m/s"; public string SampleRateText { get; } = "30 Hz"; public string ActiveMode { get; } = "水平模式"; public string DeviceStatus { get; } = "联机 / 待机"; public string BatchNumber { get; } = DateTime.Now.ToString("yyyy-MM-dd"); public string ResultSummary { get; } = "平均 0.62"; public string UploadProgressText => $"{UploadProgress}%"; public string StaticCoefficient { get; } = "0.66"; public string VerticalPressure { get; } = "401.6"; public string HorizontalForce { get; } = "248.9"; public string FrictionCoefficient { get; } = "0.62"; public string Distance { get; } = "218.4"; public string StandardReference { get; } = "GB/T 3903.6-2024 7.1-8:速度、载荷、三次平均与系数计算"; public ObservableCollection Samples { get; } = [ new(16, "08:46:35", "0.58", "0.57", "有效"), new(15, "08:46:27", "0.71", "0.61", "有效"), new(14, "08:46:12", "0.66", "0.66", "有效"), new(13, "08:46:04", "0.77", "0.69", "有效"), new(12, "08:45:51", "0.62", "0.62", "有效"), new(11, "08:45:43", "0.74", "0.64", "有效"), new(10, "08:45:28", "0.64", "0.64", "有效"), new(9, "08:45:19", "0.73", "0.66", "有效"), new(8, "08:45:03", "0.57", "0.56", "有效"), new(7, "08:44:49", "0.65", "0.57", "有效"), new(6, "08:44:35", "0.56", "0.54", "有效"), new(5, "08:44:27", "0.63", "0.56", "有效"), new(4, "08:44:09", "0.56", "0.54", "有效"), new(3, "08:44:01", "0.63", "0.56", "有效"), new(2, "08:43:41", "0.55", "0.52", "有效"), new(1, "08:43:33", "0.62", "0.55", "有效"), ]; public DrawMarginFrame ChartFrame { get; } = new() { Fill = new SolidColorPaint(SKColor.Parse("#FFFFFF")), Stroke = new SolidColorPaint(SKColor.Parse("#CBD5E1")) { StrokeThickness = 1.4f } }; public SolidColorPaint LegendTextPaint { get; } = new(SKColor.Parse("#334155")); public ISeries[] Series { get; } = [ new LineSeries { Name = "垂直压力(N)", Values = CreatePressureSeries(), MiniatureShapeSize = 8, MiniatureStrokeThickness = 2, Stroke = new SolidColorPaint(SKColor.Parse("#DC2626")) { StrokeThickness = 3.6f }, Fill = new SolidColorPaint(new SKColor(220, 38, 38, 18)), GeometryFill = new SolidColorPaint(SKColors.White), GeometryStroke = new SolidColorPaint(SKColor.Parse("#DC2626")) { StrokeThickness = 2 }, GeometrySize = 5, LineSmoothness = 0.65, ScalesYAt = 0 }, new LineSeries { Name = "水平拉力(N)", Values = CreatePullSeries(), MiniatureShapeSize = 8, MiniatureStrokeThickness = 2, Stroke = new SolidColorPaint(SKColor.Parse("#16A34A")) { StrokeThickness = 3.4f }, Fill = new SolidColorPaint(new SKColor(22, 163, 74, 16)), GeometryFill = new SolidColorPaint(SKColors.White), GeometryStroke = new SolidColorPaint(SKColor.Parse("#16A34A")) { StrokeThickness = 2 }, GeometrySize = 5, LineSmoothness = 0.65, ScalesYAt = 0 }, new LineSeries { Name = "摩擦系数", Values = CreateFrictionSeries(), MiniatureShapeSize = 8, MiniatureStrokeThickness = 2, Stroke = new SolidColorPaint(SKColor.Parse("#C026D3")) { StrokeThickness = 3.4f }, Fill = new SolidColorPaint(new SKColor(192, 38, 211, 16)), GeometryFill = new SolidColorPaint(SKColors.White), GeometryStroke = new SolidColorPaint(SKColor.Parse("#C026D3")) { StrokeThickness = 2 }, GeometrySize = 5, LineSmoothness = 0.65, ScalesYAt = 1 }, new LineSeries { Name = "距离(mm)", Values = CreateDistanceSeries(), MiniatureShapeSize = 8, MiniatureStrokeThickness = 2, Stroke = new SolidColorPaint(SKColor.Parse("#2563EB")) { StrokeThickness = 3.2f }, Fill = new SolidColorPaint(new SKColor(37, 99, 235, 14)), GeometryFill = new SolidColorPaint(SKColors.White), GeometryStroke = new SolidColorPaint(SKColor.Parse("#2563EB")) { StrokeThickness = 2 }, GeometrySize = 5, LineSmoothness = 0.35, ScalesYAt = 0 } ]; public Axis[] XAxes { get; } = [ new Axis { Name = "时间(s)", MinLimit = -0.10, MaxLimit = 0.62, UnitWidth = 0.05, SeparatorsPaint = new SolidColorPaint(SKColor.Parse("#D7E0EA")) { StrokeThickness = 1 }, SubseparatorsPaint = new SolidColorPaint(SKColor.Parse("#EEF3F8")) { StrokeThickness = 1 }, SubseparatorsCount = 4, LabelsPaint = new SolidColorPaint(SKColor.Parse("#475569")), NamePaint = new SolidColorPaint(SKColor.Parse("#334155")), TextSize = 12, NameTextSize = 13, Padding = new LiveChartsCore.Drawing.Padding(4, 3, 4, 3) } ]; public Axis[] YAxes { get; } = [ new Axis { Name = "压力 / 拉力 / 距离", MinLimit = 0, MaxLimit = 800, UnitWidth = 50, SeparatorsPaint = new SolidColorPaint(SKColor.Parse("#D7E0EA")) { StrokeThickness = 1 }, SubseparatorsPaint = new SolidColorPaint(SKColor.Parse("#EEF3F8")) { StrokeThickness = 1 }, SubseparatorsCount = 4, LabelsPaint = new SolidColorPaint(SKColor.Parse("#475569")), NamePaint = new SolidColorPaint(SKColor.Parse("#334155")), TextSize = 11, NameTextSize = 12, Padding = new LiveChartsCore.Drawing.Padding(2, 2, 3, 2) }, new Axis { Name = "摩擦系数", Position = LiveChartsCore.Measure.AxisPosition.End, MinLimit = 0, MaxLimit = 1.5, UnitWidth = 0.1, LabelsPaint = new SolidColorPaint(SKColor.Parse("#7E22CE")), NamePaint = new SolidColorPaint(SKColor.Parse("#7E22CE")), SeparatorsPaint = new SolidColorPaint(SKColor.Parse("#F1E7FF")) { StrokeThickness = 1 }, TextSize = 11, NameTextSize = 12, Padding = new LiveChartsCore.Drawing.Padding(3, 2, 2, 2) } ]; public MainWindowViewModel() { LoadDeviceSettings(); } [RelayCommand] private void Clear() { UploadProgress = 0; CurrentStatus = "传感器已清零,垂直压力与水平拉力基线重置完成"; } [RelayCommand] private void Preload() { UploadProgress = 34; CurrentStatus = "预压中:0.2s 内施加目标垂直载荷,等待滑动动作"; } [RelayCommand] private void StartTest() { UploadProgress = 86; CurrentStatus = "测试运行:正在采集峰值静摩擦力与 0.3s-0.6s 动摩擦力"; } [RelayCommand] private void StopTest() { UploadProgress = 68; CurrentStatus = "测试停止,请按“测试”键进行下次试验"; } [RelayCommand] private void ExportReport() { UploadProgress = 100; CurrentStatus = "报告数据已整理:三次平均、静/动摩擦系数与试验条件可导出"; } [RelayCommand] private void LiftFast() => CurrentStatus = "垂直架快速上升"; [RelayCommand] private void Lift() => CurrentStatus = "垂直架提升"; [RelayCommand] private void Lower() => CurrentStatus = "垂直架下降"; [RelayCommand] private void LowerFast() => CurrentStatus = "垂直架快速下降"; [RelayCommand] private void MoveLeft() => CurrentStatus = "水平板向左移动"; [RelayCommand] private void MoveRight() => CurrentStatus = "水平板向右移动"; [RelayCommand] private void DeleteSelectedSample() { var sample = Samples.FirstOrDefault(x => x.Index == SelectedSampleIndex); if (sample is null) { CurrentStatus = $"未找到序号 {SelectedSampleIndex} 的实验数据"; return; } Samples.Remove(sample); CurrentStatus = $"已删除序号 {SelectedSampleIndex} 的实验数据"; } [RelayCommand] private void ShowSettingsDialog() { IsSettingsDialogOpen = true; } [RelayCommand] private void CloseSettingsDialog() { IsSettingsDialogOpen = false; } partial void OnUploadProgressChanged(int value) { OnPropertyChanged(nameof(UploadProgressText)); } partial void OnManualSpeedChanged(string value) => SaveDeviceSettings(); partial void OnManualDisplacementChanged(string value) => SaveDeviceSettings(); partial void OnTestSpeedChanged(string value) { OnPropertyChanged(nameof(TestSpeedText)); SaveDeviceSettings(); } partial void OnNormalPressureCoefficientChanged(string value) => SaveDeviceSettings(); partial void OnFrictionCoefficient1Changed(string value) => SaveDeviceSettings(); partial void OnFrictionCoefficient2Changed(string value) => SaveDeviceSettings(); private void LoadDeviceSettings() { if (!File.Exists(DeviceSettingsPath)) { return; } try { var json = File.ReadAllText(DeviceSettingsPath); var settings = JsonSerializer.Deserialize(json); if (settings is null) { return; } isLoadingDeviceSettings = true; ManualSpeed = settings.ManualSpeed ?? ManualSpeed; ManualDisplacement = settings.ManualDisplacement ?? ManualDisplacement; TestSpeed = settings.TestSpeed ?? TestSpeed; NormalPressureCoefficient = settings.NormalPressureCoefficient ?? NormalPressureCoefficient; FrictionCoefficient1 = settings.FrictionCoefficient1 ?? FrictionCoefficient1; FrictionCoefficient2 = settings.FrictionCoefficient2 ?? FrictionCoefficient2; } catch { } finally { isLoadingDeviceSettings = false; } } private void SaveDeviceSettings() { if (isLoadingDeviceSettings) { return; } try { Directory.CreateDirectory(Path.GetDirectoryName(DeviceSettingsPath)!); var settings = new DeviceSettings( ManualSpeed, ManualDisplacement, TestSpeed, NormalPressureCoefficient, FrictionCoefficient1, FrictionCoefficient2); var json = JsonSerializer.Serialize(settings, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(DeviceSettingsPath, json); } catch { } } private static string DeviceSettingsPath => Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "FootwearSlipResistance", "device-settings.json"); private static ObservablePoint[] CreatePressureSeries() => [ new(-0.10, 36), new(-0.08, 42), new(-0.06, 50), new(-0.04, 90), new(-0.02, 400), new(0.00, 405), new(0.05, 407), new(0.10, 409), new(0.20, 407), new(0.30, 412), new(0.40, 421), new(0.50, 426), new(0.62, 430) ]; private static ObservablePoint[] CreatePullSeries() => [ new(-0.10, 0), new(-0.05, 0), new(0.00, 160), new(0.04, 226), new(0.08, 232), new(0.16, 236), new(0.26, 241), new(0.36, 248), new(0.46, 255), new(0.56, 260), new(0.62, 266) ]; private static ObservablePoint[] CreateFrictionSeries() => [ new(-0.10, 0), new(-0.02, 0), new(0.00, 0.47), new(0.04, 0.55), new(0.08, 0.57), new(0.16, 0.56), new(0.26, 0.58), new(0.36, 0.59), new(0.46, 0.60), new(0.56, 0.61), new(0.62, 0.62) ]; private static ObservablePoint[] CreateDistanceSeries() => [ new(-0.10, 0), new(0.00, 0), new(0.06, 20), new(0.12, 42), new(0.20, 72), new(0.30, 108), new(0.40, 145), new(0.50, 178), new(0.62, 218) ]; } public sealed record TestSample( int Index, string Time, string StaticCoefficient, string DynamicCoefficient, string Verdict); internal sealed record DeviceSettings( string ManualSpeed, string ManualDisplacement, string TestSpeed, string NormalPressureCoefficient, string FrictionCoefficient1, string FrictionCoefficient2); }