chronaxis.planning.sequences
源码基线:8904c2f744951857659abd40aaa3535d57faf53c
1"""检查请求/响应点事件配对的公开声明,不读取信号或执行配对。23输入是JSON节点或冻结SequencePlan;输出为已验证的计划和联合依赖。4只引用同计划的点规则,拒绝活动、跨时钟、重名和非法延迟。观测条件直接5采用响应规则的条件,用户不需要重复配置另一份观测表达式。6"""78from dataclasses import dataclass9from .diagnostic_collection import usable_node, collect_node_error10from .checks import PlanValidationError11from .checks import array_at, duration_at, model_at, number_at, object_at, text_at12from .nodes import register_id13from ..core.validation import require_identifier14from ..core.types import TimeType15from ..timebase.conversion import require_int64_ns161718@dataclass(frozen=True)19class SequencePlan:20 """保存一个FIFO配对声明;延迟包含两端,max_pending限制同时等待的请求数。"""2122 sequence_id: str23 start_rule: str24 response_rule: str25 min_delay_ns: int26 max_delay_ns: int27 max_pending: int = 655362829 def __post_init__(self):30 """检查独立字段,不读取其他规则。3132 参数: 无。返回: None。异常: TypeError/ValueError为名称或整数边界错误。33 """34 for name in (self.sequence_id, self.start_rule, self.response_rule):35 require_identifier(name, 'sequence节点名称')36 if any(type(value) is not int for value in (self.min_delay_ns, self.max_delay_ns, self.max_pending)):37 raise TypeError('sequence延迟和容量须为原生整数')38 if not 0 <= self.min_delay_ns <= self.max_delay_ns or self.max_pending < 1:39 raise ValueError('sequence需要0<=min_delay<=max_delay及正max_pending')40 require_int64_ns(self.min_delay_ns, 'min_delay_ns')41 require_int64_ns(self.max_delay_ns, 'max_delay_ns')424344def sequence_dependencies(item, rules, bindings):45 """检查两端引用和时钟,返回两规则实际原信号依赖。4647 参数: item为SequencePlan,rules为RulePlan元组,bindings为声明或运行绑定。48 返回: 去重且稳定排序的别名tuple。异常: TypeError/ValueError为不支持的引用或时钟。49 """50 if type(item) is not SequencePlan:51 raise TypeError('sequences需要SequencePlan')52 catalog = {rule.rule_id: rule for rule in rules}53 dependencies = set()54 for name in (item.start_rule, item.response_rule):55 rule = catalog.get(name)56 if rule is None or rule.rule_type not in ('rising_edge', 'falling_edge', 'start_only'):57 raise ValueError('sequence只引用本计划rising/falling/start_only点规则:' + name)58 dependencies.update(rule.condition.dependencies)59 result = tuple(sorted(dependencies))60 bindings.require_synchronized(result)61 # 常量条件没有信号依赖,但使用dataset观测轴;其隐含时钟也必须与另一端相同。62 if any(not catalog[name].condition.dependencies for name in (item.start_rule, item.response_rule)):63 declared = getattr(bindings, 'declarations', bindings)64 evidenced = {proof.source for proof in declared.synchronization}65 sources = {source.name: source for source in declared.sources}66 for name in result:67 source = sources[bindings.resolve(name).source]68 mapped = (source.timebase == declared.timebase or source.name in evidenced69 or source.timebase.time_type is TimeType.ABSOLUTE and declared.timebase.time_type is TimeType.ABSOLUTE)70 if not mapped:71 raise ValueError('常量规则与响应/请求信号没有共同dataset时钟:' + source.name)72 return result737475def parse_sequences(value, rules, bindings, ids):76 """解析独立sequences数组,节点ID与规则/窗口/指标共用名称空间。7778 参数: value为JSON数组,rules/bindings为已验证声明,ids为本计划名称集合。79 返回: SequencePlan tuple。异常: PlanValidationError保留非法字段或引用的位置。80 """81 result = []82 for index, raw in enumerate(array_at(value, '$.sequences')):83 path = f'$.sequences[{index}]'84 if not usable_node(path, raw):85 continue86 try:87 path = f'$.sequences[{index}]'88 raw = object_at(raw, path, ('name', 'start', 'response', 'max_delay'), ('min_delay', 'max_pending'))89 item = model_at(SequencePlan, path, register_id(raw['name'], path + '.name', ids),90 text_at(raw['start'], path + '.start', identifier=True),91 text_at(raw['response'], path + '.response', identifier=True),92 duration_at(raw.get('min_delay', '0s'), path + '.min_delay'),93 duration_at(raw['max_delay'], path + '.max_delay'),94 number_at(raw.get('max_pending', 65536), path + '.max_pending', integer=True, minimum=1))95 model_at(sequence_dependencies, path, item, rules, bindings)96 result.append(item)97 except PlanValidationError as error:98 collect_node_error(error, path, raw)99 return tuple(result)100