✳相遇
导航

从零到架构师:以 PU/FTU 配电终端为样本的完整系统学习路径

7953 字
40 分钟
从零到架构师:以 PU/FTU 配电终端为样本的完整系统学习路径

从零到架构师:以 PU/FTU 配电终端为样本的完整系统学习路径#

目标不是“把代码看完”,而是建立一套从业务问题、质量属性、系统边界、架构分层、数据模型、并发模型、故障模型,到构建、测试、部署和演进的完整设计能力。

写在前面:架构师到底要会什么#

架构师不是画框图的人,也不是写代码最多的人。一个合格的架构师需要同时回答五类问题:

  1. 系统为什么存在:解决什么业务问题,边界在哪里。
  2. 系统如何工作:数据、控制、时间和故障如何在系统中流动。
  3. 系统为什么这样设计:每个关键决策牺牲了什么,又换来了什么。
  4. 系统如何证明可靠:怎样构建、测试、观测、发布、回滚和恢复。
  5. 系统如何持续演进:新需求怎样加入而不破坏已有行为。

PU/FTU 工程是一个很好的学习样本。它同时包含嵌入式 Linux、C 语言、多线程、数据库、实时数据、工业通信协议、硬件设备、看门狗、升级和故障恢复。把这类工程真正吃透,得到的不是某个模块的经验,而是一套可以迁移到网关、机器人、工业控制器、边缘计算节点和高可靠服务端系统的方法。


第一部分:建立系统全景#

1. 从业务问题开始,而不是从目录开始#

配电终端的核心任务,可以抽象为四件事:

  1. 感知现场:采集开关状态、电压、电流、功率、电量、温度和设备状态。
  2. 做出处理:计算、越限判断、故障判断、保护逻辑和状态聚合。
  3. 对外通信:通过 IEC 60870-5-101/104 等协议向主站上送数据、接收参数和控制命令。
  4. 持续可靠运行:断网、掉电、数据库损坏、线程退出和升级失败时仍能恢复。

由此可以得到系统的顶层输入与输出:

输入
├─ 模拟量:电压、电流、功率、温度
├─ 开关量:开关位置、告警、硬压板
├─ 通信报文:总召、对时、参数、遥控
├─ 本地操作:液晶、维护工具
└─ 系统事件:启动、升级、网络变化、线程退出
处理
├─ 采集与标准化
├─ 数据质量判断
├─ 状态机与业务规则
├─ 数据持久化和历史归档
├─ 协议编码/解码
└─ 故障检测与恢复
输出
├─ IEC101/104 上送
├─ 遥控执行
├─ 参数更新
├─ 液晶展示
├─ 日志、SOE、冻结和录波
└─ 重启、告警和升级结果

架构分析的第一个产物应是“系统上下文图”,而不是代码调用图。

2. 明确系统边界#

PU 应用内部负责:

  • 业务线程管理;
  • 参数、实时和历史数据管理;
  • 通信规约实现;
  • 通道与设备适配;
  • 系统配置、对时和网络管理;
  • 液晶与维护接口;
  • 应用级故障恢复。

PU 应用外部依赖:

  • Linux 内核与设备驱动;
  • 串口、SPI、RTC、PPS、看门狗等设备节点;
  • GPSD、chrony 等系统服务;
  • SQLite、OpenSSL、FFTW 等库;
  • 启动、守护和升级脚本;
  • 主站、维护工具和现场硬件。

边界决定责任。如果驱动没有产生 PPS 信号,应用不能假装它能通过重试解决;如果数据库事务没有提交,协议线程也不能靠重复发送修复数据一致性。架构师必须明确每一类故障属于哪一层。

3. PU 工程的顶层架构#

