90 lines
2.8 KiB
C#
90 lines
2.8 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace AnotherReplayReader.Utils
|
||
{
|
||
internal static class AIAnalyzeUI
|
||
{
|
||
public record AIAnalyzeProgressData(AIAnalyze.AIChunk Delta, DateTimeOffset? TimeStamp, bool IsExtra);
|
||
|
||
public class EmaSpeed
|
||
{
|
||
const double Tau = 5;
|
||
|
||
private DateTimeOffset lastEventTime = DateTimeOffset.UtcNow;
|
||
private DateTimeOffset timeSinceLastSpeedMeasure = DateTimeOffset.UtcNow;
|
||
private int bufferedCharactersSinceLastSpeedMeasure = 0;
|
||
private double emaSpeed = double.NaN;
|
||
|
||
public void ProcessEvent(AIAnalyzeProgressData data)
|
||
{
|
||
if (data.IsExtra)
|
||
{
|
||
return;
|
||
}
|
||
if (data.TimeStamp is { } timestamp)
|
||
{
|
||
lastEventTime = timestamp;
|
||
}
|
||
bufferedCharactersSinceLastSpeedMeasure += data.Delta.Text.Length;
|
||
}
|
||
|
||
public double GetDisplaySpeed(DateTimeOffset now)
|
||
{
|
||
// =========================
|
||
// 2. 计算 instant speed(真实时间)
|
||
// =========================
|
||
double instant = 0;
|
||
double dt = (now - timeSinceLastSpeedMeasure).TotalSeconds;
|
||
|
||
if (bufferedCharactersSinceLastSpeedMeasure > 0 && dt > 0.05)
|
||
{
|
||
instant = bufferedCharactersSinceLastSpeedMeasure / dt;
|
||
|
||
// reset window
|
||
bufferedCharactersSinceLastSpeedMeasure = 0;
|
||
timeSinceLastSpeedMeasure = now;
|
||
}
|
||
|
||
double alpha = 1 - Math.Exp(-dt / Tau);
|
||
|
||
if (double.IsNaN(emaSpeed))
|
||
{
|
||
emaSpeed = instant;
|
||
}
|
||
else
|
||
{
|
||
emaSpeed += alpha * (instant - emaSpeed);
|
||
}
|
||
double idle = (now - lastEventTime).TotalSeconds;
|
||
double display = emaSpeed;
|
||
|
||
if (idle > 0.5)
|
||
{
|
||
double decay = Math.Exp(-(idle - 0.5) / Tau);
|
||
display *= decay;
|
||
}
|
||
|
||
return display;
|
||
}
|
||
}
|
||
|
||
public static Action<AIAnalyze.AIChunk> BuildAIChunkReader(Action<AIAnalyzeProgressData> newContent)
|
||
{
|
||
void OnAIChunk(AIAnalyze.AIChunk c)
|
||
{
|
||
if (c.Type == AIAnalyze.AIChunkType.Json)
|
||
{
|
||
return;
|
||
}
|
||
newContent(new(Delta: c, TimeStamp: DateTimeOffset.UtcNow, IsExtra: false));
|
||
}
|
||
|
||
return OnAIChunk;
|
||
}
|
||
}
|
||
}
|