Files
FilmForge/GUI/README.md
T
hulk 000781e2f5 GUI v0.2.13: chart Y-axis range control (right-edge dual-handle slider)
- TemperatureChart gains a right-edge slider on a FIXED -20..120C rail
  (user-specified): top handle = upper bound, bottom handle = lower bound,
  drag middle band = pan keeping span, click free rail = snap that bound.
  First drag engages manual mode (yManualEngaged -> sync button state +
  config); release quantizes to 0.1C via yRangeChanged, min span 0.5C.
  Bound values during drag must come from live display range, not the
  stale paint cache (two drags without repaint made pan jump the range).
- Auto/manual toggle moved into the card header (was a wrapped second
  row); pause/clear merged into header too; legend moved to top-left.
- Value labels next to handles only visible on hover/drag (constant
  labels covered the curve per user feedback).
- Out-of-range curves clipped back to the plot area (setClipRect);
  current-value marker suppressed outside the manual range.
- Draft textbox rows from v0.2.10 superseded; versions v0.2.10..v0.2.13.
- Fixed "no data yet" hint never disappearing: set_source cached a
  stale _has_data bool once at build time while data flows through the
  same list references; now evaluated live at paint time.
- config: y_axis key persists mode/bounds; invalid stored values fall
  back to auto. selftest settings isolated to a temp dir (user json with
  stored manual state broke the fresh-default assertions).
- selftest_gui: slider hit/drag/clamp/pan/clipping regression + hint
  gating; 83 checks all pass.
2026-10-15 21:30:21 +08:00

8.5 KiB
Raw Blame History

FilmForge GUI — 热压模具温控闭环监视台

FilmForge 热压模具温控板(GD32F405, RS485 数据口)的桌面上位机:实时温度曲线、 目标温度/加热/周期上报控制、收发日志与 CSV 数据记录。协议权威定义见固件仓根目录 ../protocols.md(v1.0,帧 hex 由脚本生成)。

功能

  • 实时温度曲线(自绘 QPainter 控件,无第三方图表依赖):实测温度折线 + 目标温度橙色阶梯线同图对照(闭环监视),NTC 开路/短路处曲线断开并打故障标记; 时间窗 2/5/15/60 分钟/全部,暂停/恢复,窗口统计(最小/最大/平均); Y 轴(温度轴)量程可选自动(随可见数据自适应)或手动——直接拖动 图表右缘滑条(固定参考域 -20~120℃,把手位置=℃ 读数):上把手=上限、 下把手=下限、拖动把手中段上下=整段平移并把跨度保持不变、点击轨道空白直接 把对应边界设到该处;首次任意拖拽自动切入手动("Y轴自动/手动"按钮同步), 把手数值标签仅悬停/拖拽时显示(不常驻遮挡曲线),曲线越界时裁剪回绘图区; 模式与量程记忆在 filmforge_gui.json。
  • 温度数据:温度监控统一走固件 M2 周期上报帧(M2S 开启后主动推送入曲线); M1 仅作手动单点查询(命令区按钮/预设)。
  • 控制面板:目标温度 M4S(0200 整数℃,滑条+输入框)、加热 M3S1/M3S0 (真实 PID 闭环控温,开启需确认)、周期上报 M2S(10060000,10ms 步进)、 预设/自定义命令。
  • 状态联动:连接后自动查询版本(M999)/上报(M2)/加热(M3)/目标(M4)并同步到状态芯片; 软件复位(M888)/进 OTA(M9999) 二次确认,复位后 2 秒自动重查状态。
  • 收发日志:译码 + 原始 hex,500 行上限防卡顿。
  • CSV 记录:逐行写入 logs/temp_*.csv(时间/相对秒/温度/目标/加热/传感器)。

快速开始

源码运行(Python 3.12+,Windows):

py -m pip install PySide6 PySide6-Fluent-Widgets pyserial
python filmforge_gui.py

选择串口(板上 RS485 数据口,本机通常 COM5)→ 连接,115200-8-N-1。

命令行模式(自动化/无 GUI 联调):

python filmforge_gui.py --port COM5 --cmd M999 --cmd M1      # 逐条命令并打印译码响应
python filmforge_gui.py --port COM5 --cmd M2S500 --seconds-report 3   # 收 3s 上报帧
python filmforge_gui.py --seconds 5                          # GUI 冒烟(自动关闭)

打包 / 自检 / 发布

python selftest_rs485.py        # 协议回归(对照 protocols.md 帧向量, 无硬件)
python selftest_gui.py          # GUI 离屏回归(曲线渲染/帧派发/主题/CSV)
python build_exe.py             # → release/FilmForge.exe + 发布 zip
python publish_gitea.py release/FilmForge-GUI-v<版本>-win64.zip --replace --insecure

