Files
CSI-Z420-Tablet-Multi-Funct…/Services/TestCalculationService.cs
GukSang.Jin 69557bc108 更新122
2026-05-19 20:33:16 +08:00

151 lines
5.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using TabletTester2025.Models;
namespace TabletTester2025.Services
{
public readonly record struct HardnessStatistics(
double Average,
double AverageDeviation,
double RsdPercent,
double Maximum,
double Minimum,
int Count,
bool IsPass);
public static class TestCalculationService
{
public static HardnessStatistics CalculateHardness(
IReadOnlyCollection<double> values,
double internalMin,
double internalMax,
int requiredCount)
{
var validValues = values
.Where(value => double.IsFinite(value) && value > 0)
.ToList();
if (validValues.Count == 0)
return new HardnessStatistics(0, 0, 0, 0, 0, 0, false);
double average = validValues.Average();
double averageDeviation = validValues.Average(value => Math.Abs(value - average));
double standardDeviation = CalculateSampleStandardDeviation(validValues, average);
double rsd = average == 0 ? 0 : standardDeviation / average * 100;
bool countMet = validValues.Count >= Math.Max(1, requiredCount);
bool inRange = validValues.All(value => value >= internalMin && value <= internalMax);
return new HardnessStatistics(
average,
averageDeviation,
rsd,
validValues.Max(),
validValues.Min(),
validValues.Count,
countMet && inRange);
}
public static double CalculateFriabilityLossPercent(double weightBefore, double weightAfter)
{
if (!double.IsFinite(weightBefore) || !double.IsFinite(weightAfter) || weightBefore <= 0)
throw new InvalidOperationException("脆碎前重量必须大于0");
if (weightAfter < 0)
throw new InvalidOperationException("脆碎后重量数据异常");
if (weightAfter > weightBefore)
throw new InvalidOperationException("脆碎后重量不能大于初始重量");
return (weightBefore - weightAfter) / weightBefore * 100;
}
public static bool TryGetDissolutionRateAt30Min(
IReadOnlyList<double> times,
IReadOnlyList<double> values,
out double rate)
{
rate = 0;
if (times.Count == 0 || times.Count != values.Count)
return false;
var points = times
.Zip(values, (time, value) => new { Time = time, Value = value })
.Where(point => double.IsFinite(point.Time)
&& double.IsFinite(point.Value)
&& point.Time >= 0
&& point.Value >= 0
&& point.Value <= 150)
.OrderBy(point => point.Time)
.ToList();
if (points.Count == 0)
return false;
var exact = points.FirstOrDefault(point => Math.Abs(point.Time - 30) < 0.0001);
if (exact != null)
{
rate = exact.Value;
return true;
}
var before = points.LastOrDefault(point => point.Time < 30);
var after = points.FirstOrDefault(point => point.Time > 30);
if (before == null || after == null || after.Time <= before.Time)
return false;
double fraction = (30 - before.Time) / (after.Time - before.Time);
rate = before.Value + (after.Value - before.Value) * fraction;
return double.IsFinite(rate);
}
public static double CalculateRSquared(IReadOnlyList<double> timeMinutes, IReadOnlyList<double> concentration)
{
if (timeMinutes.Count < 2 || timeMinutes.Count != concentration.Count)
return 0;
int n = timeMinutes.Count;
double sumX = timeMinutes.Sum();
double sumY = concentration.Sum();
double sumXY = timeMinutes.Zip(concentration, (x, y) => x * y).Sum();
double sumX2 = timeMinutes.Select(x => x * x).Sum();
double sumY2 = concentration.Select(y => y * y).Sum();
double numerator = n * sumXY - sumX * sumY;
double denominator = Math.Sqrt((n * sumX2 - sumX * sumX) * (n * sumY2 - sumY * sumY));
if (denominator <= 0 || double.IsNaN(denominator))
return 0;
double r = numerator / denominator;
double result = r * r;
return double.IsFinite(result) ? result : 0;
}
public static bool ResolveCurrentTestQualified(
TestType currentTest,
bool hardnessPass,
bool friabilityPass,
bool disintegrationPass,
bool dissolutionPass)
{
return currentTest switch
{
TestType.Hardness => hardnessPass,
TestType.Friability => friabilityPass,
TestType.Disintegration => disintegrationPass,
TestType.Dissolution => dissolutionPass,
_ => false
};
}
private static double CalculateSampleStandardDeviation(IReadOnlyList<double> values, double average)
{
if (values.Count < 2)
return 0;
double sum = values.Sum(value => Math.Pow(value - average, 2));
return Math.Sqrt(sum / (values.Count - 1));
}
}
}