// rx.mbt — FEC 接收恢复窗(K8 刀3)
//
// 语义(具名效应):收帧校验 → 按 group_id 攒窗 → 数据片即投(不攒,
// 零排队延时)→ 组齐 k 片当场恢复补投缺失数据片 → 组销账弃迟到片。
// 窗深定常环形,超深逐最老(未 done 计组亡,ARQ 兜底)。
///|
/// 组槽位(包私有):逐组自描述 k/m——发侧逐组拍板,收侧零协商。
struct RxGroup {
k : Int32
m : Int32
slots : Array[FixedArray[Byte]?]
mut filled : Int32
mut done : Bool
}
///|
/// FEC 接收机:纯形零 IO,帧由宿主搬运。
pub struct FecRx {
depth : Int32
codec_factory : (Int32, Int32) -> &MdsCodec
groups : Map[Int32, RxGroup]
order : Array[Int32]
mut frames_in : Int32
mut delivered : Int32
mut recovered_shards : Int32
mut recovered_groups : Int32
mut dropped_malformed : Int32
mut dropped_late : Int32
mut groups_lost : Int32
}
///|
/// RS 缺省适配器版。
pub fn FecRx::new(depth : Int32) -> FecRx {
FecRx::with_codec(fn(k : Int32, m : Int32) { RsCodec::new(k, m) }, depth)
}
///|
/// 端口注入版(编解码器工厂逐组调用,k/m 由帧头自描述)。
pub fn FecRx::with_codec(
codec_factory : (Int32, Int32) -> &MdsCodec,
depth : Int32,
) -> FecRx {
{
depth,
codec_factory,
groups: Map([]),
order: [],
frames_in: 0,
delivered: 0,
recovered_shards: 0,
recovered_groups: 0,
dropped_malformed: 0,
dropped_late: 0,
groups_lost: 0,
}
}
///|
/// 接收快照(观测面,R6;信号聚合器将来读此喂策略端口)。
pub struct FecRxStats {
frames_in : Int32
delivered : Int32
recovered_shards : Int32
recovered_groups : Int32
dropped_malformed : Int32
dropped_late : Int32
groups_lost : Int32
}
///|
pub fn FecRx::stats(self : FecRx) -> FecRxStats {
{
frames_in: self.frames_in,
delivered: self.delivered,
recovered_shards: self.recovered_shards,
recovered_groups: self.recovered_groups,
dropped_malformed: self.dropped_malformed,
dropped_late: self.dropped_late,
groups_lost: self.groups_lost,
}
}
///|
/// 取组或建组:不在窗 → 超深逐最老(未 done 计组亡)→ 建新组入窗。
fn FecRx::group_of(self : FecRx, header : FecHeader) -> RxGroup {
match self.groups.get(header.group_id) {
Some(existing) => return existing
None => ()
}
while self.order.length() >= self.depth {
let oldest = self.order[0]
let _ = self.order.remove(0)
match self.groups.get(oldest) {
Some(old_group) => if !old_group.done { self.groups_lost += 1 }
None => ()
}
let _ = self.groups.remove(oldest)
}
let slots : Array[FixedArray[Byte]?] = []
for _i in 0.. Array[FixedArray[Byte]] {
self.frames_in += 1
let header : FecHeader = match fec_read_header(frame) {
Some(h) => h
None => {
self.dropped_malformed += 1
return []
}
}
let group = self.group_of(header)
if group.done || group.slots[header.shard_index] is Some(_) {
self.dropped_late += 1
return []
}
// 数据帧:头 payload_len 与体内 body_len 镜像核(漂移 = 腐坏,弃)
if header.shard_index < header.k && header.payload_len != fec_body_len(frame) {
self.dropped_malformed += 1
return []
}
group.slots[header.shard_index] = Some(frame)
group.filled += 1
let out : Array[FixedArray[Byte]] = []
if header.shard_index < header.k {
out.push(fec_payload_of(frame))
self.delivered += 1
}
// 组齐 k 片当场恢复:缺失数据片补投(冗余片由数据片重编码,不投)
if group.filled >= group.k {
let codec = (self.codec_factory)(group.k, group.m)
match codec.recover(group.slots) {
Some(fullset) => {
group.done = true
let mut missed = 0
for i in 0.. 0 {
self.recovered_shards += missed
self.recovered_groups += 1
self.delivered += missed
}
}
None => {
// MDS 阵列不该不可逆——诚实计数,组废(ARQ 兜底)
group.done = true
self.groups_lost += 1
}
}
}
out
}