┌──────────────────────────────────────────────┐
│ 主进程 │
│ PU.c:启动、线程编排、退出策略、看门狗 │
└──────────────────────┬───────────────────────┘
│ 按数据库配置启动线程
┌──────────────────────▼───────────────────────┐
│ 业务线程层 │
│ 通道管理 / 系统配置 / IEC101 / IEC104 │
│ 液晶 / 维护 / GPS / 电源 / 组合遥信 │
└───────────────┬──────────────────┬───────────┘
│ │
┌───────────────▼──────────┐ ┌─────▼───────────┐
│ 数据与领域层 │ │ 协议适配层 │
│ 参数/遥信/遥测/电量/遥控 │ │ ASDU/链路/映射 │
│ 实时事件/历史/转发表 │ │ 安全和运维协议 │
└───────────────┬──────────┘ └─────┬───────────┘
│ │
┌───────────────▼──────────────────▼───────────┐
│ 基础设施层 │
│ SQLite / pthread / socket / serial / SPI │
│ 文件系统 / 网络 / GPSD / watchdog / 脚本 │
└──────────────────────────────────────────────┘

这是一种“单进程、多业务线程、共享数据库、外部脚本守护”的架构。

它的优点是部署简单、跨模块共享数据方便、进程间通信成本低;代价是线程隔离弱、共享资源容易耦合、单个严重故障可能拖垮整个进程。因此工程额外使用线程退出码、关键线程分类、局部重启、进程重启和看门狗来补偿隔离不足。


第二部分:从零理解底层原理#

4. C 程序从源码到运行的全过程#

必须真正理解下面这条链路:

.c/.h 源码
→ 预处理:展开 #include、宏和条件编译
→ 编译:C 代码变为汇编
→ 汇编:汇编变为目标文件 .o
→ 链接:目标文件和库合成为 ELF 可执行文件
→ 加载:Linux 映射代码段、数据段、堆栈和动态库
→ 运行:从入口进入 main()

需要掌握的基础:

  • 声明与定义;
  • 头文件保护和链接可见性;
  • static、extern、符号表;
  • 栈、堆、全局区和只读区;
  • 指针、数组、结构体和对齐;
  • 位域、大小端和二进制协议;
  • 静态库、动态库和运行时加载;
  • DEBUG_SQDB、X86BPS、RELEASE 条件编译。

实践验收#

你应该能做到:

  1. 从某个函数追到它最终链接自哪个目标文件。
  2. 解释同一份源码为何在 x86 和 ARM 上行为不同。
  3. 用预处理结果解释某段条件编译代码是否存在于最终程序。
  4. 看到未定义符号时判断是缺源码、缺库还是声明不一致。

5. Linux 进程、线程和调度#

PU 是单进程多线程系统。进程拥有独立虚拟地址空间,线程共享代码、堆、全局变量和打开的文件描述符,但每个线程有自己的栈和调度状态。

共享带来效率,也带来三类问题:

  1. 竞争条件:两个线程同时读写同一状态。
  2. 死锁:线程以不同顺序持有多把锁。
  3. 可见性:编译器和 CPU 重排导致另一个线程看不到预期顺序。

工程中用到的典型手段:

  • pthread_mutex_t:保护复合状态;
  • 递归互斥锁:允许同线程重复进入,但容易掩盖设计问题;
  • C11 原子类型:适合计数器和简单状态;
  • pthread_once:一次性线程安全初始化;
  • pthread_tryjoin_np:主线程非阻塞检查业务线程退出。

架构师不只要问“有没有加锁”,还要问:

  • 被保护的不变量是什么?
  • 锁的持有范围有多大?
  • 是否在持锁时执行数据库、串口或文件 IO?
  • 锁顺序是否全局一致?
  • 线程退出时是否可能遗留锁、事务和文件描述符?

实践验收#

为工程画一张共享资源表:

资源写线程读线程同步方式失败影响
参数数据库系统/维护/规约多个业务线程SQLite + 参数锁配置不一致
实时数据库采集/业务线程规约/液晶SQLite WAL实时数据延迟
串口描述符通道管理规约/模块通道锁通信中断
SPI 安全芯片通道/安全模块多线程SPI 锁加密通信失败

