Chronaxis用户手册English
本页目录

chronaxis.fast.blocks

源码基线:8904c2f744951857659abd40aaa3535d57faf53c

1"""贡献块的有界二进制编码。浮点使用binary64,时间和记录序号保持整数。23同一帧多个贡献共享时间和记录位置;CRC检查暂存意外损坏。只处理本次会话4内部数据,不加载任意对象,不保存完整CAN帧或解码字典。5"""6import math7import struct8import zlib910ROWS = 102411LIMIT = 26214412RAM = 4 * 104857613HEADER = struct.Struct('<4sQQI')  # 固定版本、文件号、块号、帧数。14FRAME = struct.Struct('<qQI')15ENTRY = struct.Struct('<IdB')16CRC = struct.Struct('<I')171819def encode(file_index, number, records):20    """编码一个有界块。参数: 文件号/块号/贡献记录。返回: bytes。21    异常: 超大块或非法数值;不会改成低精度或丢弃尾部。22    """23    payload = bytearray(HEADER.pack(b'CFS1', file_index, number, len(records)))24    if not 0 < len(records) <= ROWS:25        raise ValueError('贡献块记录数量非法')26    for timestamp, sequence, entries in records:27        payload.extend(FRAME.pack(timestamp, sequence, len(entries)))28        for metric, value, keep in entries:29            if type(value) is not float or not math.isfinite(value) or type(keep) is not bool:30                raise ValueError('贡献须为有限float64和布尔入选标志')31            payload.extend(ENTRY.pack(metric, value, keep))32        if len(payload) + CRC.size > LIMIT:33            raise ValueError('贡献块超出字节上限')34    payload.extend(CRC.pack(zlib.crc32(payload)))35    return bytes(payload)363738def decode(payload, file_index, number, metric_count):39    """核验并逐帧解码。参数: bytes/预期身份/计算项数。返回: 记录生成器。40    异常: 身份、长度、CRC、序号、计算项或数值非法;不接受重复项。41    """42    if not HEADER.size + CRC.size <= len(payload) <= LIMIT:43        raise ValueError('贡献块长度非法')44    magic, actual_file, actual_number, count = HEADER.unpack_from(payload)45    if (magic, actual_file, actual_number) != (b'CFS1', file_index, number) or not 0 < count <= ROWS:46        raise ValueError('贡献块身份或数量不符')47    end = len(payload) - CRC.size48    if zlib.crc32(memoryview(payload)[:end]) != CRC.unpack_from(payload, end)[0]:49        raise ValueError('贡献块完整性校验失败')50    offset = HEADER.size51    previous = -152    for _ in range(count):53        if offset + FRAME.size > end:54            raise ValueError('贡献块缺少帧头')55        timestamp, sequence, size = FRAME.unpack_from(payload, offset)56        offset += FRAME.size57        if sequence <= previous or not 0 < size <= metric_count or offset + size * ENTRY.size > end:58            raise ValueError('贡献记录顺序或数量非法')59        previous = sequence60        entries = []61        seen = set()62        for _ in range(size):63            metric, value, keep = ENTRY.unpack_from(payload, offset)64            offset += ENTRY.size65            if metric >= metric_count or metric in seen or keep not in (0, 1) or not math.isfinite(value):66                raise ValueError('贡献计算项/数值非法')67            seen.add(metric)68            entries.append((metric, value, bool(keep)))69        yield timestamp, sequence, tuple(entries)70    if offset != end:71        raise ValueError('贡献块有未消费尾部')727374def batches(stream):75    """按记录数及字节双上限切块。参数: 单文件贡献流。返回: 有限列表生成器。76    异常: 单帧超限;不会拆散同帧元数据或整文件载入。77    """78    block = []79    size = HEADER.size + CRC.size80    for record in stream:81        cost = FRAME.size + ENTRY.size * len(record[2])82        if cost + HEADER.size + CRC.size > LIMIT:83            raise ValueError('单帧贡献超过字节上限')84        if len(block) == ROWS or size + cost > LIMIT:85            yield block86            block = []87            size = HEADER.size + CRC.size88        block.append(record)89        size += cost90    if block:91        yield block92
Chronaxis 用户手册 · 离线阅读