#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ FilmForge 温控闭环监视台 — 入口与命令行模式 ================================================ 配合 FilmForge 热压模具温控板(RS485 数据口, 协议见 ../protocols.md)使用。 模块划分: app_fluent.py PySide6-Fluent-Widgets 主界面 + 自绘温度曲线 filmforge_rs485.py RS485 协议与串口链路(纯标准库 + 可选 pyserial) config.py 运行目录与 filmforge_gui.json 设置 依赖: GUI 需 PySide6 + PySide6-Fluent-Widgets; 串口需 pyserial。CLI 仅标准库+pyserial。 命令行模式(自动化/联调用): python filmforge_gui.py 启动 GUI python filmforge_gui.py --seconds 5 GUI 5 秒自动关闭(冒烟测试) python filmforge_gui.py --port COM5 --cmd M999 --cmd M1 RS485 逐条发命令并打印译码响应 python filmforge_gui.py --port COM5 --cmd M2S500 --seconds-report 5 开 500ms 上报并连续收 5 秒打印 """ import argparse import sys import time import filmforge_rs485 as ff def run_gui(auto_close=0): """启动 PySide6-Fluent-Widgets 主界面(实现在 app_fluent.py)""" import app_fluent app_fluent.run_gui(auto_close) def run_cli(port, baud, cmds, seconds_report=0): """RS485 直连模式: 逐条发送命令并打印译码响应(自动化脚本用)。 响应超时 2s(协议建议 300ms 重发, 此处不重发直接判失败)。 返回码: 任一命令非法/超时/响应非 F0 -> 1, 全部成功 -> 0。 """ if not ff.HAS_SERIAL: print('错误: 未安装 pyserial, 请先执行 py -m pip install pyserial') return 2 try: ser = ff.serial.Serial(port, baud, timeout=0.1) except Exception as e: print(f'打开串口失败: {port} @ {baud}: {e}') return 1 print(f'已连接 {port} @ {baud} (RS485 半双工, 单发单收)') parser = ff.FrameParser() rc = 0 def wait_reply(deadline_s): got = None deadline = time.time() + deadline_s while got is None and time.time() < deadline: data = ser.read(256) if data: got = next(iter(parser.feed(data)), None) return got for cmd in cmds: try: frame = ff.build_frame(cmd) except ValueError as e: print(f'→ {cmd} 命令非法: {e}') rc = 1 continue ser.write(frame) print(f'→ {cmd} [{frame.hex(" ").upper()}]') got = wait_reply(2.0) if got is None: print('← (2s 超时无响应)') rc = 1 else: print(f'← {ff.describe(got)} [{got.raw.hex(" ").upper()}]') if got.type_code != 0xF0: rc = 1 time.sleep(0.05) # 协议: 命令间隔 >=50ms if seconds_report > 0: # 连续收帧(验证 M2 周期上报) print(f'── 连续接收 {seconds_report}s ──') deadline = time.time() + seconds_report n = 0 while time.time() < deadline: data = ser.read(256) for fr in parser.feed(data): n += 1 print(f'← {ff.describe(fr)} [{fr.raw.hex(" ").upper()}]') print(f'── 共 {n} 帧 ──') ser.close() return rc def main(): ap = argparse.ArgumentParser(description='FilmForge 热压模具温控闭环监视台') ap.add_argument('--seconds', type=int, default=0, help='GUI 自动退出秒数(冒烟测试)') ap.add_argument('--port', default=None, help='RS485 串口(如 COM5), 配合 --cmd') ap.add_argument('--baud', type=int, default=ff.DEFAULT_BAUD, help='波特率(默认 115200)') ap.add_argument('--cmd', action='append', default=None, dest='cmds', metavar='CMD', help='RS485 发送命令(可多次), 如 --cmd M999 --cmd M1') ap.add_argument('--seconds-report', type=int, default=0, metavar='SEC', help='发送完命令后连续接收 SEC 秒(打印周期上报帧)') args = ap.parse_args() if args.cmds or args.seconds_report: if not args.port: print('错误: --cmd/--seconds-report 需要配合 --port 指定串口(如 --port COM5)') sys.exit(2) sys.exit(run_cli(args.port, args.baud, args.cmds or [], args.seconds_report)) run_gui(args.seconds) if __name__ == '__main__': main()