如果不能明确写出资源所有者,就还没有真正理解并发架构。

6. 文件描述符和设备抽象#

在 Linux 中,文件、串口、socket、设备节点、管道都可以被抽象为文件描述符。应用通过 open/read/write/ioctl/close 与内核交互。

关键原理:

  • 文件描述符只是进程内的整数索引;
  • read/write 的短读短写是正常情况;
  • 阻塞、非阻塞、超时和事件驱动是不同模型;
  • ioctl 用于设备特有控制;
  • 设备消失、驱动重置和网络断开都会使旧描述符失效;
  • 关闭和重开必须与其他线程协调。

PU 对串口、SPI、GPS、看门狗的处理,本质都是资源生命周期设计:创建、配置、使用、错误检测、关闭、重连和最终释放。

7. 网络与工业协议的底层模型#

IEC101 和 IEC104 在业务语义上接近,但承载层不同:

  • IEC101 通常运行在串行链路上;
  • IEC104 将 IEC60870 应用层承载在 TCP/IP 上。

理解协议时要分四层:

  1. 传输层:字节如何可靠到达。
  2. 链路层:连接、确认、序号、超时、重发。
  3. 应用层:ASDU 类型、传送原因、公共地址、信息体地址。
  4. 领域语义:遥信、遥测、遥控、总召、对时、参数和文件。

协议实现的核心不是 switch(type),而是状态机:

未连接
→ 建立连接
→ 链路启动
→ 可传输
→ 总召/周期/变化上送
→ 异常、超时或关闭
→ 清理并重连

架构设计必须明确:

  • 每个状态允许什么输入;
  • 输入产生什么状态迁移和副作用;
  • 超时由谁维护;
  • 重复报文是否幂等;
  • 断线后哪些数据需要补发;
  • 时间、品质和点号如何映射。

8. SQLite 的底层设计意义#

SQLite 不是简单的“存数据工具”。在 PU 中它承担了四个架构角色:

  1. 配置中心:通道、装置、参数和规约配置。
  2. 共享状态总线:线程通过实时库交换数据。
  3. 查询与映射引擎:视图和转发表把内部点映射为协议点。
  4. 历史容器:日志、SOE、冻结和录波元数据。

必须理解:

  • ACID 与事务边界;
  • WAL 的读写并发模型;
  • busy timeout 只能缓解锁冲突,不能修复错误事务设计;
  • prepared statement、参数绑定和 SQL 注入;
  • 索引为何加速读取又增加写入成本;
  • 视图是查询抽象,不是数据副本;
  • /dev/shm 数据在重启后会消失;
  • 数据库升级是一种模式演进,而不是复制文件。

第三部分:理解现有系统的设计思路#

9. 配置驱动,而不是硬编码驱动#

主程序不是直接写死“启动几个 104 线程”,而是从参数数据库读取通道的规约类型,再查映射表启动对应业务线程。

这是配置驱动架构:

数据库配置
→ 规约名称
→ 线程启动规格
→ 业务入口

优点:

  • 同一程序适配不同设备配置;
  • 通道启停和规约选择可配置;
  • 设备模型和协议实现解耦。

代价:

  • 配置错误变成运行时错误;
  • 字符串名称形成隐式契约;
  • 数据库版本必须与程序兼容;
  • 启动前必须验证配置完整性。

架构改进方向是把隐式字符串契约变成可校验的模式、枚举、版本和启动诊断报告。

10. 参数库、实时库、历史库分离#

工程没有把所有数据塞进一个数据库,因为不同数据的质量属性不同:

数据主要特点设计目标
参数低频写、必须持久、要升级兼容正确性和可恢复
实时高频更新、断电可重建、并发读取低延迟和吞吐
历史持续增长、需要查询和归档容量控制和可追溯

这是按数据生命周期分区,而不只是按业务模块分表。

