chore: initial project import

This commit is contained in:
justme committed 2026-10-04 21:46:39 +08:00
commit 3fc4e5ca53
347 files changed
+56172

No files matched your search

@@ -0,0 +1,241 @@
namespace Monitor.Backend.Analysis;
public enum AccKind : byte
{
Counter = 1,
Ratio = 2,
Sum = 3,
Min = 4,
Max = 5,
Histogram = 6,
RunLength = 7,
Series = 8,
}
/// <summary>
/// 所有指标的中间状态只能是下面几种累加器之一。合并规则只写一次,
/// 存储、降采样、按时间范围聚合都通用处理。
/// </summary>
public abstract class Accumulator
{
public abstract AccKind Kind { get; }
/// <summary>加入一个观测值。含义由累加器决定:Counter 加 <paramref name="a"/>,Ratio 的 (a, b) 是 (分子, 分母),Histogram 记录 a。</summary>
public abstract void Add(double a, double b = 0);
/// <summary>合并同类型的另一个累加器(精确合并,不是平均)。</summary>
public abstract void Merge(Accumulator other);
public abstract Accumulator Clone();
/// <summary>样本数,用于置信说明。</summary>
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<T>(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<CounterAcc>(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;
/// <summary>百分比(0–100);没有样本时为 null。</summary>
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<RatioAcc>(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<SumAcc>(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<MinAcc>(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<MaxAcc>(other);
Value = Math.Max(Value, o.Value);
Count += o.Count;
}
public override Accumulator Clone() => new MaxAcc { Value = Value, Count = Count };
}
/// <summary>连续段长度(例如连续丢包):只保留次数、总和和最大值,合并是精确的。</summary>
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<RunLengthAcc>(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 };
}
/// <summary>固定分辨率的时间序列(仅用于测试结果):Add(a = 序号, b = 值),合并为拼接。</summary>
public sealed class SeriesAcc : Accumulator
{
public List<double> 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<SeriesAcc>(other).Points);
public override Accumulator Clone()
{
var copy = new SeriesAcc();
copy.Points.AddRange(Points);
return copy;
}
}