"""数据模型模块。 定义换板打包器所用的全部数据类、类型别名和常量,包括: - 板件任务 (PlateJob) - G-code 补丁规则与配置 (GcodePatchRule / GcodePatchConfig) - 构建选项 (BuildOptions) - 板件来源与摘要 (PlateSource / ThreeMfSummary / BuildSummary) - 构建结果 (BuildResult) """ from __future__ import annotations from dataclasses import dataclass from pathlib import Path from typing import Literal # 默认插入标记:在此行之前插入换板 G-code DEFAULT_INSERT_BEFORE_MARKER = ";=====printer finish sound=========" # 默认 ZIP 压缩级别 (0-9,7 为平衡选择) DEFAULT_ZIP_COMPRESS_LEVEL = 7 # 元数据计算模式:取源文件数值还是累加求和 MetadataMode = Literal["source", "sum"] # 换行符风格:LF (Unix) 或 CRLF (Windows) LineEnding = Literal["lf", "crlf"] @dataclass(frozen=True) class PlateJob: """单个板件打包任务。 Attributes: source_3mf: 源 3MF 文件路径。 copies: 该板件的副本数量,默认为 1。 """ source_3mf: Path copies: int = 1 @dataclass(frozen=True) class GcodePatchRule: """单条 G-code 查找替换规则。 Attributes: name: 规则名称,用于识别和日志。 find: 要查找的目标字符串。 replace: 替换后的字符串。 flag: 正则标志(可选),如 "i" 表示忽略大小写。 enabled: 是否启用该规则,默认启用。 max_count: 最大替换次数,默认 1(仅替换首次出现)。 """ name: str find: str replace: str flag: str = "" # 正则标志,例如 'i' 表示忽略大小写 enabled: bool = True # 是否启用此规则 max_count: int = 1 # 最多替换次数 @dataclass(frozen=True) class GcodePatchConfig: """G-code 补丁配置,包含一组规则和插入标记。 Attributes: rules: 补丁规则元组。 insert_before_marker: 在此标记行之前插入换板 G-code。 """ rules: tuple[GcodePatchRule, ...] insert_before_marker: str = DEFAULT_INSERT_BEFORE_MARKER @dataclass(frozen=True) class BuildOptions: """构建打包选项。 Attributes: swap_gcode: 换板 G-code 文件路径或内容字符串。 output_3mf: 输出 3MF 文件的保存路径。 cool_bed_temp: 冷却热床目标温度(摄氏度),None 表示不等待冷却。 wait_after_eject_seconds: 推板后等待秒数。 show_plate_number: 是否在屏幕上显示当前板件编号。 swap_after_final: 最终板件完成后是否也执行换板。 metadata_mode: 元数据模式,"source" 取源文件值,"sum" 累加求和。 line_ending: 输出 G-code 的换行符风格。 add_preview_label: 是否为板件添加预览标签注释。 apply_gcode_patches: 是否应用 G-code 补丁规则。 swap_gcode_dir: 换板 G-code 文件所在目录,默认自动检测。 zip_compress_level: 3MF (ZIP) 压缩级别,默认 7。 """ swap_gcode: Path | str output_3mf: Path cool_bed_temp: int | None = 45 # 热床冷却目标温度,None 跳过冷却 wait_after_eject_seconds: int = 45 # 推板完成后等待时间 show_plate_number: bool = True # 是否显示板件编号到屏幕 swap_after_final: bool = True # 最后一块是否也换板 metadata_mode: MetadataMode = "sum" # 元数据计算模式 line_ending: LineEnding = "crlf" # 输出换行符 add_preview_label: bool = True # 是否添加预览注释 apply_gcode_patches: bool = True # 是否启用 G-code 补丁 swap_gcode_dir: Path | None = None # 换板 G-code 所在目录 zip_compress_level: int = DEFAULT_ZIP_COMPRESS_LEVEL # ZIP 压缩级别 @dataclass class PlateSource: """单板 G-code 来源数据。 Attributes: source_3mf: 来源 3MF 文件。 member_name: 3MF 内部成员名称,用于正确提取 G-code。 gcode_text: 提取出的 G-code 文本内容。 prediction_seconds: 预估打印时间(秒)。 weight_grams: 预估耗材重量(克)。 filament_used_m: 预估耗材用量(米)。 filament_used_g: 预估耗材用量(克),部分切片软件以此字段为主。 """ source_3mf: Path member_name: str # 3MF 内的成员名,用于定位 G-code gcode_text: str # 完整的 G-code 文本 prediction_seconds: float | None = None # 预估打印时间(秒) weight_grams: float | None = None # 预估耗材重量(克) filament_used_m: float | None = None # 耗材用量(米) filament_used_g: float | None = None # 耗材用量(克) @dataclass(frozen=True) class ThreeMfSummary: """单个 3MF 文件的元数据摘要。 Attributes: source_3mf: 来源 3MF 文件。 plate_count: 该文件中包含的板件数量。 prediction_seconds: 预估打印时间(秒)。 weight_grams: 预估耗材重量(克)。 filament_used_m: 预估耗材用量(米)。 filament_used_g: 预估耗材用量(克)。 """ source_3mf: Path plate_count: int # 板件数量 prediction_seconds: float | None = None weight_grams: float | None = None filament_used_m: float | None = None filament_used_g: float | None = None @dataclass(frozen=True) class BuildSummary: """总体构建摘要。 Attributes: plate_count: 总板件数。 total_prediction_seconds: 总预估打印时间(秒)。 total_weight_grams: 总耗材重量(克)。 total_filament_used_m: 总耗材用量(米)。 total_filament_used_g: 总耗材用量(克)。 """ plate_count: int total_prediction_seconds: float | None # 总预估时间 total_weight_grams: float | None # 总耗材重量 total_filament_used_m: float | None # 总耗材长度 total_filament_used_g: float | None # 总耗材质量 @dataclass class BuildResult: """单次构建打包的最终结果。 Attributes: output_3mf: 输出 3MF 文件路径。 plate_count: 包含的板件总数。 total_prediction_seconds: 总预估打印时间(秒)。 total_weight_grams: 总耗材重量(克)。 gcode_md5: 最终 G-code 内容的 MD5 哈希值,用于唯一性校验。 """ output_3mf: Path plate_count: int total_prediction_seconds: float | None total_weight_grams: float | None gcode_md5: str # G-code MD5 哈希值