参数库先在临时文件中完整创建,再整体转正;实时库由参数库派生。这体现了两个重要设计原则:

  1. 构建成功后再发布,避免半成品状态。
  2. 派生数据可重建,源数据才需要长期保存。

11. 转发表:领域模型与协议模型之间的桥#

现场内部点号、数据库点号和主站协议点号不一定相同。转发表承担映射:

内部装置 + 分类 + 点号
→ 转发序号
→ 协议信息体地址
→ 类型、系数、取反、位宽、单位

它本质上是 Anti-Corruption Layer:隔离内部模型与外部协议模型。

架构师必须关注映射规则的:

  • 唯一性;
  • 可追溯性;
  • 版本兼容;
  • 导入校验;
  • 运行时性能;
  • 变更后的缓存失效。

12. 线程故障分级与恢复#

工程把线程分为关键和非关键,并定义三种退出语义:

  • 正常退出;
  • 请求线程级重启;
  • 请求进程级重启。

非关键线程可以局部重启,但短时间内多次失败会升级为进程重启;关键线程异常通常直接请求进程重启。进程外还有守护脚本和硬件看门狗。

这形成多级恢复链:

函数返回错误
→ 业务线程自行恢复
→ 主进程局部重启线程
→ 主进程退出
→ 守护脚本重启进程
→ 硬件看门狗复位系统

架构上的关键不是“多重启几次”,而是避免恢复机制互相打架。每一级都要有明确触发条件、次数限制、退避策略和日志证据。

13. 脚本作为应用生命周期的一部分#

Release 不只是一个二进制文件。完整运行系统包括:

  • 稳定入口脚本;
  • 升级脚本;
  • 应用守护脚本;
  • 内核模块加载;
  • 应用二进制;
  • 配置和数据库;
  • 日志及升级包。

因此“应用架构”必须覆盖进程外生命周期。只看 main() 不看启动和升级脚本,得到的是残缺架构。


第四部分:架构师的分析方法#

14. 用四种流读系统#

不要按文件顺序阅读。架构师应沿四种流分析:

14.1 控制流#

谁启动谁,谁调用谁,异常时控制权回到哪里。

14.2 数据流#

数据从设备、报文或用户输入进入后,经过哪些结构、数据库和转换,最终输出到哪里。

14.3 时间流#

哪些逻辑由周期、超时、冻结时刻、SOE 时间戳和看门狗驱动。

14.4 故障流#

错误如何产生、传播、记录、隔离、重试、降级和恢复。

对每个核心用例,都画出这四种流。这样得到的是系统行为,不只是静态模块图。

15. 用质量属性驱动架构#

架构不是按“模块看起来整齐”来评判,而是按质量属性:

可靠性#

  • 线程退出能否恢复?
  • 数据库损坏能否检测?
  • 升级失败能否回滚?

实时性#

  • 遥信变化到主站上送的最坏延迟是多少?
  • 数据库锁冲突是否影响周期任务?

一致性#

  • 参数预置、确认和固化是否原子?
  • 遥控选择与执行是否绑定同一操作者和同一值?

可维护性#

  • 新增规约需要改多少地方?
  • 表字段修改是否会破坏旧版本?

可观测性#

  • 现场只有日志时能否还原故障链?
  • 是否能区分驱动、通道、协议、数据库和业务失败?

安全性#

  • 外部输入是否校验?
  • 动态 SQL、文件路径、升级包和命令执行是否可信?
  • 控制命令是否鉴权、审计和防重放?

16. 用架构决策记录代替口头经验#

每个重要决策写成 ADR:

标题:为什么实时库放在 /dev/shm
背景:实时数据高频写,重启后可由参数库重新生成。
决策:实时数据库放在内存文件系统,启用 WAL。
收益:减少闪存写入,提高并发读取性能。
代价:掉电后实时数据丢失,启动流程必须可靠重建。
替代方案:单一持久数据库、共享内存、自定义内存表。
验证:启动重建时间、写吞吐、锁等待、掉电恢复测试。

架构师的价值很大一部分体现在“让未来的人知道为什么”。


