docs: 为全部13个源码模块和5个测试文件添加简体中文注释
- 每个文件补充模块级 docstring、类/函数 docstring、关键逻辑行内注释 - 注释风格:"中文说明" 或 # 中文说明 - 代码结构和逻辑完全不变
This commit is contained in:
@@ -1,3 +1,9 @@
|
||||
"""3MF 文件元数据读取与处理模块。
|
||||
|
||||
提供从 3MF 归档文件中提取切片信息、耗材数据、模型设置等元数据的功能,
|
||||
以及基于元数据生成构建摘要(时间预估、耗材用量等)的辅助函数。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import zipfile
|
||||
@@ -10,6 +16,18 @@ from .models import BuildOptions, BuildSummary, PlateJob, PlateSource, ThreeMfSu
|
||||
|
||||
|
||||
def read_slice_plate_metadata(archive: zipfile.ZipFile) -> dict[int, dict[str, str]]:
|
||||
"""读取 3MF 归档中各打印板的切片元数据。
|
||||
|
||||
从 Metadata/slice_info.config 中解析每个 <plate> 元素内的 <metadata> 键值对,
|
||||
按盘索引号 index 组织为字典。
|
||||
|
||||
参数:
|
||||
archive: 已打开的 3MF 压缩文件对象。
|
||||
|
||||
返回:
|
||||
dict[int, dict[str, str]]: plate_index -> {metadata_key: metadata_value},
|
||||
若配置文件不存在则返回空字典。
|
||||
"""
|
||||
try:
|
||||
data = archive.read("Metadata/slice_info.config")
|
||||
except KeyError:
|
||||
@@ -23,6 +41,7 @@ def read_slice_plate_metadata(archive: zipfile.ZipFile) -> dict[int, dict[str, s
|
||||
value = metadata.attrib.get("value")
|
||||
if key is not None and value is not None:
|
||||
plate_data[key] = value
|
||||
# 使用 index 字段确定盘编号,缺失或无效时自动递增
|
||||
index_text = plate_data.get("index")
|
||||
try:
|
||||
index = int(index_text) if index_text else len(result) + 1
|
||||
@@ -33,6 +52,18 @@ def read_slice_plate_metadata(archive: zipfile.ZipFile) -> dict[int, dict[str, s
|
||||
|
||||
|
||||
def read_filament_metadata(archive: zipfile.ZipFile) -> dict[int, dict[str, str]]:
|
||||
"""读取 3MF 归档中各打印板的耗材(filament)元数据。
|
||||
|
||||
从 Metadata/slice_info.config 中提取每个 <plate> 下 <filament> 元素的属性,
|
||||
按盘索引号组织。
|
||||
|
||||
参数:
|
||||
archive: 已打开的 3MF 压缩文件对象。
|
||||
|
||||
返回:
|
||||
dict[int, dict[str, str]]: plate_index -> {filament_attr: value},
|
||||
若配置文件不存在则返回空字典。
|
||||
"""
|
||||
try:
|
||||
data = archive.read("Metadata/slice_info.config")
|
||||
except KeyError:
|
||||
@@ -41,6 +72,7 @@ def read_filament_metadata(archive: zipfile.ZipFile) -> dict[int, dict[str, str]
|
||||
result: dict[int, dict[str, str]] = {}
|
||||
for plate in root.findall("plate"):
|
||||
plate_index = 1
|
||||
# 先获取当前盘的 index 编号
|
||||
for metadata in plate.findall("metadata"):
|
||||
if metadata.attrib.get("key") == "index":
|
||||
try:
|
||||
@@ -55,6 +87,17 @@ def read_filament_metadata(archive: zipfile.ZipFile) -> dict[int, dict[str, str]
|
||||
|
||||
|
||||
def read_model_settings_gcode_members(archive: zipfile.ZipFile) -> list[str]:
|
||||
"""读取模型设置中配置的 G-code 文件成员名称列表。
|
||||
|
||||
从 Metadata/model_settings.config 中提取所有 plate 的 gcode_file 元数据值,
|
||||
用于确定需要处理的 G-code 归档成员。
|
||||
|
||||
参数:
|
||||
archive: 已打开的 3MF 压缩文件对象。
|
||||
|
||||
返回:
|
||||
list[str]: 符合 G-code 命名规范的成员路径列表,若配置不存在则返回空列表。
|
||||
"""
|
||||
try:
|
||||
data = archive.read("Metadata/model_settings.config")
|
||||
except KeyError:
|
||||
@@ -68,6 +111,7 @@ def read_model_settings_gcode_members(archive: zipfile.ZipFile) -> list[str]:
|
||||
for metadata in plate.findall("metadata"):
|
||||
if metadata.attrib.get("key") != "gcode_file":
|
||||
continue
|
||||
# 规范化路径分隔符并去掉前导斜杠
|
||||
value = metadata.attrib.get("value", "").strip().replace("\\", "/").lstrip("/")
|
||||
if value and GCODE_MEMBER_RE.match(value):
|
||||
members.append(value)
|
||||
@@ -76,6 +120,14 @@ def read_model_settings_gcode_members(archive: zipfile.ZipFile) -> list[str]:
|
||||
|
||||
|
||||
def safe_float(value: str | None) -> float | None:
|
||||
"""安全地将字符串转换为浮点数,失败时返回 None。
|
||||
|
||||
参数:
|
||||
value: 待转换的字符串或 None。
|
||||
|
||||
返回:
|
||||
float | None: 转换成功返回浮点数,否则返回 None。
|
||||
"""
|
||||
if value is None:
|
||||
return None
|
||||
try:
|
||||
@@ -85,6 +137,14 @@ def safe_float(value: str | None) -> float | None:
|
||||
|
||||
|
||||
def _sum_optional(values: Iterable[float | None]) -> float | None:
|
||||
"""对一组可选浮点值求和,全为 None 时返回 None。
|
||||
|
||||
参数:
|
||||
values: 包含 float 或 None 的可迭代对象。
|
||||
|
||||
返回:
|
||||
float | None: 有效值的总和,若无任何有效值则返回 None。
|
||||
"""
|
||||
total = 0.0
|
||||
found = False
|
||||
for value in values:
|
||||
@@ -95,6 +155,19 @@ def _sum_optional(values: Iterable[float | None]) -> float | None:
|
||||
|
||||
|
||||
def read_3mf_summary(source_3mf: Path) -> ThreeMfSummary:
|
||||
"""读取单个 3MF 文件的完整摘要信息。
|
||||
|
||||
汇总文件内所有打印板的预估时间、重量、耗材用量等数据。
|
||||
|
||||
参数:
|
||||
source_3mf: 3MF 源文件路径。
|
||||
|
||||
返回:
|
||||
ThreeMfSummary: 包含盘数量、预估秒数、重量克数、耗材米数和克数的摘要对象。
|
||||
|
||||
抛出:
|
||||
ValueError: 若文件中未找到任何 G-code 成员。
|
||||
"""
|
||||
with zipfile.ZipFile(source_3mf, "r") as archive:
|
||||
members = list_gcode_members(archive)
|
||||
if not members:
|
||||
@@ -111,13 +184,16 @@ def read_3mf_summary(source_3mf: Path) -> ThreeMfSummary:
|
||||
plate_index = int(match.group(1)) if match else 1
|
||||
metadata = plate_metadata.get(plate_index, {})
|
||||
filament = filament_metadata.get(plate_index, {})
|
||||
# 提取各项元数据值
|
||||
prediction = safe_float(metadata.get("prediction"))
|
||||
weight = safe_float(metadata.get("weight"))
|
||||
used_m = safe_float(filament.get("used_m"))
|
||||
used_g = safe_float(filament.get("used_g"))
|
||||
predictions.append(prediction)
|
||||
# 重量优先取 weight,若无则回退到 used_g
|
||||
weights.append(weight if weight is not None else used_g)
|
||||
used_m_values.append(used_m)
|
||||
# 耗材克数优先取 used_g,若无则回退到 weight
|
||||
used_g_values.append(used_g if used_g is not None else weight)
|
||||
|
||||
return ThreeMfSummary(
|
||||
@@ -131,6 +207,17 @@ def read_3mf_summary(source_3mf: Path) -> ThreeMfSummary:
|
||||
|
||||
|
||||
def multiply_summary(summary: ThreeMfSummary, copies: int) -> BuildSummary:
|
||||
"""将单个 3MF 摘要按指定份数倍增,生成构建摘要。
|
||||
|
||||
用于计算同一文件多次打印时的累计耗时和耗材用量。
|
||||
|
||||
参数:
|
||||
summary: 单个 3MF 文件的摘要对象。
|
||||
copies: 打印份数(至少为 1)。
|
||||
|
||||
返回:
|
||||
BuildSummary: 倍增后的构建摘要。
|
||||
"""
|
||||
multiplier = max(1, int(copies))
|
||||
return BuildSummary(
|
||||
plate_count=summary.plate_count * multiplier,
|
||||
@@ -142,6 +229,16 @@ def multiply_summary(summary: ThreeMfSummary, copies: int) -> BuildSummary:
|
||||
|
||||
|
||||
def summarize_jobs(jobs: Iterable[PlateJob]) -> BuildSummary:
|
||||
"""汇总所有换板作业的构建摘要。
|
||||
|
||||
遍历所有 PlateJob,依次读取各自的 3MF 摘要并按份数倍增后累加。
|
||||
|
||||
参数:
|
||||
jobs: PlateJob 对象的可迭代集合。
|
||||
|
||||
返回:
|
||||
BuildSummary: 所有作业合并后的构建摘要。
|
||||
"""
|
||||
plate_count = 0
|
||||
predictions: list[float | None] = []
|
||||
weights: list[float | None] = []
|
||||
@@ -164,12 +261,26 @@ def summarize_jobs(jobs: Iterable[PlateJob]) -> BuildSummary:
|
||||
|
||||
|
||||
def update_first_slice_info(base_data: bytes, sources: list[PlateSource], options: BuildOptions) -> bytes:
|
||||
"""更新打包后 3MF 中首个 slice_info.config 的元数据。
|
||||
|
||||
根据 metadata_mode 选项决定是保留源数据还是累加所有来源的预估时间和耗材用量。
|
||||
|
||||
参数:
|
||||
base_data: 原始 slice_info.config 的字节数据。
|
||||
sources: 所有 PlateSource 的列表。
|
||||
options: 构建选项(含 metadata_mode 字段)。
|
||||
|
||||
返回:
|
||||
bytes: 更新后的 slice_info.config 字节数据。
|
||||
"""
|
||||
if options.metadata_mode == "source":
|
||||
# 源模式:保持原始数据不变
|
||||
return base_data
|
||||
try:
|
||||
root = ET.fromstring(base_data.decode("utf-8-sig", errors="replace"))
|
||||
except Exception:
|
||||
return base_data
|
||||
# 累加所有来源的预估时间和耗材用量
|
||||
total_prediction = sum(value for value in (source.prediction_seconds for source in sources) if value is not None)
|
||||
total_weight = sum(value for value in (source.weight_grams for source in sources) if value is not None)
|
||||
total_used_m = sum(value for value in (source.filament_used_m for source in sources) if value is not None)
|
||||
@@ -177,6 +288,7 @@ def update_first_slice_info(base_data: bytes, sources: list[PlateSource], option
|
||||
first_plate = root.find("plate")
|
||||
if first_plate is None:
|
||||
return base_data
|
||||
# 更新第一个 plate 的 metadata 和 filament 字段
|
||||
for metadata in first_plate.findall("metadata"):
|
||||
key = metadata.attrib.get("key")
|
||||
if key == "prediction" and total_prediction > 0:
|
||||
|
||||
Reference in New Issue
Block a user