chronaxis.core.selection
源码基线:8904c2f744951857659abd40aaa3535d57faf53c
1"""声明已解码信号层的选择行,保留时间、原值、三值状态和来源身份。23输入为CalculationSeries和select请求,输出由各独立后端逐行迭代SelectionRow。4TRUE行是已知选中;UNKNOWN行用于保留无法决定的诊断,消费方必须检查selection。5FALSE行被过滤。只输出目标表达式依赖的真实时间组更新,不伪造裁剪或TTL样本。6本模块仅验证不可变协议,不计算筛选掩码、不读写文件、不改变旧读取或解码。7来源定位是绑定字段与时间组,不宣称保存旧日志的物理行号或逐消息求值序号。8输入仍为完整内存历史;逐行输出不意味着AnalysisRunner的内存预算已经实现。9"""1011# 冻结行协议避免消费者修改结果身份。12from dataclasses import dataclass13# 只共享静态模型,不包含筛选数值内核。14from .calculations import CalculationSeries, CalculationDefinition, calculation_conversion15from .models import SourceRef16from .numeric import ScalarValue17from .types import Truth, QualityFlag as Q18from .validation import require_text19from ..timebase.conversion import require_int64_ns202122@dataclass(frozen=True)23class SelectionRow:24 """保存一个真实表达式更新时间组的选择结果。2526 value保存输出dtype、枚举标签、有效性和质量;selection只允许TRUE或UNKNOWN。27 quality汇总原依赖、值及条件原因;binding_identity与source_refs定位当前输入。28 unit与当前行值一致;允许已核实的目标单位换算。29 所有字段不可变,没有跨任务状态;UNKNOWN不是已知选中,不能隐式转bool。30 """3132 time_ns: int # 原子更新时间组的精确纳秒时间。33 value: ScalarValue # 原typed值,未知不填正常零,枚举不输出内部整数编码。34 selection: Truth # TRUE选中或UNKNOWN诊断,不包含明确FALSE行。35 quality: Q # 原值、条件与依赖的完整质量原因。36 binding_identity: str # 当前表达式/选择条件的实际执行身份。37 source_refs: tuple[SourceRef, ...] # 当前所有依赖的原字段定位。38 unit: str # 当前分析值单位,和原typed值一起保留。3940 def __post_init__(self):41 """验证行类型、时间和当前来源身份,不执行筛选。4243 参数: 无;读取冻结字段。44 返回: None。45 异常: TypeError/ValueError/OverflowError为不合法协议。46 """47 # 保持精确int64时间,不接受float或bool冒充坐标。48 require_int64_ns(self.time_ns, 'selection.time_ns')49 # FALSE不属于输出,value即使无效也必须有完整typed质量。50 if not isinstance(self.value, ScalarValue) or self.selection not in (Truth.TRUE, Truth.UNKNOWN) or not isinstance(self.quality, Q):51 raise TypeError('选择行需typed值、TRUE/UNKNOWN及QualityFlag')52 # 身份不可为空;来源引用必须冻结且是完整字段模型。53 require_text(self.binding_identity, 'selection.binding_identity')54 require_text(self.unit, 'selection.unit')55 if not isinstance(self.source_refs, tuple) or any(not isinstance(ref, SourceRef) for ref in self.source_refs):56 raise TypeError('选择行来源需tuple SourceRef')57 # 输出不能隐藏原值原因,UNKNOWN必须保留至少一个质量诊断。58 if self.quality & self.value.quality != self.value.quality or self.selection is Truth.UNKNOWN and self.quality == Q.NONE:59 raise ValueError('选择行质量不能隐藏无效值或UNKNOWN原因')606162def require_selection(series, spec):63 """验证选择行流的输入及当前版本允许的单位换算。6465 参数: series为CalculationSeries,spec为operation=select的CalculationDefinition。66 返回: None。67 异常: TypeError为模型错误;ValueError为算子或不被当前版本支持的单位转换。68 """69 # 两个后端共享已校验协议,筛选算法仍独立。70 if not isinstance(series, CalculationSeries) or not isinstance(spec, CalculationDefinition):71 raise TypeError('选择行流需CalculationSeries和CalculationDefinition')72 # 此入口保留原值,转换需要先在分析表达式/绑定中明确完成。73 if spec.operation != 'select':74 raise ValueError('选择行流需select且保留输入单位')75 # v4允许已登记目标单位;类型也在产出第一行前检查,不能转换枚举编码。76 calculation_conversion(spec, series.unit).dtype(series.values[0].dtype)777879def partial_selection(value,quality,spec):80 """处理来自部分时间窗口的选中值,不更改真实行身份。8182 参数: value 为转换后的值,quality 为行的诊断,spec 决定 report/strict。83 返回: 值及行质量;strict 不提供正常值。异常: 值契约不合法时传播。84 """85 from dataclasses import replace86 if spec.partial_policy=='strict':87 value=ScalarValue(None,value.dtype,False,value.quality|Q.ESTIMATED,value.labels)88 elif value.valid:89 value=replace(value,partial=True)90 return value,quality|Q.ESTIMATED91