实现要点(改代码前必读)

  • 分层:filmforge_rs485.py(协议+串口线程,纯标准库+可选 pyserial)/ app_fluent.py(PySide6-Fluent 呈现层)/ config.py(运行目录+设置记忆)/ filmforge_gui.py(入口+CLI)。协议模块不依赖 GUI。
  • 应答派发是 FIFO 队列(app_fluent._pending):半双工上固件严格按收到顺序应答, 连接序列(150ms 间隔连发)场景下单 pending 槽会被乱序击穿——实测踩坑: M2 的 OFF 被 M3 槽吃掉、M4 的目标值混进温度曲线。应答形态与队首期望匹配才消费, 不匹配记杂散,1.2s 超时自清。
  • M4 查询走"暂停上报→查询→恢复"应答驱动链:M4 应答与周期上报帧同为 ASCII 浮点,仅靠时间窗拆分在 100ms 上报下实测必现误占(目标 chip 显示成实测温度)—— 上报开启时查目标自动执行 M2S0(ok)→M4→M2S 恢复;连接同步序列同样走此路径。
  • 晚到 ok 补偿:USB-RS485 适配器换向延迟实测可 >4s,设定命令的 ok 迟到超过 1.2s 已按超时清队,8s 内的迟到 ok 按发送时记录的计划补偿应用(否则状态 chip 与设备脱节,实测:M2S100 ok 迟到,chip 停在 OFF 而设备在报)。
  • 连接按钮按"意图"翻转:点击立即切换文本,实际开/关由串口线程异步完成, 已连接/已断开/丢失全部写入日志;期望连接但 2s 未建立按丢失处理回退按钮。 连接同步序列每步发送前确认在线(线程异步打开期间早发会被"未连接串口"丢弃)。
  • 时间戳在串口线程读取点打(FF485.on_frame(fr, t_rx)),不晚于 GUI 轮询消费—— 避免重蹈 SerialMate 的时间戳失真问题(见 E:\Hulk-Coding\HW-Debug-Tools\SerialMate)。
  • 发送入口幂等去重:同一命令已有未决应答时忽略重复发送(状态栏提示 "应答中, 已忽略重复发送")——真机实测连点"停止"12 次会把 8 条 pending 队列 挤爆("丢弃积压命令"刷屏);协议全幂等,同命令一条在途足够,超时自清后 自然允许重发。
  • 串口读取按到达量收,不攒大块:固定 read(256) 在持续帧流下要攒满 256 字节 才返回(Windows 串口"字节间隔超时"150ms 因帧间隔更短永不触发,100ms 上报时 ≈2.5s 一批),表现为曲线/命令全部 3 秒一卡、一股脑一大坨。已改为 read(in_waiting or 1),延迟回到 ≤150ms 量级(v0.2.2,真机实测确认)。
  • 日志裁剪走原生 setMaximumBlockCount(500):手工 textCursor 裁剪用了 cursor.Start/cursor.BlockUnderCursor 实例级枚举访问,PySide6 6.11 已移除 (类上仍在),必然抛 AttributeError——日志超 500 行(100ms 上报 ≈50s)后每个 100ms tick 在 _log 处异常中止:_tick 后半段(应答超时清理/连接状态机/统计 节流)永不执行(pending 永不清 → "停止"点击被幂等去重永久拦截)、RX 队列积压 ratchet(帧处理滞后涨到 1~2.7s,日志/chip/曲线显示全面滞后)、日志无限增长。 v0.2.8 改原生环形裁剪修复(离屏 A/B 实测:修复后 6 分钟漂移 ≤15ms、零异常、 零积压;selftest 新增 3 条回归用例)。
  • 日志自动滚动仅在贴底时跟随:上报运行时可上翻查看历史(每行强制滚底会抢 滚动条);CSV 写失败(磁盘满/记录盘被拔)自动停止记录并提示——_csv_write 在 _tick 帧处理路径上,未捕获异常会中止主循环(v0.2.8 日志裁剪同款教训);故障 标记封顶 1000 条;冻结视图切片右缘二分(暂停时长不增加渲染成本);串口写超时 只丢该条命令、链路保留(不按设备拔出断链)。均 v0.2.9。
  • Fluent 控件自定义样式必须走 setCustomStyleSheet(覆盖 hover/pressed/disabled), 直接 setStyleSheet 会在切主题时被库重刷(DLPilot 同款坑);标题字号走 QFont。
  • 图表刷新双节流:数据到达即重绘(≤10Hz)+ 0.5s 时间轴推进;“全部”窗口超 4000 点 按步进抽稀(NaN 断口保留)。
  • 是否有数据由绘制时实时判定(paintEvent 直接看数据列表引用):图表只在 构建时调用一次 set_source,旧版在这里缓存过 _has_data 布尔——数据经引用 追加后永不刷新,“暂无数据”提示文字在有数据后仍绘在曲线上(实测截图暴露)。 手动 Y 轴模式下曲线/目标线裁剪回绘图区(setClipRect),当前值标记 超出量程时不画(实时读数卡片仍可见)。v0.2.10。
  • Y 轴滑条的两套坐标系别混用:把手像素位置在参考域(全量数据范围 ±15%,_y_ref)上映射,与"显示量程"是两回事;拖拽中的界值必须取实时 显示范围(manual=_y_manual,auto=当前自适应值),不能取绘制缓存 _cached_layout 的陈旧范围——连续两次拖拽之间没有重绘,实测曾把 pan 的 量程整段拖跳(v0.2.11)。

已知事项

  • 当前 USB-RS485 适配器自动换向偶发丢响应/大延迟(固件侧已立”适配器更换”TODO): 查询 1.2s 超时提示后重发即可;设定命令的晚到 ok 已自动补偿应用;曲线走上报帧 (设备单向推送),不受影响。
  • 加热(M3S1)为真实 PID 闭环:开启后模具真实升温(NTC 故障/过温 150℃ 固件自动 断电保护,上电默认关闭);当前占空比无查询命令,从温度曲线间接观察。