docs: 为全部13个源码模块和5个测试文件添加简体中文注释
- 每个文件补充模块级 docstring、类/函数 docstring、关键逻辑行内注释 - 注释风格:"中文说明" 或 # 中文说明 - 代码结构和逻辑完全不变
This commit is contained in:
@@ -1,3 +1,9 @@
|
||||
"""G-code 文件处理工具集。
|
||||
|
||||
本模块提供了 G-code 文本的读取、换行符标准化、时间/耗材格式化、
|
||||
M73 剩余时间偏移计算、换板代码块构建与插入等核心功能。
|
||||
所有对换板 G-code 的文本操作均通过本模块完成。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
@@ -7,14 +13,33 @@ from pathlib import Path
|
||||
from .models import DEFAULT_INSERT_BEFORE_MARKER, LineEnding
|
||||
from .paths import default_swap_gcode_dir
|
||||
|
||||
# 匹配 M73 剩余时间指令,捕获 P/R 参数中的分钟数值
|
||||
# 示例: M73 P10 R120 ; 表示当前进度 10%,剩余 120 分钟
|
||||
M73_RE = re.compile(r"^(M73\s+P\d+\s+R)(\d+)(.*)$", re.MULTILINE)
|
||||
|
||||
|
||||
def normalize_newlines(text: str) -> str:
|
||||
"""将文本中的 CRLF 和 CR 统一转换为 LF 换行符。
|
||||
|
||||
参数:
|
||||
text: 待处理的原始文本。
|
||||
|
||||
返回:
|
||||
使用 LF 换行的规范化文本。
|
||||
"""
|
||||
return text.replace("\r\n", "\n").replace("\r", "\n")
|
||||
|
||||
|
||||
def apply_line_ending(text: str, line_ending: LineEnding) -> bytes:
|
||||
"""将文本按指定的换行符风格编码为字节串。
|
||||
|
||||
参数:
|
||||
text: 待编码的文本。
|
||||
line_ending: 目标换行风格,"lf" 或 "crlf"。
|
||||
|
||||
返回:
|
||||
使用指定换行符编码的 UTF-8 字节串。
|
||||
"""
|
||||
normalized = normalize_newlines(text)
|
||||
if line_ending == "crlf":
|
||||
normalized = normalized.replace("\n", "\r\n")
|
||||
@@ -22,10 +47,35 @@ def apply_line_ending(text: str, line_ending: LineEnding) -> bytes:
|
||||
|
||||
|
||||
def read_swap_gcode_file(path: Path) -> str:
|
||||
"""读取换板 G-code 文件内容,自动处理 BOM 头。
|
||||
|
||||
参数:
|
||||
path: G-code 文件路径。
|
||||
|
||||
返回:
|
||||
文件的文本内容。
|
||||
"""
|
||||
return path.read_text(encoding="utf-8-sig", errors="replace")
|
||||
|
||||
|
||||
def resolve_swap_gcode_path(value: Path | str, swap_gcode_dir: Path | None = None) -> Path:
|
||||
"""将用户输入的文件名或路径解析为实际存在的换板 G-code 文件路径。
|
||||
|
||||
解析策略(按优先级):
|
||||
1. 直接作为路径查找。
|
||||
2. 在 swap_gcode_dir 目录下查找。
|
||||
3. 无后缀名时依次尝试 .gcode, .nc, .ngc, .txt 后缀。
|
||||
|
||||
参数:
|
||||
value: 文件名字符串或路径。
|
||||
swap_gcode_dir: 默认搜索目录,为 None 时使用内置默认目录。
|
||||
|
||||
返回:
|
||||
解析后的文件路径。
|
||||
|
||||
异常:
|
||||
FileNotFoundError: 无法在任何位置找到该文件。
|
||||
"""
|
||||
raw = Path(value)
|
||||
if raw.exists():
|
||||
return raw
|
||||
@@ -33,6 +83,7 @@ def resolve_swap_gcode_path(value: Path | str, swap_gcode_dir: Path | None = Non
|
||||
candidate = search_dir / str(value)
|
||||
if candidate.exists():
|
||||
return candidate
|
||||
# 无后缀名时自动尝试常见 G-code 后缀
|
||||
if raw.suffix:
|
||||
raise FileNotFoundError(f"Swap G-code file not found: {value}")
|
||||
for suffix in (".gcode", ".nc", ".ngc", ".txt"):
|
||||
@@ -43,14 +94,31 @@ def resolve_swap_gcode_path(value: Path | str, swap_gcode_dir: Path | None = Non
|
||||
|
||||
|
||||
def list_swap_gcode_files(directory: Path | None = None) -> list[Path]:
|
||||
"""列出指定目录下所有可用的换板 G-code 文件。
|
||||
|
||||
参数:
|
||||
directory: 要扫描的目录,为 None 时使用内置默认目录。
|
||||
|
||||
返回:
|
||||
按名称排序的 G-code 文件路径列表。
|
||||
"""
|
||||
root = directory or default_swap_gcode_dir()
|
||||
if not root.exists():
|
||||
return []
|
||||
allowed = {".gcode", ".nc", ".ngc", ".txt"}
|
||||
# 过滤掉隐藏文件(以 . 开头),只保留允许的后缀名
|
||||
return sorted(path for path in root.iterdir() if path.is_file() and not path.name.startswith(".") and path.suffix.lower() in allowed)
|
||||
|
||||
|
||||
def format_duration(seconds: float | None) -> str:
|
||||
"""将秒数格式化为易读的时间字符串。
|
||||
|
||||
参数:
|
||||
seconds: 秒数,可为 None。
|
||||
|
||||
返回:
|
||||
格式化后的时间字符串,如 "2h 30m"、"5m 20s"、"45s",未知则返回 "Unknown"。
|
||||
"""
|
||||
if seconds is None:
|
||||
return "Unknown"
|
||||
total = max(0, int(round(seconds)))
|
||||
@@ -64,6 +132,15 @@ def format_duration(seconds: float | None) -> str:
|
||||
|
||||
|
||||
def format_filament(weight_grams: float | None, used_m: float | None = None) -> str:
|
||||
"""将耗材重量和长度格式化为易读字符串。
|
||||
|
||||
参数:
|
||||
weight_grams: 耗材重量(克),可为 None。
|
||||
used_m: 耗材使用长度(米),可为 None。
|
||||
|
||||
返回:
|
||||
格式化后的耗材信息字符串,未知则返回 "Unknown"。
|
||||
"""
|
||||
if weight_grams is None and used_m is None:
|
||||
return "Unknown"
|
||||
parts: list[str] = []
|
||||
@@ -76,10 +153,30 @@ def format_filament(weight_grams: float | None, used_m: float | None = None) ->
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class M73OffsetTemplate:
|
||||
"""M73 剩余时间偏移模板。
|
||||
|
||||
用于在合并多板 G-code 时,将各板的 M73 剩余时间递增偏移,
|
||||
使合并后的 G-code 中每块板的剩余时间估算保持连续。
|
||||
|
||||
属性:
|
||||
segments: M73 指令之间的文本片段(含首尾)。
|
||||
base_remaining_minutes: 每个 M73 指令的原始剩余分钟数。
|
||||
"""
|
||||
segments: tuple[str, ...]
|
||||
base_remaining_minutes: tuple[int, ...]
|
||||
|
||||
def apply(self, plate_number: int) -> str:
|
||||
"""根据板号生成偏移后的 M73 指令文本。
|
||||
|
||||
偏移量 = plate_number * 100 * 60(单位:分钟),
|
||||
即每块板增加 100 小时的剩余时间偏移。
|
||||
|
||||
参数:
|
||||
plate_number: 板号(从 0 开始)。
|
||||
|
||||
返回:
|
||||
应用偏移后的完整 G-code 文本片段。
|
||||
"""
|
||||
if not self.base_remaining_minutes:
|
||||
return self.segments[0]
|
||||
minute_offset = plate_number * 100 * 60
|
||||
@@ -92,40 +189,88 @@ class M73OffsetTemplate:
|
||||
|
||||
|
||||
def prepare_m73_offset_template(gcode: str) -> M73OffsetTemplate:
|
||||
"""从 G-code 文本中提取 M73 剩余时间偏移模板。
|
||||
|
||||
参数:
|
||||
gcode: 原始换板 G-code 文本。
|
||||
|
||||
返回:
|
||||
可用于多板偏移计算的 M73OffsetTemplate 实例。
|
||||
"""
|
||||
segments: list[str] = []
|
||||
base_remaining_minutes: list[int] = []
|
||||
last_index = 0
|
||||
for match in M73_RE.finditer(gcode):
|
||||
# 保存 M73 指令前的文本片段
|
||||
segments.append(gcode[last_index : match.start(2)])
|
||||
# 提取原始剩余分钟数
|
||||
base_remaining_minutes.append(int(match.group(2)))
|
||||
last_index = match.end(2)
|
||||
# 保存最后一段尾部文本
|
||||
segments.append(gcode[last_index:])
|
||||
return M73OffsetTemplate(tuple(segments), tuple(base_remaining_minutes))
|
||||
|
||||
|
||||
def apply_plate_number_offset(gcode: str, plate_number: int) -> str:
|
||||
"""对 G-code 应用板号偏移,更新所有 M73 剩余时间。
|
||||
|
||||
参数:
|
||||
gcode: 原始换板 G-code 文本。
|
||||
plate_number: 板号(从 0 开始)。
|
||||
|
||||
返回:
|
||||
偏移后的 G-code 文本。
|
||||
"""
|
||||
return prepare_m73_offset_template(gcode).apply(plate_number)
|
||||
|
||||
|
||||
def build_swap_block(swap_gcode: str, cool_bed_temp: int | None, wait_seconds: int) -> str:
|
||||
"""构建换板代码块。
|
||||
|
||||
由可选的热床降温 M190 指令、换板 G-code 主体和可选的暂停等待 G4 指令组成。
|
||||
|
||||
参数:
|
||||
swap_gcode: 换板 G-code 主体内容。
|
||||
cool_bed_temp: 目标热床温度(°C),为 None 时不插入降温指令。
|
||||
wait_seconds: 换板后等待秒数,0 时不插入等待指令。
|
||||
|
||||
返回:
|
||||
完整的换板代码块字符串。
|
||||
"""
|
||||
parts: list[str] = []
|
||||
if cool_bed_temp is not None:
|
||||
parts.append(f"M190 S{int(cool_bed_temp)}")
|
||||
parts.append(normalize_newlines(swap_gcode).rstrip("\n"))
|
||||
if wait_seconds > 0:
|
||||
parts.append(f"G4 P{int(wait_seconds) * 1000}")
|
||||
# 过滤空字符串后拼接,末尾加换行
|
||||
return "\n".join(part for part in parts if part) + "\n"
|
||||
|
||||
|
||||
def insert_swap_block(gcode: str, swap_block: str, insert_before_marker: str = DEFAULT_INSERT_BEFORE_MARKER) -> str:
|
||||
"""在原始 G-code 中的指定标记之前插入换板代码块。
|
||||
|
||||
如果找不到标记,则将换板代码块追加到 G-code 末尾。
|
||||
|
||||
参数:
|
||||
gcode: 原始 G-code 文本。
|
||||
swap_block: 要插入的换板代码块。
|
||||
insert_before_marker: 插入位置标记,在该标记所在行之前插入。
|
||||
|
||||
返回:
|
||||
插入换板代码块后的 G-code 文本。
|
||||
"""
|
||||
text = normalize_newlines(gcode)
|
||||
marker = insert_before_marker.strip()
|
||||
if marker:
|
||||
# 查找标记行,支持换行后的标记或文件开头的标记
|
||||
marker_index = text.find("\n" + marker)
|
||||
if marker_index == -1 and text.startswith(marker):
|
||||
marker_index = 0
|
||||
if marker_index != -1:
|
||||
# 在标记行之前插入换板代码块
|
||||
prefix = text[: marker_index + (1 if marker_index > 0 else 0)]
|
||||
suffix = text[marker_index + (1 if marker_index > 0 else 0) :]
|
||||
return prefix + swap_block + suffix
|
||||
# 未找到标记时追加到末尾
|
||||
return text.rstrip("\n") + "\n" + swap_block
|
||||
|
||||
Reference in New Issue
Block a user