Files
FilmForge/GUI/filmforge_gui.py

120 lines
4.5 KiB
Python

#!/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()