第五部分:完整学习路线#

17. 阶段 0:建立安全实验环境#

目标:所有学习和试验可重复、可回滚、不触碰真实设备数据。

任务:

  1. 保留只读的原始工程副本。
  2. 在独立本地副本或临时目录中实验。
  3. 禁止实验程序访问真实 /userdata。
  4. 禁止执行可能 reboot、insmod、改密码或改网络的脚本。
  5. 为临时数据库、日志和模拟设备指定独立目录。

验收产物:

  • 实验环境说明;
  • 允许和禁止的命令清单;
  • 数据清理与恢复方法。

18. 阶段 1:补齐计算机系统基础#

学习内容:

  • C 编译链接;
  • 内存布局与指针;
  • Linux 进程线程;
  • 文件描述符与系统调用;
  • TCP、串口和二进制协议;
  • SQLite 事务与索引;
  • shell 启动和进程守护。

实践:

  1. 写一个两线程生产者/消费者程序。
  2. 写一个带超时和重连的 TCP 客户端。
  3. 写一个 SQLite WAL 并发读写实验。
  4. 写一个简单二进制帧编码/解码器。
  5. 用脚本守护一个故意退出的小程序。

通过标准:能解释每个实验的正常路径、失败路径和资源清理路径。

19. 阶段 2:跑通构建链#

学习顺序:

makefile
→ sources.mk
→ 各目录 subdir.mk
→ 编译参数
→ 目标文件
→ 链接库
→ 可执行文件
→ Release 打包脚本

当前工程的首要阻塞是构建配置引用缺失的 src/proctl/ 和 src/lineLosses/。在确认来源前,不应通过删除构建项制造“假成功”。

验收产物:

  • 构建依赖清单;
  • 三种构建配置差异表;
  • 从干净环境构建的步骤;
  • 所有外部库及版本;
  • 一份可复现的构建日志。

20. 阶段 3:读懂主进程生命周期#

阅读顺序:

  1. src/PU.c 的 main()。
  2. 看门狗初始化和运行阶段切换。
  3. sysConfigInit()。
  4. 参数库和实时库打开。
  5. 通道到线程的映射。
  6. 线程退出与恢复。
  7. Release 守护脚本。

实践问题:

  • 启动失败共有多少出口?
  • 哪些失败会退出进程,哪些会重试?
  • 看门狗在启动和运行阶段的超时为何不同?
  • 正式程序和仿真程序为何要互斥?
  • 主进程退出后谁负责拉起?

验收产物:启动时序图、线程清单、退出码矩阵和恢复状态机。

21. 阶段 4:独立验证数据库架构#

先脱离硬件做数据库实验:

  1. 在临时路径创建参数库。
  2. 查看所有表、视图、索引和触发器。
  3. 插入默认装置和参数。
  4. 从参数库生成实时库。
  5. 初始化一个 SQ_DEVICE_NOTE。
  6. 完成参数、遥信、遥测、电量和遥控的最小读写闭环。
  7. 模拟事务失败和数据库损坏。
  8. 验证升级、备份和回滚。

验收标准:

  • 能画出数据库实体关系;
  • 能解释每张核心表的所有者;
  • 能证明转发表如何连接内部点和协议点;
  • 能用数据库快照证明写入前后变化;
  • 能演示失败后没有半成品库。

22. 阶段 5:打通一条遥信链路#

选择一个单点遥信,从头追踪:

硬件/模拟输入
→ 遥信采集结构
→ 实时遥信表
→ SOE 判定
→ 遥信转发表
→ IEC ASDU
→ 发送队列
→ 主站报文

必须记录:

  • 点号和编码;
  • 时间戳生成位置;
  • 品质位来源;
  • 变化检测逻辑;
  • SOE 与普通遥信的区别;
  • 断线时数据如何处理;
  • 重复上送是否可能发生。

23. 阶段 6:打通遥测和遥控#

