Chronaxis用户手册English
本页目录

chronaxis.configuration.resolution

源码基线:8904c2f744951857659abd40aaa3535d57faf53c

1"""按请求生成可独立加载的有效配置;只读配置,不读取业务输入。"""2from copy import deepcopy3from dataclasses import asdict4from pathlib import Path56from . import load_configuration, parse_configuration7from .file_tools import path_fields,validate_document,serialize_configuration,output_format,write_new_configuration8910def _duration(value):11    """纳秒用整数文本输出,不经浮点。参数: int。返回: 时长str。异常: 无。"""12    return str(value)+'ns'131415def _interval(choice):16    """输出合法间隔对象,不为actual/dbc补伪周期。参数: IntervalChoice。返回: dict。异常: 无。"""17    result={'mode':choice.mode}18    if choice.step_ns is not None:result['value']=_duration(choice.step_ns)19    if choice.driver is not None:result['driver']=choice.driver20    return result212223def _signal_defaults(raw,plan,*,normalized=False):24    """复用当前编译的信号事实,不为导出配置再次读取DBC。2526    参数: raw为独立配置副本;plan为当前计划;normalized表示转换字段已展开为内部平铺形式。27    返回: None;只补入可重复加载的原类型、单位及映射。28    异常: KeyError为内部配置与计划不匹配;不修改用户原文件。29    """30    from ..planning.signal_groups import declared_signals31    for signal,typed in zip(declared_signals(raw['signals']),plan.signals):32        # 保留换算前的声明类型,重载再推导同一提升;不输出额外input_dtype字段。33        signal.setdefault('dtype',(typed.definition.input_dtype or typed.definition.dtype).value)34        # 固定DBC原单位而不新增另一份转换记录;重载继续沿完整系数做一次转换。35        if plan.config_version == 4:36            destination = signal if normalized else signal.setdefault('conversion', {})37            destination.setdefault('input_unit', typed.definition.input_unit)38        signal.setdefault('unit',typed.definition.unit)39        if typed.definition.dtype.value=='enum':40            signal.setdefault('choices',[list(pair) for pair in typed.definition.choices])41            if typed.definition.code_dtype is not None:42                signal.setdefault('code_dtype', typed.definition.code_dtype.value)434445def _analysis_defaults(raw,plan):46    """显式化已编译模型的适用默认值。参数: 独立原生配置、计划。返回: None。异常: 内部契约错误。4748    只填写实际有含义且可重新加载的字段;不猜单位/方向/阈值,不写无关规则参数或DBC周期。49    """50    _signal_defaults(raw,plan,normalized=True)51    raw.setdefault('execution',{}).update(asdict(plan.execution))52    raw.setdefault('diagnostics',{}).update(asdict(plan.diagnostics))53    outputs=raw.setdefault('outputs',{})54    for key in ('tables','manifest','contributions'):outputs.setdefault(key,getattr(plan.outputs,key))55    for key in ('signal_csv','can_frames'):56        option=getattr(plan.outputs,key)57        if option is None:outputs.setdefault(key,None);continue58        item=outputs[key];item.setdefault('scope',option.scope)59        grid=option.spec.resampling if key=='signal_csv' else option.resampling60        if grid is not None:item['resampling'].setdefault('origin',_duration(grid.origin_ns))61        if key=='can_frames':62            item.setdefault('source_time_reference',option.spec.source_time_reference)63            item.setdefault('output_offset',_duration(option.spec.output_offset_ns))64    for index,(source,typed) in enumerate(zip(raw['sources'],plan.sources)):65        # --defaults展开有效来源默认值;普通resolve仍保留用户简写。66        from ..planning.input_defaults import source_defaults67        source.update(source_defaults(source,index,inferred_type=typed.source_type))68        source['time'].setdefault('offset','0s')69        if typed.source_type=='signal_csv':70            for key in ('encoding','delimiter'):source.setdefault(key,getattr(typed.config,key))71            source.setdefault('missing_values',list(typed.config.missing_values))72        elif typed.source_type in ('can_blf','can_asc','asc_or_blf','raw_can_csv'):73            source.setdefault('reader_options',{}).setdefault('decode_policy',typed.config.decode_policy)74            if typed.source_type=='can_blf':75                for key in ('decode_scope','input_check'):source.setdefault(key,getattr(typed.config,key))76    from ..planning.signal_groups import declared_signals77    for signal,typed in zip(declared_signals(raw['signals']),plan.signals):78        for key in ('scale','offset','sample_policy','same_time_policy'):79            signal.setdefault(key,getattr(typed.definition,key))80    for typed,expression in zip(plan.derived,plan.derived_expressions):81        raw['derived'][typed.definition.name].setdefault('unit',expression.root.unit)82    by_rule={item.rule_id:item for item in plan.rules}83    for rule in raw.get('rules',[]):84        typed=by_rule[rule['name']];kind=typed.rule_type85        if kind=='condition_duration':rule.setdefault('selection_origin',typed.selection_origin)86        elif kind in ('debounce','hysteresis'):87            rule.setdefault('enter_delay',_duration(typed.stability.enter_ns))88            rule.setdefault('exit_delay',_duration(typed.stability.exit_ns))89        elif kind=='start_end':90            rule.setdefault('emit_initial',typed.start_end.emit_initial)91            rule.setdefault('timeout',None if typed.start_end.timeout_ns is None else _duration(typed.start_end.timeout_ns))92        else:93            rule.setdefault('emit_initial',typed.emit_initial)94            if kind=='window_delta':95                rule.setdefault('retrigger',typed.retrigger)96                for key in ('monotonic','coverage_policy'):rule.setdefault(key,getattr(typed.window_delta,key))97    for window,typed in zip(raw.get('windows',[]),plan.windows):98        for key in ('pre','post'):window.setdefault(key,_duration(getattr(typed,key+'_ns')))99        window.setdefault('merge',typed.merge)100    for calculation,typed in zip(raw.get('calculations',[]),plan.calculations):101        # 对象数本身没有覆盖率门槛;补齐默认值后仍应是一份合法配置。102        keys = ('unit','partial_policy') if typed.count_target is not None else ('unit','partial_policy','minimum_coverage')103        for key in keys:calculation.setdefault(key,getattr(typed,key))104        if typed.method is not None:calculation.setdefault('method',typed.method)105        if typed.ddof is not None:calculation.setdefault('ddof',typed.ddof)106    # 只列出该归约实际使用的默认值;例如 count 不接受 field,也不需要输入单位转换。107    for aggregation, typed in zip(raw.get('aggregations', []), plan.aggregations):108        aggregation.setdefault('unit', typed.unit)109        aggregation.setdefault('partial_policy', typed.partial_policy)110        if typed.field is not None:111            aggregation.setdefault('field', typed.field)112        if typed.operation == 'variance':113            aggregation.setdefault('ddof', typed.ddof)114        if typed.operation != 'count' and typed.source_kind == 'calculation':115            aggregation.setdefault('include_partial', typed.include_partial)116    # 变换可相互引用,计划按依赖排序;用名字找对应项,不能和原列表按位置配对。117    transform_plans = {item.name: item for item in plan.transforms}118    for transform in raw.get('transforms', ()):119        typed = transform_plans[transform['name']]120        transform.setdefault('unit', typed.output.unit)121        if typed.step_ns is not None:122            transform.setdefault('partial_policy', typed.partial_policy)123            transform.setdefault('minimum_coverage', typed.minimum_coverage)124            # 网格默认以该时钟的零点为起点。绝对时钟不能填入相对时长。125            zero = '1970-01-01T00:00:00+00:00' if plan.bindings.timebase.time_type.value == 'absolute' else '0s'126            transform.setdefault('origin', zero)127    for sequence,typed in zip(raw.get('sequences',[]),plan.sequences):sequence.setdefault('max_pending',typed.max_pending)128    intervals=raw.setdefault('intervals',{})129    intervals.setdefault('default',_interval(plan.intervals.default))130    # 每个消费者的驱动已由静态依赖确定;DBC周期仍须运行时读取,绝不虚构数值。131    if plan.intervals.resolved:132        intervals['overrides']={name:_interval(choice) for name,choice in plan.intervals.resolved}133134135def resolved_configuration(source, *, defaults=False):136    """展开预设并固定路径。参数: 输入配置、是否填默认。返回: 原生配置。异常: 配置/IO错误。137138    原配置不写回;省略defaults时保原显式值。公开预设构建器只在独立副本执行。139    """140    from ..presets import expand_presets141    source=Path(source).absolute();document=load_configuration(source)142    from .imports import expand_document_imports143    document=expand_document_imports(document)144    name,plan=validate_document(document,source.parent,return_plan=True);raw=deepcopy(document.data)145    if name=='batch':146        if defaults:147            raw.setdefault('process_count',plan.process_count)148            for key in ('fail_fast','backend'):raw.setdefault(key,getattr(plan,key))149    else:150        from ..planning.config_groups import normalize_configuration151        raw=normalize_configuration(raw)152        if raw.get('presets'):153            expand_presets(raw,tuple(item.definition for item in plan.signals))154        raw.pop('presets',None)155        if defaults:_analysis_defaults(raw,plan)156    # 默认展开和预设内部片段统一还原成当前公共分组,不输出第二套配置语法。157    if name!='batch':158        from ..planning.config_groups import public_configuration159        raw=public_configuration(raw)160    # 使用词法绝对定位,保留..与符号链接的原路径解释;不stat/open业务文件。161    for owner,key in path_fields(raw,name):162        owner[key]=str((source.parent/Path(owner[key])).absolute())163    # 导出前复核全部有效字段;失败时还没有创建输出文件。164    validation=deepcopy(raw)165    if name!='batch':166        _signal_defaults(validation,plan)167    checked=parse_configuration(serialize_configuration(validation,'json'),format='json',source=str(source))168    validate_document(checked,source.parent)169    return raw170171172def resolve_configuration(source, *, output=None, format=None, defaults=False):173    """显示或导出有效配置。参数: 输入、可选新文件/格式/默认值。返回: 文本或Path。异常: 配置/IO错误。174175    无输出文件时默认YAML写给调用者;有文件时扩展名定格式,显式格式不一致则拒绝。176    """177    chosen='yaml' if output is None else output_format(Path(output))178    if format is not None:179        if output is not None and format!=chosen:raise ValueError('--format与输出扩展名不一致')180        chosen=format181    raw=resolved_configuration(source,defaults=defaults)182    text=serialize_configuration(raw,chosen)183    return text if output is None else write_new_configuration(output,text)184
Chronaxis 用户手册 · 离线阅读