Chronaxis用户手册English
本页目录

chronaxis.core.start_end

源码基线:8904c2f744951857659abd40aaa3535d57faf53c

1"""从开始条件到结束条件记录活动,并区分超时与缺观测。23输入是同一会话中按时间严格递增的两组ConditionTick。开始采用已知上升沿,4可选择首次已知TRUE也启动;活动期间只等待结束条件或时限,不排队重复开始。5输出沿用正长度Event,status=timeout表示在可判断的结束条件下等到时限。6UNKNOWN/真实失效与EOF继续区分interrupted/truncated,不能凭缺数据判超时。7这是有界有序状态转换,不读取来源、不保留全量历史,也不属于数值数组核。8"""910from dataclasses import dataclass11from .temporal import make_event12from .temporal_reference import require_batch, require_detector13from .types import QualityFlag as Q, Truth as T14from ..timebase.conversion import require_int64_ns151617@dataclass(frozen=True)18class StartEndSpec:19    """保存最长等待时长与首次启动策略;None明确表示不限时。"""2021    timeout_ns: int | None = None  # 从开始时刻算起;正纳秒或显式不限时。22    emit_initial: bool = True  # 首次已知开始条件为TRUE时允许启动。2324    def __post_init__(self):25        """拒绝零/负时限及假布尔。参数: 无。返回: None。异常: 类型/范围错误。"""26        if self.timeout_ns is not None:27            value = require_int64_ns(self.timeout_ns, 'timeout_ns')28            if value <= 0:29                raise ValueError('start_end时限必须为正或None')30            object.__setattr__(self, 'timeout_ns', value)31        if type(self.emit_initial) is not bool:32            raise TypeError('emit_initial必须为bool')333435class StartEndDetector:36    """为单个规则保存至多一段活动和开始条件前态;批次边界不重置。3738    活动中忽略重复开始;首次真实结束优先同刻时限。结束条件未知会终止已知39    活动,恢复后需要新的开始边沿;当前开始和结束同时成立不制造零长度活动。40    source_refs在构造时固定,输出可drain移交,未移交事件才留在实例中。41    """4243    def __init__(self, rule, start_ns, end_ns, spec, source_refs=(), *, backend='reference', record_clock=False):44        """建立独立的活动状态,规则沿用condition_duration=0输出模型。4546        参数: rule为TemporalRule;start_ns/end_ns为会话域;spec为StartEndSpec;47        source_refs为真实依赖引用;backend验证reference/numpy入口。48        返回: None;初始化实例状态。异常: 规则/范围/参数错误。状态算法没有数值后端分叉。49        """50        require_detector(rule, start_ns, end_ns, source_refs)51        if not isinstance(spec, StartEndSpec) or rule.rule_type != 'condition_duration' or rule.minimum_duration_ns != 0:52            raise ValueError('start_end需要StartEndSpec和duration=0输出规则')53        if backend not in ('reference', 'numpy'):54            raise ValueError('start_end后端需reference或numpy')55        if type(record_clock) is not bool:56            raise TypeError('record_clock需要bool')57        self.record_clock, self.remaining = record_clock, None58        self.rule, self.spec, self.source_refs = rule, spec, source_refs59        self.start_ns, self.end_ns = int(start_ns), int(end_ns)60        self.active_start, self.deadline = None, None61        self.previous_start, self.seen_start = T.UNKNOWN, False62        self.diagnostic = Q.NONE63        self.last_time, self.ended, self.finished = None, False, False64        self.events = []6566    @property67    def next_deadline_ns(self):68        """提供域内时限供时间线推进。参数: 无。返回: int或None。异常: 无。"""69        return self.deadline if not self.record_clock and self.deadline is not None and self.deadline <= self.end_ns else None7071    def _close(self, time_ns, status, quality=Q.NONE):72        """保存有正长度的活动后清理计时,绝不延长零长度点。7374        参数: time_ns结束纳秒;status/quality表示实际结束原因。返回: None。75        异常: Event结构或分配错误;失败后调用方丢弃实例。76        """77        if self.active_start is not None and time_ns > self.active_start:78            self.events.append(make_event(self.rule, self.active_start, self.active_start,79                time_ns, status, quality, self.diagnostic, self.source_refs))80        self.active_start, self.deadline = None, None81        self.remaining = None82        self.diagnostic = Q.NONE8384    @staticmethod85    def _unknown_status(end_condition):86        """按真实失效与观测结束区分原因。参数: end_condition证据。返回: str。异常: 无。"""87        return ('truncated' if (end_condition.terminal or end_condition.observation_end) and not end_condition.actual_unknown88                else 'interrupted')8990    def _advance(self, start_condition, end_condition, advance=None):91        """推进一个完整时间组,先补精确时限,再处理实际结束和新开始。9293        参数: 同刻start_condition/end_condition条件证据。返回: None。异常: 输出构造错误。94        边界: 跨过时限前未出现其它组,表示此前连续结束条件已知不成立;95        同刻未知则不能证明截止点没有end_condition,保留未知/EOF原因。96        """97        time_ns = int(start_condition.time_ns)98        if self.record_clock and self.remaining is not None:99            self.remaining = max(0, self.remaining - advance)100        if self.deadline is not None and self.deadline < time_ns:101            self._close(self.deadline, 'timeout')102        diagnostic = (start_condition.diagnostic_quality | end_condition.diagnostic_quality | start_condition.quality103                      | end_condition.quality | start_condition.point_quality | end_condition.point_quality)104        was_active = self.active_start is not None105        if was_active:106            self.diagnostic |= diagnostic107            # 只有结束条件决定已启动活动;开始信号后来失效不抹去已确认的开始。108            if end_condition.point_state is T.TRUE:109                self._close(time_ns, 'closed')110            elif end_condition.point_state is T.UNKNOWN:111                self._close(time_ns, self._unknown_status(end_condition), end_condition.point_quality)112            elif end_condition.state is T.TRUE and not end_condition.terminal:113                # 右侧连续条件已经成立时,结束边界就在当前时刻,不等下条采样。114                self._close(time_ns, 'closed')115            elif (self.remaining == 0 if self.record_clock else self.deadline is not None and self.deadline == time_ns):116                self._close(time_ns, 'timeout')117            elif end_condition.terminal or end_condition.observation_end:118                self._close(time_ns, 'truncated', end_condition.quality | Q.MISSING)119            elif end_condition.state is T.UNKNOWN:120                self._close(time_ns, 'interrupted', end_condition.quality)121        # 不在结束同刻抢占新的开始,也不把持续TRUE或UNKNOWN恢复当新上升沿。122        # 首次策略也适用于常量TRUE:有实际观测域即可,不要求常量收到信号更新。123        initial = not self.seen_start and self.spec.emit_initial124        edge = self.previous_start is T.FALSE and start_condition.actual_update125        begins = start_condition.point_state is T.TRUE and (initial or edge)126        if (not was_active and self.active_start is None and begins and not start_condition.terminal127                and end_condition.point_state is T.FALSE and end_condition.state is T.FALSE and not end_condition.observation_end):128            self.active_start = time_ns129            # Python整数避免跨int64上界回绕;域外时限不会交给时间推进器。130            self.deadline = None if self.record_clock or self.spec.timeout_ns is None else time_ns + self.spec.timeout_ns131            self.remaining = self.spec.timeout_ns132            self.diagnostic = diagnostic133        if start_condition.point_state is not T.UNKNOWN:134            self.seen_start = True135        self.previous_start = start_condition.state136137    def push(self, ticks, end_ticks, *, advances=None):138        """消费同域、同刻、完整的开始/结束条件批。139140        参数: ticks/end_ticks为ConditionTick tuple,可为空;advances仅record_clock模式141            提供本组周期增量,不改变开始/结束的实际时间。返回: 本批新增Event tuple;142        同时保留到drain。异常: 协议/排序/结束后推进错误;输入失败不排序或补齐。143        """144        if self.finished:145            raise ValueError('start_end检测器已finish')146        require_batch(ticks, self.start_ns, self.end_ns, self.last_time, self.ended)147        from .temporal_clock import require_clock_advances148        require_clock_advances(ticks, advances, self.record_clock, self.last_time)149        require_batch(end_ticks, self.start_ns, self.end_ns, self.last_time, self.ended)150        if len(ticks) != len(end_ticks) or any(a.time_ns != b.time_ns for a, b in zip(ticks, end_ticks)):151            raise ValueError('start_end两个条件必须在相同完整时间组求值')152        first = len(self.events)153        for index, (start, end) in enumerate(zip(ticks, end_ticks)):154            self._advance(start, end, None if advances is None else advances[index])155            self.last_time, self.ended = int(start.time_ns), start.terminal156        return tuple(self.events[first:])157158    def drain_events(self):159        """转交当前输出,不清活动状态。参数: 无。返回: Event tuple。异常: 分配错误。"""160        result = tuple(self.events)161        self.events.clear()162        return result163164    def finish(self):165        """仅在已收到真实末组后封闭。参数: 无。返回: 未移交Event tuple。异常: 协议错误。"""166        if self.finished or not self.ended:167            raise ValueError('start_end需要唯一finish且已有真实terminal')168        self.finished = True169        return tuple(self.events)170
Chronaxis 用户手册 · 离线阅读