Replace Probe with Agent across node runtime, Fleet contracts, backend state, API and storage. Report incoming sessions and outgoing link health through each Agent uplink, and retain directed link controls and retry. Update protocol fixtures and end-to-end coverage for the unified model; accept requested cancellation during the Fleet timeout test cleanup.
129 lines
4.7 KiB
C#
129 lines
4.7 KiB
C#
namespace MoShiro.Protocol;
|
|
|
|
[Flags]
|
|
public enum Scope
|
|
{
|
|
Line = 1,
|
|
Target = 2,
|
|
}
|
|
|
|
public enum ColumnType
|
|
{
|
|
U8,
|
|
U16,
|
|
U32,
|
|
U64,
|
|
I32,
|
|
I64,
|
|
}
|
|
|
|
public enum ColumnEnc
|
|
{
|
|
Plain,
|
|
Delta,
|
|
}
|
|
|
|
/// <summary>时间列的时钟归属:拨号方、监听方各自的单调时钟(微秒),或所在一侧的本地时钟。</summary>
|
|
public enum ColumnRole
|
|
{
|
|
None,
|
|
TsDialer,
|
|
TsListener,
|
|
TsLocal,
|
|
}
|
|
|
|
public sealed record ColumnSpec(string Name, ColumnType Type, ColumnEnc Enc = ColumnEnc.Plain, bool Nullable = false, ColumnRole Role = ColumnRole.None);
|
|
|
|
/// <summary>数据面上的一侧:拨号方发起连接,监听方接受连接。</summary>
|
|
public enum Side
|
|
{
|
|
Dialer,
|
|
Listener,
|
|
}
|
|
|
|
/// <summary>
|
|
/// 一个记录流(一张表)。<see cref="Driver"/> 是行的主导方:行由它发出的包或事件决定,另一侧的列按 <see cref="Key"/>(例如 <c>seq</c>)补充,
|
|
/// 缺失就是缺失(回显缺 t2/t3 表示上行丢失,单向数据流缺 t_recv 表示丢失)。没有 <see cref="Key"/> 的流只有主导方一侧的列,原样透传。
|
|
/// 每一列属于哪一侧由它的时钟决定(<c>TsDialer</c> 属于拨号方,<c>TsListener</c> 属于监听方),其他列属于主导方。
|
|
/// <see cref="Union"/> 流是例外:两侧各自记录完整的行(行里有一列标明是哪一侧),合并时直接拼接,不连接。
|
|
/// 这一条声明同时决定了两端各记录什么、监听方怎么合并,新增一种记录流不需要写合并代码。
|
|
/// </summary>
|
|
public sealed record StreamSpec(string Name, IReadOnlyList<ColumnSpec> Columns, Side Driver = Side.Dialer, string? Key = null, bool Union = false)
|
|
{
|
|
public Side OwnerOf(ColumnSpec column) => column.Role switch
|
|
{
|
|
ColumnRole.TsDialer => Side.Dialer,
|
|
ColumnRole.TsListener => Side.Listener,
|
|
_ => Driver,
|
|
};
|
|
|
|
/// <summary>
|
|
/// 该侧记录的列(不含连接键:键两侧都有,由行的编号表示)。<see cref="Union"/> 流两侧各自记录完整的行(例如各自套接字的内核采样),
|
|
/// 所以每一侧拥有所有列。
|
|
/// </summary>
|
|
public IReadOnlyList<ColumnSpec> ColumnsOf(Side side) =>
|
|
[.. Columns.Where(c => c.Name != Key && (Union || OwnerOf(c) == side))];
|
|
}
|
|
|
|
/// <summary>积木组合(只描述,不含代码;节点按这个描述组装执行图)。</summary>
|
|
public sealed record BlockSet(string Transport, string Pacer, string Payload, string Behavior, IReadOnlyList<string> Samplers);
|
|
|
|
/// <summary>
|
|
/// 采集方法声明:积木组合 + 参数 + 记录的列 + 带宽预算。
|
|
/// 这一条声明同时被拨号方和监听方(组装执行)、Backend(校验与目录)、Analysis(按列名读数据)使用。
|
|
/// </summary>
|
|
public sealed record KindSpec(
|
|
string Id,
|
|
string Title,
|
|
string Description,
|
|
Scope Scopes,
|
|
BlockSet Blocks,
|
|
IReadOnlyList<ParamSpec> Params,
|
|
IReadOnlyList<StreamSpec> Streams,
|
|
Func<ParamValues, long> BudgetBitsPerSecond)
|
|
{
|
|
public StreamSpec? FindStream(string name)
|
|
{
|
|
foreach (StreamSpec stream in Streams)
|
|
{
|
|
if (stream.Name == name)
|
|
{
|
|
return stream;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public static class Col
|
|
{
|
|
public static ColumnSpec U8(string name, bool nullable = false) => new(name, ColumnType.U8, ColumnEnc.Plain, nullable);
|
|
|
|
public static ColumnSpec U16(string name, bool nullable = false) => new(name, ColumnType.U16, ColumnEnc.Plain, nullable);
|
|
|
|
public static ColumnSpec U32(string name, bool nullable = false) => new(name, ColumnType.U32, ColumnEnc.Plain, nullable);
|
|
|
|
public static ColumnSpec U64(string name, ColumnEnc enc = ColumnEnc.Plain) => new(name, ColumnType.U64, enc);
|
|
|
|
/// <summary>单调递增的序号:差分编码。</summary>
|
|
public static ColumnSpec Seq(string name) => new(name, ColumnType.U32, ColumnEnc.Delta);
|
|
|
|
public static ColumnSpec TsDialer(string name, bool nullable = false) => new(name, ColumnType.I64, ColumnEnc.Delta, nullable, ColumnRole.TsDialer);
|
|
|
|
public static ColumnSpec TsListener(string name, bool nullable = false) => new(name, ColumnType.I64, ColumnEnc.Delta, nullable, ColumnRole.TsListener);
|
|
|
|
public static ColumnSpec TsLocal(string name) => new(name, ColumnType.I64, ColumnEnc.Delta, false, ColumnRole.TsLocal);
|
|
}
|
|
|
|
/// <summary>带宽预算的估算函数(bit/s)。</summary>
|
|
public static class Bandwidth
|
|
{
|
|
private const int UdpOverheadBytes = 28;
|
|
private const int TcpOverheadBytes = 52;
|
|
|
|
public static long UdpEcho(long rateHz, long sizeBytes) => rateHz * ((sizeBytes + UdpOverheadBytes) * 8) * 2;
|
|
|
|
public static long TcpEcho(long rateHz, long sizeBytes) => rateHz * ((sizeBytes + TcpOverheadBytes) * 8) * 2;
|
|
}
|