Chronaxis用户手册English
本页目录

chronaxis.execution.record_exports

源码基线:8904c2f744951857659abd40aaa3535d57faf53c

1"""只为显式导出接同次读取的帧或样本;复用既有writer和结果发布。23会话范围在真实EOF后确定;片段由窗口消费者直接交付,不复制片段表。4不再次读原日志或解码,默认精简导出不产生逐帧旁表。5"""6from ..core.models import Interval7from .record_signal_export import RecordSignalExport8from .legacy_frame_samples import LegacyFrameSamples9from ..results.frame_rebuild import FrameRebuildRead10from ..results.artifact_exports import ArtifactExportStore11from ..results.frame_exports import export_can_frames12from ..results.errors import ResultClosedError13from ..results.budget import _reserve_owned_ram,_release_owned_ram,_cleanup_owner141516class RecordExports:17    """拥有导出索引、在线信号写出与当前帧游标;原帧仓由来源游标所有。"""18    def __init__(self,plan,budget,path,cursors):19        """建立仅显式请求的索引。参数: 计划/预算/新路径/借用来源。返回: owner。异常: 原构造错。"""20        self._plan,self._budget,self._cursors=plan,budget,cursors21        self._held_cost=self._fixed_cost=022        self._exports=self._reader=self._export_owner=self._signal=self._rebuild=None23        self._closed=self._failed=self._finished=False24        self._option=plan.outputs.can_frames25        try:26            _reserve_owned_ram(self,65536,'_fixed_cost')27            self._require_clock()28            try:29                self._exports=ArtifactExportStore(budget,path,plan.dataset.session_id,plan.metadata.get('device_label','pending'),integrity=plan.diagnostics.integrity)30            except BaseException as error:31                self._exports=getattr(error,'artifact_export_store_cleanup_owner',None);raise32            if plan.outputs.signal_csv is not None:33                try:34                    self._signal=RecordSignalExport(plan,budget,cursors,self._exports,path)35                except BaseException as error:36                    self._signal=getattr(error,'record_signal_export_cleanup_owner',None);raise37        except BaseException as error:38            _cleanup_owner(self,error,'record_exports_cleanup_owner','记录流原帧导出');raise3940    def _require_clock(self):41        """多来源片段须有可比时钟;单来源不强求映射。参数: 无。返回: None。异常: 缺同步依据。"""42        plan=self._plan43        options=[]44        if self._option is not None:45            options.append((self._option,{self._option.source}))46        if plan.outputs.signal_csv is not None:47            option=plan.outputs.signal_csv48            options.append((option,{plan.bindings.resolve(n).source for n in option.spec.signals}))49        for option,sources in options:50            if option.scope!='segments':continue51            for window in (w for w in plan.windows if option.window is None or w.window_id==option.window):52                checked_sources=set(sources)53                rule=next(r for r in plan.rules if r.rule_id==window.event)54                names=set(rule.dependencies)55                choice=dict(plan.intervals.resolved)[window.window_id]56                if choice.driver:names.add(choice.driver)57                checked_sources.update(plan.bindings.resolve(n).source for n in names)58                if not names:checked_sources.update(s.name for s in plan.sources)59                evidenced={item.source for item in plan.bindings.synchronization}60                keys=set()61                for source in plan.bindings.sources:62                    if source.name not in checked_sources:continue63                    clock=source.timebase64                    if source.name in evidenced or clock==plan.dataset.timebase or clock.time_type.value=='absolute' and plan.dataset.timebase.time_type.value=='absolute':65                        keys.add(('dataset',))66                    else:67                        keys.add(('utc',) if clock.time_type.value=='absolute' else ('relative',clock.timebase_id,clock.origin))68                if len(keys)>1:raise ValueError('导出与片段缺少共同时间基准')6970    def _export(self,actual,segment_id=None):71        """借同次封口原帧写一份所需输出。参数: 实际范围/父片段。返回: None。异常: 原消费/发布错。"""72        option=self._option73        cursor=next(c for c in self._cursors if c.source_name==option.source)74        store=cursor._raw_store75        if store is None or store.closed or store.state!='sealed':76            raise ValueError('原帧导出缺少同次封口输入')77        interval=actual or Interval(self._plan.dataset.domain.start_ns,self._plan.dataset.domain.start_ns)78        try:79            if option.resampling is None:80                self._reader=store.iter_frames()81            else:82                context=cursor._dbc_context83                if context is None:raise ValueError('报文重采样缺同次编码定义')84                if cursor.legacy_frame_storage:85                    source=next(s for s in self._plan.sources if s.name==option.source)86                    try:87                        self._reader=LegacyFrameSamples(store,cursor._message_store,context,self._budget,88                            read_identity=cursor._read_identity,session_id=self._plan.dataset.session_id,channel=source.config.channel,trace=False)89                    except BaseException as error:90                        self._reader=getattr(error,'legacy_frame_samples_cleanup_owner',None);raise91                else:92                    self._reader=store.iter_samples()93                try:94                    self._rebuild=FrameRebuildRead(self._reader,option.resampling,interval,context.pairs(),self._budget,95                        self._exports.path/f'rebuild-{self._exports.count:08d}',backend=self._plan.execution.backend,96                        periods=context.periods,integrity=self._plan.diagnostics.integrity,trace=False)97                except BaseException as error:98                    self._rebuild=getattr(error,'frame_rebuild_cleanup_owner',None);raise99                self._reader=None100            files=[]101            export_can_frames(self._rebuild or self._reader,option.spec,interval,self._exports.next_path(),self._budget,102                source=option.source,device_label=self._plan.metadata.get('device_label','pending'),compact=True,103                integrity=self._plan.diagnostics.integrity,file_consumer=files.extend)104            if self._rebuild is not None:self._rebuild.close();self._rebuild=None105            if self._reader is not None:self._reader.close();self._reader=None106            self._exports.append('can_frames',option.scope,actual,segment_id=segment_id,completed_files=files)107        except BaseException as error:108            self._export_owner=getattr(error,'export_cleanup_owner',None)109            self._failed=True;error.record_exports_cleanup_owner=self;raise110111    def session(self):112        """EOF后裁实际会话范围,空输入保持空输出。参数: 无。返回: None。异常: 原导出错。"""113        if self._signal is not None:self._signal.finish()114        if self._option is None or self._option.scope!='session': return115        cursor=next(c for c in self._cursors if c.source_name==self._option.source)116        actual=None117        if cursor.observed_start is not None and cursor.observed_end is not None:118            domain=self._plan.dataset.domain119            start,end=max(domain.start_ns,cursor.observed_start),min(domain.end_ns,cursor.observed_end)120            if start<=end: actual=Interval(start,end)121        self._export(actual)122123    def segment(self,segment):124        """直接接最终片段,不保存第二份片段历史。参数: CompactSegment。返回: None。异常: 原导出错。"""125        if self._signal is not None:self._signal.segment(segment)126        if self._option is not None and self._option.scope=='segments' and (self._option.window is None or segment.window_id==self._option.window):127            self._export(segment.actual,segment.segment_id)128129    def seal(self):130        """全部请求完成后封索引。参数: 无。返回: None。异常: 原封口错。"""131        self._exports.seal();self._finished=True132133    def take_exports(self,receiver):134        """唯一移交已完成结果;失败前责任仍可达。参数: 同预算父owner。返回: None。异常: 状态错。"""135        if not self._finished or self._closed or receiver._budget is not self._budget or receiver._artifact_exports is not None:136            raise ResultClosedError('原帧输出尚不可移交')137        owner=self._exports138        try:139            receiver._artifact_exports=owner140        finally:141            if receiver._artifact_exports is owner: self._exports=None142143    @property144    def closed(self):145        """返回实际关闭状态。参数: 无。返回: bool。异常: 无。"""146        return self._closed147148    def close(self):149        """先关当前输出及索引,再释放自有状态;不关闭借用原帧。参数: 无。返回: None。异常: 原关闭错。"""150        if self._closed:return151        for slot in ('_signal','_export_owner','_rebuild','_reader','_exports'):152            owner=getattr(self,slot)153            if owner is not None: owner.close();setattr(self,slot,None)154        self._plan=self._cursors=self._option=None155        _release_owned_ram(self,self._fixed_cost,'_fixed_cost');self._closed=True156
Chronaxis 用户手册 · 离线阅读