using System.ComponentModel; using System.Globalization; using System.Runtime.CompilerServices; namespace DentistryHandpieces; public enum ErrorMode { Absolute, Percent } public enum PlcFloatWordOrder { LowWordFirst, HighWordFirst } public sealed class TestPointRow : INotifyPropertyChanged { private string _measuredText = string.Empty; private string _note = string.Empty; public TestPointRow(string projectName, double target, string unit, double tolerance, ErrorMode errorMode) { ProjectName = projectName; Target = target; Unit = unit; Tolerance = tolerance; ErrorMode = errorMode; } public event PropertyChangedEventHandler? PropertyChanged; public string ProjectName { get; } public double Target { get; } public string Unit { get; } public double Tolerance { get; } public ErrorMode ErrorMode { get; } public string TargetText => $"{FormatNumber(Target)} {Unit}"; public string MeasuredText { get => _measuredText; set { if (_measuredText == value) { return; } _measuredText = value; OnPropertyChanged(); OnPropertyChanged(nameof(AllowedRangeText)); OnPropertyChanged(nameof(ResultText)); } } public string AllowedRangeText { get { (double lower, double upper) = GetAllowedRange(); return $"{FormatNumber(lower)} - {FormatNumber(upper)} {Unit}"; } } public string ToleranceText => ErrorMode == ErrorMode.Absolute ? $"±{FormatNumber(Tolerance)} {Unit}" : $"±{Tolerance:P0}"; public string ResultText { get { if (!TryGetMeasuredValue(out double measured)) { return "待输入"; } (double lower, double upper) = GetAllowedRange(); return measured >= lower && measured <= upper ? "合格" : "不合格"; } } public string Note { get => _note; set { if (_note == value) { return; } _note = value; OnPropertyChanged(); } } public PointPayload ToPayload() { return new PointPayload { ProjectName = ProjectName, Target = Target, Unit = Unit, TargetText = TargetText, MeasuredText = MeasuredText, AllowedRangeText = AllowedRangeText, ToleranceText = ToleranceText, Result = ResultText, Note = Note }; } private (double Lower, double Upper) GetAllowedRange() { double delta = ErrorMode == ErrorMode.Absolute ? Tolerance : Target * Tolerance; return (Target - delta, Target + delta); } private bool TryGetMeasuredValue(out double measured) { return double.TryParse(MeasuredText, NumberStyles.Float, CultureInfo.CurrentCulture, out measured) || double.TryParse(MeasuredText, NumberStyles.Float, CultureInfo.InvariantCulture, out measured); } private static string FormatNumber(double value) { return value.ToString("0.####", CultureInfo.InvariantCulture); } private void OnPropertyChanged([CallerMemberName] string? propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } public sealed class AcceptancePayload { public int SchemaVersion { get; init; } = 2; public string SessionId { get; init; } = string.Empty; public DateTime CreatedAt { get; init; } public DateTime LastUpdatedAt { get; init; } public string SampleCode { get; init; } = string.Empty; public string OperatorName { get; init; } = string.Empty; public string OverallResult { get; init; } = string.Empty; public TestParameterConfig ParameterSnapshot { get; init; } = TestParameterConfig.CreateDefault(); public List Projects { get; init; } = []; public List Runs { get; init; } = []; } public sealed class ProjectPayload { public string Name { get; init; } = string.Empty; public string Requirement { get; init; } = string.Empty; public string Result { get; init; } = string.Empty; public List Points { get; init; } = []; public TorqueCurvePayload? TorqueCurve { get; init; } } public sealed class PointPayload { public string ProjectName { get; init; } = string.Empty; public double Target { get; init; } public string Unit { get; init; } = string.Empty; public string TargetText { get; init; } = string.Empty; public string MeasuredText { get; init; } = string.Empty; public string AllowedRangeText { get; init; } = string.Empty; public string ToleranceText { get; init; } = string.Empty; public string Result { get; init; } = string.Empty; public string Note { get; init; } = string.Empty; } public sealed class TorqueSamplePayload { public double ElapsedSeconds { get; init; } public double SpeedRpm { get; init; } public double TorqueMilliNewtonMeters { get; init; } public bool IsStableHold { get; init; } } public sealed class TorqueCurvePayload { public double HoldTimeSeconds { get; init; } public int EvaluationSampleCount { get; init; } public double? EvaluationStartSeconds { get; init; } public double? EvaluationEndSeconds { get; init; } public double ChangeThresholdMilliNewtonMeters { get; init; } public double SpeedChangeThresholdRpm { get; init; } public double MinTorqueMilliNewtonMeters { get; init; } public double MaxTorqueMilliNewtonMeters { get; init; } public double AverageTorqueMilliNewtonMeters { get; init; } public double FluctuationMilliNewtonMeters { get; init; } public double MinSpeedRpm { get; init; } public double MaxSpeedRpm { get; init; } public double AverageSpeedRpm { get; init; } public double SpeedFluctuationRpm { get; init; } public string Result { get; init; } = string.Empty; public List Samples { get; init; } = []; } public sealed class TestRunPayload { public string RunId { get; init; } = string.Empty; public string TestType { get; init; } = string.Empty; public DateTime StartedAt { get; init; } public DateTime? CompletedAt { get; set; } public string CompletionStatus { get; set; } = "测试中"; public TestParameterConfig ParameterSnapshot { get; init; } = TestParameterConfig.CreateDefault(); public double? FinalDisplacementMm { get; set; } public double? FinalAxialForceN { get; set; } public double? FinalSpeedRpm { get; set; } public double? FinalTorqueMilliNewtonMeters { get; set; } public double? NoLoadSpeedRpm { get; set; } public double? NoLoadSpeedErrorRatePercent { get; set; } public List Samples { get; init; } = []; } public sealed class RealtimeSamplePayload { public long Sequence { get; init; } public DateTime SampledAt { get; init; } public double DialIndicatorMm { get; init; } public double RelativeDisplacementMm { get; init; } public double AxialAxisPositionMm { get; init; } public double AxialSampleStartMm { get; init; } public double AxialSampleEndMm { get; init; } public double AxialSampleDifferenceMm { get; init; } public double AxialForceN { get; init; } public double SpeedTorqueAxisPositionMm { get; init; } public double SpeedTorqueDisplacementMm { get; init; } public double SpeedTorquePeakTorqueMilliNewtonMeters { get; init; } public double RealtimeTorqueMilliNewtonMeters { get; init; } public double RealtimeSpeedRpm { get; init; } public double RealtimePressureKpa { get; init; } public double NoLoadSpeedRpm { get; init; } public double NoLoadSpeedErrorRatePercent { get; init; } public bool SpeedTorqueDone { get; init; } public bool SpeedTorqueStable { get; init; } public bool SpeedTorqueResetEnabled { get; init; } public bool SpeedTorqueResetDone { get; init; } public bool AxialResetEnabled { get; init; } public bool AxialResetDone { get; init; } } public sealed class TestParameterConfig { public double AxialDisplacementLimit { get; init; } public double AxialSpeed { get; init; } public double AxialManualDisplacement { get; init; } public double AxialForceLowerLimit { get; init; } public double AxialForceUpperLimit { get; init; } public double AxialForceCoefficient { get; init; } public double AxialForceSetpoint { get; init; } public double AxialJumpForceSetpoint { get; init; } public bool UseAxialPullForceSetpoint { get; init; } public double AxialForceProtection { get; init; } public double AxialForceHoldTime { get; init; } public double SpeedTorqueDisplacementLimit { get; init; } public double SpeedTorqueSpeed { get; init; } public double SpeedTorqueManualDisplacement { get; init; } public double TorqueCoefficient { get; init; } public double TorqueProtection { get; init; } public double HoldTorque { get; init; } public double TorqueHoldTime { get; init; } public double SpeedCoefficient { get; init; } public double SpeedStopThreshold { get; init; } public double PressureCoefficient { get; init; } public double LoadSpeedSetting { get; init; } public double NoLoadSpeedSetting { get; init; } public string PlcIpAddress { get; init; } = "192.168.1.10"; public int PlcPort { get; init; } = 502; public int PlcUnitId { get; init; } = 1; public int PlcPulseMilliseconds { get; init; } = 200; public int PlcTimeoutMilliseconds { get; init; } = 2000; public PlcFloatWordOrder FloatWordOrder { get; init; } = PlcFloatWordOrder.LowWordFirst; public PlcConnectionConfig ToPlcConnectionConfig() { return new PlcConnectionConfig { IpAddress = string.IsNullOrWhiteSpace(PlcIpAddress) ? "192.168.1.10" : PlcIpAddress, Port = PlcPort > 0 ? PlcPort : 502, UnitId = PlcUnitId is >= 0 and <= byte.MaxValue ? (byte)PlcUnitId : (byte)1, PulseMilliseconds = PlcPulseMilliseconds > 0 ? PlcPulseMilliseconds : 200, TimeoutMilliseconds = PlcTimeoutMilliseconds > 0 ? PlcTimeoutMilliseconds : 2000, FloatWordOrder = FloatWordOrder }; } public static TestParameterConfig CreateDefault() { return new TestParameterConfig { AxialDisplacementLimit = 0, AxialSpeed = 10, AxialManualDisplacement = 0, AxialForceLowerLimit = 0, AxialForceUpperLimit = 0, AxialForceCoefficient = 1, AxialForceSetpoint = 0, AxialJumpForceSetpoint = 0, UseAxialPullForceSetpoint = false, AxialForceProtection = 50, AxialForceHoldTime = 0, SpeedTorqueDisplacementLimit = 0, SpeedTorqueSpeed = 10, SpeedTorqueManualDisplacement = 0, TorqueCoefficient = 1, TorqueProtection = 0, HoldTorque = 0, TorqueHoldTime = 0, SpeedCoefficient = 1, SpeedStopThreshold = 0, PressureCoefficient = 1, LoadSpeedSetting = 0, NoLoadSpeedSetting = 0, PlcIpAddress = "192.168.1.10", PlcPort = 502, PlcUnitId = 1, PlcPulseMilliseconds = 200, PlcTimeoutMilliseconds = 2000, FloatWordOrder = PlcFloatWordOrder.LowWordFirst }; } }