generated from hulk/gd32f4xx_template_cmake_vscode
120 lines
4.5 KiB
Python
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()
|