遥测重点:

  • 原始值、工程值、归一化值和短浮点;
  • 系数、单位、零漂、死区和越限;
  • 极大值、极小值和冻结;
  • 周期上送与变化上送。

遥控重点:

  • 选择、执行、撤销;
  • 操作者与占用;
  • 值一致性;
  • 超时;
  • 命令落库和设备执行;
  • 结果反馈与审计。

遥控是安全关键路径。必须用状态机和不变量描述,不能只看几个函数调用。

24. 阶段 7:理解 IEC101/104#

学习顺序:

  1. 帧结构和地址长度。
  2. ASDU 类型和传送原因。
  3. 链路启动、测试和确认。
  4. 总召与周期数据。
  5. 遥信、遥测、遥控。
  6. 对时、参数、文件和安全扩展。
  7. 断链、重连、序号和超时。

实践:

  • 捕获并逐字节解释一帧总召;
  • 构造一帧单点遥信;
  • 验证错误公共地址和未知类型处理;
  • 模拟 TCP 半包、粘包、断线和重连;
  • 验证控制命令重复到达时的行为。

25. 阶段 8:理解通道和硬件适配#

围绕资源生命周期学习:

发现设备
→ 打开
→ 配置
→ 收发
→ 健康检查
→ 错误检测
→ 关闭
→ 退避重连

分别分析串口、4G、蓝牙、安全芯片、GPS、RTC、PPS 和看门狗。每个资源都写出所有者、共享者、锁、超时和重连策略。

26. 阶段 9:理解历史、日志和可观测性#

学习内容:

  • 普通日志与审计日志;
  • SOE;
  • 遥控记录;
  • 极值、定点、冻结和潮流变化;
  • COMTRADE 录波;
  • 文件目录和分段读取。

架构问题:

  • 日志是否包含时间、模块、通道、装置和错误原因?
  • 记录是否有容量上限和淘汰策略?
  • 现场故障是否能只靠日志还原?
  • 日志写入失败是否会影响主业务?

27. 阶段 10:理解升级和演进#

完整升级不是“覆盖二进制”:

发现升级包
→ 校验
→ 解压到临时目录
→ 部署文件
→ 执行一次性补丁
→ 检测关键脚本变化
→ sync
→ 必要时重启
→ 新程序启动
→ 数据库兼容检查与迁移

实践必须覆盖:

  • 包缺文件;
  • 解压中断;
  • 磁盘空间不足;
  • 脚本更新;
  • 程序更新但数据库迁移失败;
  • 重启后程序无法启动;
  • 回滚后旧程序读取新数据库。

第六部分:用测试把“看懂”变成“证明”#

28. 四层验证体系#

第一层:静态证据#

  • 模块依赖;
  • 函数调用;
  • 数据结构;
  • SQL 模式;
  • 线程和锁;
  • 条件编译。

静态阅读只能证明“代码看起来会这样运行”。

第二层:组件测试#

  • 数据库建库和迁移;
  • ASDU 编解码;
  • 时间转换;
  • 参数范围;
  • 状态机;
  • 文件格式。

第三层:集成测试#

  • 模拟主站与 IEC104 通信;
  • 模拟串口与 IEC101 通信;
  • 数据库与规约闭环;
  • 通道断开与恢复;
  • 升级与守护。

第四层:设备与故障注入#

  • 拔网线;
  • 串口无响应;
  • GPS 丢失;
  • 数据库锁冲突;
  • 磁盘写满;
  • 进程崩溃;
  • 线程退出;
  • 突然断电;
  • 升级中断。

只有四层证据相互支持,才能说真正了解系统。

29. 为每个结论设计反证实验#

不要只问“怎样证明它正确”,还要问“什么实验能证明我的理解是错的”。

例子:

结论:实时库可以由参数库重建。

反证实验:删除临时实时库,执行重建,比较表、视图、索引、触发器和默认数据。如果不能完全恢复,结论不成立。

结论:非关键线程退出不会导致整个进程退出。

