Chronaxis用户手册English
本页目录

chronaxis.sources.blf

源码基线:8904c2f744951857659abd40aaa3535d57faf53c

1"""为普通分析与高性能积分器共用既有python-can BLFReader;不重新实现BLF格式解析。23只保留当前报文,DBC由混合来源共用一次。账本计入本层对象;第三方解压器内部4分配与原BLF路径一样不代表硬RSS上限。不扫描文件、不补日期、不改原帧字段。5"""6from dataclasses import dataclass7from pathlib import Path89from ..legacy.worker import _frame10from ..results.budget import _reserve_owned_ram, _release_owned_ram, _cleanup_owner11from ..timebase.conversion import convert_time, checked_add_ns, require_int64_ns12from .raw_can import RawCANRecord, CANTextError131415@dataclass(frozen=True)16class BLFSpec:17    """单文件BLF声明;offset_ns仅平移分析坐标,原timestamp不改。"""18    path: Path19    offset_ns: int = 02021    def __post_init__(self):22        """核对静态路径与偏移,不打开输入。失败抛类型或范围错误。"""23        if not isinstance(self.path, Path) or self.path.suffix.lower() != '.blf':24            raise ValueError('BLFSpec需要.blf文件Path')25        require_int64_ns(self.offset_ns)262728class BLFRead:29    """持有一个真实BLFReader与当前迭代器,EOF、失败、取消均关闭本次文件。"""3031    def __init__(self, spec, budget, *, check_cancel=None, retain_original=False,32                 retain_source_time=False, selected_ids=None, emit_frame_ids=None,33                 channel=None, projection_only=False, time_check_all=False, time_scope='source', frame_only=False, binary_time=False, previous_order_ns=None):34        """接管真实文件并打开BLF库读取器,构造失败附恢复owner。3536        参数: spec为BLFSpec,budget为共享账本;选择/时间选项来自已有消费者。37        frame_only只供fast借用同次原消息,其时间和长度仍由fast检查;不构造样本。38        binary_time使纯BLF串行保原二进制float秒换算,previous_order_ns延续前文件顺序。39        返回: 未读首帧的owner。异常: 配置、文件头、预算或取消原错;失败不泄漏自有文件。40        """41        if type(spec) is not BLFSpec:42            raise TypeError('BLFRead需要BLFSpec')43        self.spec, self._budget, self._check_cancel = spec, budget, check_cancel44        self._retain_source_time = retain_source_time45        if type(frame_only) is not bool or type(binary_time) is not bool:46            raise TypeError('BLF内部帧投影与时间转换选项需要bool')47        # fast借同次原消息,保留自己的选择、时间及长度检查顺序。48        self._frame_only = frame_only49        self._binary_time = binary_time50        self._selected_ids = selected_ids51        self._emit_frame_ids = None if emit_frame_ids is None else frozenset(emit_frame_ids)52        self._channel=channel53        self._projection_only=projection_only and self._emit_frame_ids is not None54        self._time_check_all=time_check_all55        self._time_scope=time_scope56        if time_scope not in ('source','selected','none') or time_scope!='source' and not self._projection_only:57            raise ValueError('按需BLF时间范围必须配合明确的目标帧投影')58        self.first_ns=self.last_ns=self.first_order_ns=None59        self.last_order_ns=previous_order_ns60        if self._projection_only or binary_time:61            # 复用已有批读取的精确二进制秒转换,避免对无关帧建立完整模型。62            from ..execution.can_batch_reader import _float_seconds_ns63            self._convert_float=_float_seconds_ns64        self._file = self._reader = self._iterator = None65        self._fixed_cost = self._held_cost = 066        self._closed = False67        self.count = 068        self.full_check_count = 069        try:70            _reserve_owned_ram(self,65536,'_fixed_cost')71            if check_cancel is not None: check_cancel()72            from ..can_dependencies import import_can_dependency73            can = import_can_dependency('can')74            self._native_message_type=can.Message75            # 文件先安装到owner;BLF头解析失败时也能关闭,不依赖析构回收。76            self._file = spec.path.open('rb')77            self._reader = can.BLFReader(self._file)78            self._iterator = iter(self._reader)79        except BaseException as error:80            _cleanup_owner(self,error,'blf_read_cleanup_owner','混合来源BLF构造')81            raise8283    @property84    def closed(self):85        """返回本层所有文件和费用是否已释放。"""86        return self._closed8788    def __iter__(self):89        """返回同次迭代器,不重复打开文件。"""90        return self9192    def __next__(self):93        """读取下一实际帧,先按ID选择再转换;失败保原异常并清理。"""94        if self._closed: raise StopIteration95        try:96            while True:97                if self._check_cancel is not None: self._check_cancel()98                message = next(self._iterator)99                sequence = self.count100                self.count += 1101                if self._frame_only:102                    return message103                serial_id=message.arbitration_id if self._binary_time else None104                fallback_frame=None105                if self._binary_time and type(message) is not self._native_message_type:106                    # 非原生替身保持旧完整字段检查,不能用投影绕过属性或结构错误。107                    fallback_frame=_frame(message)108                    from ..legacy.batch_validation import _item_base_size109                    _item_base_size(fallback_frame,sequence)110                    self.full_check_count += 1111                if self._projection_only:112                    selected=(self._channel is None or message.channel==self._channel) and (serial_id if self._binary_time else message.arbitration_id) in self._emit_frame_ids113                    # 纯消息公式不读取时间;保留原消息供同次DBC解码,不制造零秒或原帧导出能力。114                    if self._time_scope=='none':115                        if not selected:116                            continue117                        return _UntimedBLFRecord(message,self.spec.path,sequence)118                    if self._time_scope=='selected' and not selected:119                        continue120                    raw_ns,quantized=self._convert_float(message.timestamp)121                    mapped=checked_add_ns(raw_ns,self.spec.offset_ns)122                    self.first_ns=mapped if self.first_ns is None else min(self.first_ns,mapped)123                    self.last_ns=mapped if self.last_ns is None else max(self.last_ns,mapped)124                    if self._time_check_all or selected:125                        if self.last_order_ns is not None and mapped<self.last_order_ns:126                            raise CANTextError(self.spec.path,None,f'原记录{sequence}时间回退')127                        if self.first_order_ns is None:128                            self.first_order_ns=mapped129                        self.last_order_ns=mapped130                    if not selected:131                        continue132                if self._selected_ids is not None and message.arbitration_id not in self._selected_ids:133                    continue134                frame = fallback_frame if fallback_frame is not None else _frame(message)135                if not self._projection_only:136                    if self._binary_time:137                        raw_ns,quantized=self._convert_float(frame.timestamp)138                    else:139                        timestamp = convert_time(frame.timestamp,'s')140                        raw_ns,quantized=timestamp.time_ns,timestamp.quantized141                    mapped=checked_add_ns(raw_ns,self.spec.offset_ns)142                # BLF远程帧已由读取库给出请求长度;保留明确的0,不能当作ASC省略DLC。143                remote_dlc = frame.dlc if frame.is_remote_frame else None144                return RawCANRecord(frame,mapped,self.spec.path,None,sequence,None,'blf-v1',remote_dlc,145                    quantized,raw_ns if self._retain_source_time else None)146        except StopIteration:147            self.close()148            raise149        except BaseException as error:150            _cleanup_owner(self,error,'blf_read_cleanup_owner','混合来源BLF读取')151            raise152153    def close(self):154        """关闭本次库迭代器和文件再释放费用;失败保留owner以便重试。"""155        if self._closed: return156        if self._iterator is not None:157            self._iterator.close()158            self._iterator = None159        if self._reader is not None:160            self._reader.stop()161            self._reader = None162        if self._file is not None:163            self._file.close()164            self._file = None165        if self._fixed_cost: _release_owned_ram(self,self._fixed_cost,'_fixed_cost')166        self._closed = True167168169@dataclass(frozen=True)170class _UntimedBLFRecord:171    """内部按需消息:保存真实输入身份与载荷,明确没有采集分析时间,禁止原帧导出。172173    参数: 本次python-can消息、路径、原序号。返回: 有限内部记录。异常: 工厂以真实解析输入构造。174    生命周期仅覆盖同次解码和有界传输;不作为公开RawCANRecord或时间线样本。175    """176    frame: object177    path: Path178    sequence: int179    time_ns: None = None180    raw_time: None = None181    line_number: None = None182
Chronaxis 用户手册 · 离线阅读