写在前面
最近做了一个 AZBIL C2/C3 温控项目,原本只是想把设备通讯先接起来,结果一路演进下来,最后沉淀成了:
- 一份协议整理文档
- 一个可复用的 Python 通讯库
- 一套 YAML 工艺配置机制
- 一套 CLI 调试工具
- 两条不同技术路线的 Dashboard
回头看,这次最有价值的不是“做完了多少功能”,而是整个开发过程越来越像产品化沉淀,而不是一次性脚本。
这篇文章主要复盘这次协作中最值得保留的开发思路。
一、第一步不是写代码,而是先确认资料是否可靠
项目一开始其实就踩了一个很典型的坑:
最先拿到的 PDF 并不是正式说明书,而是样本文件。
这个问题如果没及时发现,会直接导致后续实现建立在错误前提上。尤其对工控设备来说,样本文件和正式说明书的差别非常大:
- 样本文件适合看产品概览
- 正式说明书才能作为协议实现依据
比如这次项目里,真正关键的内容都必须回到正式说明书确认:
CPL私有协议帧结构Modbus ASCII / RTU功能码- 校验方式
- 时序要求
- RAM 地址和非易失性地址的区别
- 程序段访问区的真实地址
这一阶段最大的收获不是技术,而是一个简单但经常被忽略的原则:
资料源不可靠时,越早承认,返工越少。
二、先把“地址语义”讲清楚,再做抽象
这次还有一个特别值得记住的小细节。
最初给到的一张现场地址截图里,7190 / 7191 被标记成了 SP1 / Time1。但按原厂手册,真正的对应关系是:
7178 / 7179:第 1 段7184 / 7185:第 2 段7190 / 7191:第 3 段
也就是说,截图里的命名和原厂编号并不一致。
后来进一步沟通后,这个差异才彻底说通:
- 从原厂编号看,
7190 / 7191的确是第 3 段 - 从现场工艺习惯看,真正给人工调的主工艺也是从第 3 段开始
- 第 1、2 段更多是保护性缓升段,首次调好之后就不再给普通用户改
这件事直接影响了后续所有设计:
- YAML 配置从第 3 段开始更符合现场思维
- 程序曲线可视化也应该把第 3 段作为主工艺起点
- 程序段编辑和帮助说明不能简单照着截图命名
所以这一步其实是在做一件非常基础、但非常关键的事:
先把设备世界里的真实语义理顺,再谈代码里的抽象。
三、先做标准库,不要一上来就做界面
协议和地址理顺后,项目的交付目标很快被收敛成了两块:
- 协议整理文档
- 标准通讯库
这一步的判断我现在觉得非常正确。
因为对于这种项目,真正长期有价值的,不是某个页面,而是底层这几层能力:
- 总线层
- 设备层
- 寄存器层
- 枚举层
- 协议适配层
最后沉淀出来的结构大致就是这样:
bus.py:串口和总线抽象device.py:单台设备操作对象registers.py:常用寄存器和地址公式enums.py:RUN/READY、AUTO/MANUAL等语义化枚举protocols/:Modbus与CPLcli.py:现场调试入口
为什么这一步重要?
因为后面不管是 Web、桌面端,还是其他 Python 项目集成,复用的都不是界面,而是这些标准层。
换句话说:
标准层做对了,UI 只是换皮;标准层没做好,后面接什么都容易返工。
四、把工艺参数从代码里抽出来,交给 YAML
项目推进到一半时,一个很重要的需求变得越来越清晰:
用户真正关心的,不是某个寄存器怎么写,而是:
- 不同项目能不能换一套控温曲线
- 不同工艺能不能不改代码
- 调机完成后能不能固化下来,避免现场乱改
于是后面就把“设备通信”和“工艺配置”彻底拆开了。
最终的设计是:
设备层负责什么
- 串口
- 协议
- 从站
- 寄存器读写
- 状态读取
工艺层负责什么
- 程序号
- 起始段
- 各段
SP / 时间 / PID组 - PID 参数
- 需要初始化写入的补充寄存器
对应的 YAML 结构也就顺理成章了:
device:
protocol: modbus_rtu
port: COM3
slave: 1
baudrate: 9600
parity: E
stopbits: 1
timeout: 2.0
profile:
name: example-oven
description: Main heating profile
program_no: 1
active_segment_start: 3
pid_groups:
- group: 1
kp: 120
ti: 180
td: 45
segments:
- segment: 3
sp: 120
time: 300
pid_group: 1
- segment: 4
sp: 180
time: 600
pid_group: 1
这一步之后,整个项目的可维护性明显上了一个台阶:
- 不同项目只换 YAML
- 首次调试后可以固化配置
- 后续可做“保存配置 / 下发配置 / 校验配置”
- 方案可以归档和追溯
如果说前面是在做“驱动”,那从这里开始,已经是在做“平台基础设施”了。
五、CLI 先行,是最稳妥的现场验证方式
在做图形界面之前,先补 CLI,这个顺序后来被证明非常值。
原因很简单:
- CLI 是最薄的验证层
- 出问题时最容易看出是设备问题、协议问题还是逻辑问题
- 它本身也是非常实用的现场工具
后面逐步补上的 CLI 能力包括:
readread-wordswritescanliveprogram-statusdumpapply-profileverify-profile
其中 dump 很有意思,它其实相当于一个“纯文本版控制台”。
也是在它出现以后,很多 UI 设计的取舍才变得更清楚:
- 哪些数据值得一屏盯着看
- 哪些设置不应该占主界面
- 哪些帮助说明应该单独放一页
这一步带给我的一个经验是:
图形界面之前先做 CLI,不是多此一举,而是帮 UI 探路。
六、第一条界面路线:React + FastAPI
第一版 Dashboard 选择的是:
- 前端:
React + Vite - 后端:
FastAPI
为什么先选它?
因为它特别适合快速验证交互结构:
- 页面怎么分
- 哪些卡片该保留
- 程序曲线怎么画
- 程序段怎么编辑
- 配置方案怎么选择
- PID 放哪里更合适
这一版的最大价值,不在于最终部署,而在于它帮我们迅速把“界面逻辑”试出来了。
但它也暴露了几个很真实的问题。
1. 一屏放太多内容,主次不清
最早的版本几乎把所有东西都塞在一页里:
- 实时数据
- 程序曲线
- 配置编辑
- 串口设置
- PID
- 说明文字
结果就是页面很满,但并不好用。
后来改成按功能分屏后,体验一下就顺了很多:
实时总览程序曲线系统设置帮助说明
2. 工艺配置不应该被塞进右侧窄栏
一开始把“程序配置编辑”放在侧栏里,结果输入框、表头、按钮都被挤坏了。
后来调整成:
- 顶部:曲线预览 + 方案选择
- 中间:程序段表
- 下方:整行宽配置编辑
这个改动让我很认同一个简单判断:
真正的主任务,不能被做成附属区块。
3. 配置文件编辑和程序段参数设置本质重复
这也是一个典型的“越做越发现重复”的例子。
如果用户已经在编辑配置文件:
- 再单独做一套程序段参数卡片
那实际上就是两套入口。
后来就统一成了一个思路:
- 只保留“配置方案编辑”
- 编辑后可直接“保存”
- 或者“保存并下发”
这样逻辑一下就清楚了。
七、第二条界面路线:FastAPI 单体版
虽然 React + FastAPI 很适合做交互验证,但很快就遇到了一个特别现实的问题:
用户主要在 Windows 电脑端长期使用,希望部署更轻,不想额外依赖 Node.js。
这个反馈让我意识到:
界面技术栈不该只看“开发舒适度”,还要看“交付场景”。
所以后来又开了第二条路线:
FastAPI- 原生
HTML / CSS / JS Chart.js- 内嵌静态资源
- 单体部署
最后做成了一个很清晰的模型:
pip installazbil-dashboard- 浏览器访问本机地址
这条路线的好处非常直接:
- 不依赖 Node 运行环境
- 不依赖外网 CDN
- 更适合 Windows 现场电脑
- 更像一个单机上位机工具
这让我越来越觉得:
不是技术越“现代”越好,而是越贴近实际部署场景越好。
八、后期最重要的优化,其实不是视觉,而是语义统一
项目做到后面,真正决定“能不能好用”的,反而不是配色或布局,而是语义是否统一。
这里有几个特别典型的点。
1. 配置文件名和配置名称不一致,会让用户混乱
一开始界面里同时存在:
- 配置文件名
profile.name
如果另存为后这两个不同步,用户会非常容易困惑:
- 我现在改的是哪个文件?
- 当前方案到底叫什么?
最后的简化规则是:
- 当前编辑对象就是当前选中的配置文件
- 另存为时默认同步显示名
- 历史 YAML 中不一致的也统一清理
这个问题看起来小,但它解决的是认知负担。
2. PID 不该抢占普通用户主流程
从现场使用视角看,普通用户最关心的是:
- 当前温度
- 目标温度
- 程序段
- 剩余时间
- 控温曲线
而不是 KP / TI / TD。
所以后面 PID 被后置到了“系统设置”页,作为工程参数处理。
这是一个很典型的角色分层:
- 普通操作
- 工艺配置
- 工程参数
3. 时间单位必须统一
这个坑特别值得单独记一笔。
一开始页面里的时间显示其实不一致,出现过:
300300s5h 0m
这些表现。
从程序逻辑上看或许都能解释,但对用户来说非常容易误导。
最后统一后的原则非常明确:
- 整个 Dashboard 统一按 分钟
- 不再自动折算成小时
- 直接显示
300 min - 曲线、程序段表、帮助说明、文档同步一致
这件事再次说明:
工业界面最怕的不是丑,而是同一个量出现多种语义。
九、这次最值得保留的方法论
如果把这次协作抽象成一套以后还能复用的方法,我最想保留的是下面几条。
1. 先协议,后界面
顺序上一定是:
- 先确认资料源
- 先整理协议
- 先做标准库
- 先抽配置机制
- 先做 CLI
- 最后做 Dashboard
这个顺序几乎决定了返工量。
2. 先做可复用,再做可视化
真正长期有价值的不是页面,而是:
busdeviceregistersprofilecli
UI 只是这些能力的一种承载形式。
3. 可以用两条技术路线分别解决“验证”和“交付”
这次两条分支特别有代表性:
codex/dashboard-ui- 更适合验证交互结构
codex/dashboard-fastapi-static- 更适合最终交付与部署
这不是浪费,而是一种高效迭代方式:
- 先验证方向
- 再收敛交付模型
4. 用户觉得“别扭”的地方,往往最值得优先处理
这次很多关键优化都不是技术错误,而是用户体验层面的“不顺手”:
- 一屏太挤
- 曲线太高
- 配置编辑放错位置
- 说明文字太抢眼
- 文件名和配置名不一致
- 时间单位不统一
这些反馈如果忽略,系统即使功能完整,也很难真正落地。
十、最后沉淀下来的,其实是一套基础设施
如果只看最终产出,这次项目沉淀下来的东西已经不只是某个控制页面了,而是一整套温控项目基础设施:
- 协议文档
- 使用文档
- Python 通讯库
- 寄存器目录
- 枚举与状态封装
- YAML 配置机制
- CLI 调试工具
- React 版 Dashboard
- FastAPI 单体版 Dashboard
从这个角度看,这次协作真正完成的是:
从设备协议理解,到标准层抽象,再到配置驱动和界面落地的一整条链路。
结语
回头复盘,这次最让我认可的,不是“做了很多功能”,而是整个开发思路越来越稳:
- 从样本文件纠偏到正式说明书
- 从寄存器堆砌到标准层抽象
- 从手工设置到 YAML 配置化
- 从单页堆功能到按角色与任务分屏
- 从技术偏好到部署场景优先
如果以后再做类似的工控或上位机项目,我仍然会优先坚持这套思路:
先厘清协议,先做标准层,先让系统可复用,再去追求界面的完整和漂亮。
这次项目最大的收获,也许并不是“做成了一个 Dashboard”,而是把一套能反复复用的开发方法走通了。