反证实验:让该线程返回不同退出码并超过重启窗口,观察何时升级为进程级重启。

这种可证伪思维,是从“读代码”走向“做工程”的分界线。

30. 最小可用测试矩阵#

场景正常路径边界条件故障路径恢复验证
建库全新创建旧库存在创建中断临时库回滚
参数读写成功上下限类型错误旧值保持
遥信状态变化重复值数据库忙重试/告警
遥控选择执行超时用户冲突状态释放
IEC104建连总召半包粘包断网重连恢复
升级正常部署同版本包损坏保持旧版本

第七部分:从零设计一个新系统#

31. 第一步:写清需求和质量属性#

不要先选线程或数据库。先写场景:

当 1000 个遥信点中任一点变化时,
系统应在 200 ms 内完成时间标记、持久化并进入上送队列;
即使主站断线,事件也不得丢失;
恢复连接后按时间顺序补发;
重复上送不得导致主站状态错误。

这种场景同时定义刺激、环境、系统响应和度量,比“系统要高性能”有用得多。

32. 第二步:划分领域和边界#

推荐划分:

  • 设备接入域;
  • 数据采集域;
  • 实时状态域;
  • 参数配置域;
  • 控制命令域;
  • 协议通信域;
  • 历史与审计域;
  • 系统管理域;
  • 升级与生命周期域。

每个域定义:输入、输出、状态、所有者、不变量和失败模式。

33. 第三步:选择并发模型#

候选方案:

  1. 单进程多线程;
  2. 多进程隔离;
  3. 事件循环;
  4. Actor/消息队列;
  5. 混合模型。

选择依据:

  • 实时性;
  • 故障隔离;
  • 内存成本;
  • 数据共享频率;
  • 调试和部署复杂度;
  • 第三方库线程模型。

不要因为旧工程用了线程,新系统就一定继续用线程。

34. 第四步:设计数据所有权#

每份可变数据必须有唯一所有者。其他模块通过接口、消息或事务访问,而不是任意修改全局变量。

设计时明确:

  • 权威数据源;
  • 缓存和派生数据;
  • 一致性范围;
  • 事务边界;
  • 数据版本;
  • 迁移策略;
  • 保留与淘汰策略。

35. 第五步:设计接口和状态机#

接口不仅要定义参数,还要定义:

  • 前置条件;
  • 后置条件;
  • 幂等性;
  • 超时;
  • 错误语义;
  • 所有权;
  • 并发约束;
  • 版本兼容。

对连接、遥控、升级、参数预置等行为,用状态机表达,不要依靠散落的布尔变量。

36. 第六步:先设计故障,再设计成功#

对每个外部依赖写 FMEA:

依赖故障检测局部影响恢复最坏结果
参数库损坏integrity_check无法启动备份恢复配置丢失
主站网络断开socket 错误/超时暂停上送退避重连事件积压
串口无响应超时单通道失败重开设备数据中断
看门狗喂狗失败write/ioctl 错误系统可能复位记录并退出整机重启

37. 第七步:设计可观测性#

至少规划:

  • 结构化日志;
  • 关键指标;
  • 线程和通道健康状态;
  • 数据库延迟和锁等待;
  • 重连次数;
  • 队列深度;
  • 最近错误;
  • 构建版本和配置版本;
  • 现场诊断导出包。

38. 第八步:设计发布、升级和回滚#

构建产物必须可追溯:源码版本、工具链、编译参数、依赖库、配置模式和升级脚本属于同一个版本单元。

升级设计需要回答:

  • 包如何签名与校验;
  • 是否原子部署;
  • 数据库如何前向和后向兼容;
  • 失败如何回滚;
  • 谁决定重启;
  • 如何确认升级成功;
  • 如何避免无限重启。

第八部分:16 周学习计划#

第 1~2 周:C、Linux 和构建#

产物:编译链说明、内存实验、线程实验、文件描述符实验。

第 3 周:工程全景#

