namespace Monitor.Backend.Analysis; public enum AccKind : byte { Counter = 1, Ratio = 2, Sum = 3, Min = 4, Max = 5, Histogram = 6, RunLength = 7, Series = 8, } /// /// 所有指标的中间状态只能是下面几种累加器之一。合并规则只写一次, /// 存储、降采样、按时间范围聚合都通用处理。 /// public abstract class Accumulator { public abstract AccKind Kind { get; } /// 加入一个观测值。含义由累加器决定:Counter 加 ,Ratio 的 (a, b) 是 (分子, 分母),Histogram 记录 a。 public abstract void Add(double a, double b = 0); /// 合并同类型的另一个累加器(精确合并,不是平均)。 public abstract void Merge(Accumulator other); public abstract Accumulator Clone(); /// 样本数,用于置信说明。 public abstract ulong SampleCount { get; } public static Accumulator Create(AccKind kind) => kind switch { AccKind.Counter => new CounterAcc(), AccKind.Ratio => new RatioAcc(), AccKind.Sum => new SumAcc(), AccKind.Min => new MinAcc(), AccKind.Max => new MaxAcc(), AccKind.Histogram => new HistogramAcc(), AccKind.RunLength => new RunLengthAcc(), AccKind.Series => new SeriesAcc(), _ => throw new ArgumentOutOfRangeException(nameof(kind)), }; protected T Cast(Accumulator other) where T : Accumulator => other as T ?? throw new InvalidOperationException($"cannot merge {other.Kind} into {Kind}"); } public sealed class CounterAcc : Accumulator { public ulong Value { get; set; } public override AccKind Kind => AccKind.Counter; public override ulong SampleCount => Value; public override void Add(double a, double b = 0) => Value += (ulong)Math.Max(0, a); public override void Merge(Accumulator other) => Value += Cast(other).Value; public override Accumulator Clone() => new CounterAcc { Value = Value }; } public sealed class RatioAcc : Accumulator { public ulong Numerator { get; set; } public ulong Denominator { get; set; } public override AccKind Kind => AccKind.Ratio; public override ulong SampleCount => Denominator; /// 百分比(0–100);没有样本时为 null。 public double? Percent => Denominator == 0 ? null : 100.0 * Numerator / Denominator; public override void Add(double a, double b = 0) { Numerator += (ulong)Math.Max(0, a); Denominator += (ulong)Math.Max(0, b); } public override void Merge(Accumulator other) { var o = Cast(other); Numerator += o.Numerator; Denominator += o.Denominator; } public override Accumulator Clone() => new RatioAcc { Numerator = Numerator, Denominator = Denominator }; } public sealed class SumAcc : Accumulator { public double Sum { get; set; } public ulong Count { get; set; } public override AccKind Kind => AccKind.Sum; public override ulong SampleCount => Count; public double? Mean => Count == 0 ? null : Sum / Count; public override void Add(double a, double b = 0) { Sum += a; Count++; } public override void Merge(Accumulator other) { var o = Cast(other); Sum += o.Sum; Count += o.Count; } public override Accumulator Clone() => new SumAcc { Sum = Sum, Count = Count }; } public sealed class MinAcc : Accumulator { public double Value { get; set; } = double.PositiveInfinity; public ulong Count { get; set; } public override AccKind Kind => AccKind.Min; public override ulong SampleCount => Count; public double? Result => Count == 0 ? null : Value; public override void Add(double a, double b = 0) { Value = Math.Min(Value, a); Count++; } public override void Merge(Accumulator other) { var o = Cast(other); Value = Math.Min(Value, o.Value); Count += o.Count; } public override Accumulator Clone() => new MinAcc { Value = Value, Count = Count }; } public sealed class MaxAcc : Accumulator { public double Value { get; set; } = double.NegativeInfinity; public ulong Count { get; set; } public override AccKind Kind => AccKind.Max; public override ulong SampleCount => Count; public double? Result => Count == 0 ? null : Value; public override void Add(double a, double b = 0) { Value = Math.Max(Value, a); Count++; } public override void Merge(Accumulator other) { var o = Cast(other); Value = Math.Max(Value, o.Value); Count += o.Count; } public override Accumulator Clone() => new MaxAcc { Value = Value, Count = Count }; } /// 连续段长度(例如连续丢包):只保留次数、总和和最大值,合并是精确的。 public sealed class RunLengthAcc : Accumulator { public ulong Runs { get; set; } public double Total { get; set; } public double Max { get; set; } public override AccKind Kind => AccKind.RunLength; public override ulong SampleCount => Runs; public double? Mean => Runs == 0 ? null : Total / Runs; public override void Add(double a, double b = 0) { Runs++; Total += a; Max = Math.Max(Max, a); } public override void Merge(Accumulator other) { var o = Cast(other); Runs += o.Runs; Total += o.Total; Max = Math.Max(Max, o.Max); } public override Accumulator Clone() => new RunLengthAcc { Runs = Runs, Total = Total, Max = Max }; } /// 固定分辨率的时间序列(仅用于测试结果):Add(a = 序号, b = 值),合并为拼接。 public sealed class SeriesAcc : Accumulator { public List Points { get; } = []; public override AccKind Kind => AccKind.Series; public override ulong SampleCount => (ulong)Points.Count; public override void Add(double a, double b = 0) { int index = (int)a; while (Points.Count <= index) { Points.Add(double.NaN); } Points[index] = b; } public override void Merge(Accumulator other) => Points.AddRange(Cast(other).Points); public override Accumulator Clone() { var copy = new SeriesAcc(); copy.Points.AddRange(Points); return copy; } }