Chronaxis用户手册English
本页目录

chronaxis.legacy.export_worker

源码基线:8904c2f744951857659abd40aaa3535d57faf53c

1"""在独立spawn进程按PUT许可将原报文写入新的BLF文件。23有效INIT后核现有依赖版本,BLFWrite独占取得文件并把同一句柄交给python-can。4每PUT恢复原公开字段并写一次,不读取来源或DBC;FINISH仅在最终写入成功后确认。5关闭失败保留原错误与实际文件owner,第三方stop不重复执行,父侧仍核实际子退出。"""67# 模块导入不加载CAN库或打开输出;只有有效INIT之后才加载CAN依赖。8import os9from pathlib import Path10from .records import PROFILE_ID, frame_from_dict11from .worker import _exact, _load_can_runtime, _receive, _report_error, _send121314from ..can_dependencies import runtime_dependency_versions151617def _output(packet):18    """检查唯一INIT及新输出路径,不猜格式或覆盖已有文件。1920    参数: packet为已通过严格JSON边界的dict。返回: resolved新BLF Path。21    异常: TypeError/ValueError为字段、版本、profile、后缀或受保护路径;22        FileExistsError为已有目标,FileNotFoundError为尚无控制器工作根。23    副作用: 仅检查文件系统;真正独占创建仍在writer建立之前执行。24    """25    _exact(packet, ('op', 'version', 'profile', 'path'))26    if (packet['op'] != 'init' or type(packet['version']) is not int or packet['version'] != 127            or type(packet['profile']) is not str or packet['profile'] != PROFILE_ID28            or type(packet['path']) is not str):29        raise ValueError('原CAN写出需要init/version1及固定profile路径')30    path = Path(packet['path']).resolve()31    # 先取得实际目标,再检查后缀和存在性;独占创建仍由写入器执行。32    if path.suffix.lower() != '.blf':33        raise ValueError('原CAN写出仅支持新的BLF文件')34    if path.exists():35        raise FileExistsError(path)36    if not path.parent.is_dir():37        raise FileNotFoundError(path.parent)38    return path394041def _message(can, frame):42    """把全部原公开写出字段交给原can.Message构造,不添加check或归一化参数。4344    参数: can为本次已加载的python-can模块;frame为已校验LegacyRawFrame。45    返回: 新can.Message,timestamp/data/DLC及全部标志保持原参数。46    异常: 原can.Message构造错误;不以新层猜测修正旧格式限制。47    """48    if frame.is_rx is None:49        raise ValueError('BLF写出需要收发方向is_rx')50    return can.Message(timestamp=frame.timestamp, arbitration_id=frame.arbitration_id,51        is_extended_id=frame.is_extended_id, is_remote_frame=frame.is_remote_frame,52        is_error_frame=frame.is_error_frame, channel=frame.channel, dlc=frame.dlc,53        data=frame.data, is_fd=frame.is_fd, is_rx=frame.is_rx,54        bitrate_switch=frame.bitrate_switch, error_state_indicator=frame.error_state_indicator)555657def worker_main(connection):58    """作为真实spawn目标,按INIT/单PUT/FINISH顺序调用唯一BLF writer。5960    参数: connection为父传来的实际duplex端点。返回: 原stop成功并发送候选后None。61    异常: 原错误尽力发送有限ERROR,最终SystemExit(1);管道清理也不能伪成正常退出。62    副作用: 独占创建一个新BLF、一次BLFWrite、每许可一次on_message_received,63        至多一次stop;不读取输入CAN/DBC,不调parse或为ACK额外写帧。64    """65    writer = failure = request_id = None66    phase, frame_count, stop_attempted = 'init', 0, False67    try:68        path = _output(_receive(connection))69        phase = 'dependencies'70        can, cantools = _load_can_runtime()71        versions = runtime_dependency_versions()72        # 同一独占文件对象交给python-can,不关闭后按路径重新打开。73        phase = 'build_writer'74        from ..results.blf_writer import BLFWrite75        writer = BLFWrite(path)76        _send(connection, {'op': 'ready', 'version': 1, 'profile': PROFILE_ID,77            'worker_pid': os.getpid(), 'dependency_versions': versions})78        # 仅收到一条PUT后写一帧;下一许可前不保留当前消息或构造额外帧。79        while True:80            phase, request_id = 'control', None81            packet = _receive(connection)82            if packet.get('op') == 'finish':83                _exact(packet, ('op', 'frame_count'))84                if type(packet['frame_count']) is not int or packet['frame_count'] != frame_count:85                    raise ValueError('FINISH帧数与本次原writer确认数量不符')86                phase, stop_attempted = 'stop', True87                writer.finish()88                writer = None89                _send(connection, {'op': 'finished', 'frame_count': frame_count})90                break91            _exact(packet, ('op', 'request_id', 'frame'))92            candidate = packet['request_id']93            request_id = candidate if type(candidate) is int and candidate >= 0 else None94            if packet['op'] != 'put' or request_id is None or request_id != frame_count:95                raise ValueError('PUT许可必须从0严格连续且为原生int')96            phase = 'frame'97            frame = frame_from_dict(packet['frame'])98            message = _message(can, frame)99            phase = 'write'100            writer.write(message)101            frame_count += 1102            _send(connection, {'op': 'ack', 'request_id': request_id, 'frame_count': frame_count})103            frame = message = None104    except BaseException as exc:105        if writer is None:106            writer=getattr(exc,'blf_write_cleanup_owner',None)107        failure = exc108        _report_error(connection, exc, phase, request_id)109    finally:110        # 失败或父提前断管只结束已有writer;不重发PUT,不重试已经失败的原stop。111        if writer is not None:112            try:113                stop_attempted = True114                writer.close()115            except BaseException as cleanup:116                if failure is None:117                    failure = cleanup118                    _report_error(connection, cleanup, 'stop', request_id)119                else:120                    failure.add_note('原writer.stop附加失败: ' + type(cleanup).__name__)121        for resource, label in ((connection, '子连接'),):122            if resource is not None:123                try:124                    resource.close()125                except BaseException as cleanup:126                    if failure is None:127                        failure = cleanup128                    else:129                        failure.add_note(label + '关闭附加失败: ' + type(cleanup).__name__)130    if failure is not None:131        raise SystemExit(1)132
Chronaxis 用户手册 · 离线阅读