产物:上下文图、模块图、外部依赖表、质量属性清单。

第 4 周:主进程与生命周期#

产物:启动时序图、线程表、退出码矩阵、看门狗状态图。

第 5~6 周:数据库#

产物:ER 图、建库顺序、转发表说明、临时库闭环测试。

第 7 周:遥信#

产物:一条遥信端到端数据流和测试证据。

第 8 周:遥测与历史#

产物:值转换、越限、冻结、极值和历史容量模型。

第 9 周:遥控与参数#

产物:遥控状态机、参数预置/固化事务模型和异常测试。

第 10~11 周:IEC101/104#

产物:链路状态机、ASDU 手工解析、总召和断链恢复测试。

第 12 周:通道与硬件#

产物:设备资源生命周期图、锁与重连策略表。

第 13 周:升级与守护#

产物:升级时序、失败矩阵、回滚方案和看门狗协同说明。

第 14 周:安全与可观测性#

产物:威胁模型、审计清单、指标和现场诊断方案。

第 15 周:架构评审#

产物:至少 5 份 ADR、技术债清单、演进优先级。

第 16 周:从零设计#

设计一个简化的新配电终端系统,提交完整架构包并接受评审。


第九部分:架构师毕业标准#

39. 你必须能独立交付的文档#

  • 系统上下文图;
  • 容器/进程/线程图;
  • 模块依赖图;
  • 启动和关闭时序图;
  • 核心数据流;
  • 数据模型与数据生命周期;
  • 状态机;
  • 故障模型与恢复矩阵;
  • 安全威胁模型;
  • 测试策略;
  • 部署与升级架构;
  • ADR;
  • 容量与性能预算;
  • 技术债与演进路线。

40. 你必须能回答的问题#

  1. 为什么选择这种进程和线程模型?
  2. 哪些数据是权威数据,哪些可以重建?
  3. 最关键的系统不变量是什么?
  4. 单点故障在哪里?
  5. 最坏延迟和容量上限如何计算?
  6. 升级失败如何恢复?
  7. 新增一个规约会影响哪些层?
  8. 如何证明系统在断网、断电、数据库损坏时仍符合要求?
  9. 哪些决策是业务约束,哪些只是历史偶然?
  10. 如果重做一次,哪些设计保留,哪些替换,为什么?

41. 最终毕业项目#

从零设计一个可运行的简化终端:

  • 至少 100 个遥信点、100 个遥测点和 20 个遥控点;
  • 配置驱动;
  • 参数库、实时状态和历史事件;
  • TCP 模拟主站;
  • 总召、变化上送和遥控状态机;
  • 线程或事件循环并发模型;
  • 断线重连;
  • 看门狗或守护进程;
  • 可升级、可回滚;
  • 自动测试和故障注入;
  • 完整架构文档。

评审不只看能否运行,还要看:

  • 决策是否有依据;
  • 边界是否清晰;
  • 数据所有权是否明确;
  • 故障是否被隔离;
  • 测试是否能证伪;
  • 系统是否可演进。

结语:从“会改代码”到“能设计系统”#

真正的成长顺序是:

看见代码
→ 理解模块
→ 追踪行为
→ 识别不变量
→ 建立故障模型
→ 用测试证明
→ 解释设计权衡
→ 从零做出新的系统

不要以“读完多少文件”衡量进度。应该以“能否画清、讲清、测清、推翻自己的错误理解,并重新设计”衡量。

当你能够从业务场景出发,主动定义质量属性、系统边界、数据所有权、并发模型、状态机、故障恢复、可观测性和升级策略,并用实验和数据证明这些决策时,你就不再只是模块开发者,而是在真正做架构。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

从零到架构师:以 PU/FTU 配电终端为样本的完整系统学习路径
https://blog.xuefei.art/posts/pu-ftu-architect-learning-roadmap/
作者
雪飞
发布于
2026-07-16
许可协议
CC BY-NC-SA 4.0
随机文章随机推荐

文章目录