你有没有遇到过这种场景:RTU 设备装在偏远山区的山洪预警站,4G 信号时断时续。网络一断,刚采集的水位数据就丢了,等网络恢复后平台数据出现"空洞"。更头疼的是,设备同时挂了 4G、以太网、北斗三个通道,哪个通道的数据该补发、补发到哪,全靠人工管理。这篇文章就来聊聊,我们是如何用一个"补发数据管理中间件"彻底解决 RTU 离线数据零丢失问题的。
一、为什么 RTU 必须有补发机制?
水文遥测终端(RTU)是水库、灌区、山洪预警站的"神经末梢"。它持续采集雨量、水位、流量、闸门开度等数据,通过 4G/以太网/北斗等网络通道传输给中心平台。这个过程中有一个不可妥协的诉求:
数据不能丢,哪怕网络断了也不行。
但现实是残酷的:4G 信号在山区、隧道、极端天气下随时中断;一个 RTU 可能同时挂载多个网络通道,各通道状态独立;水文数据要求"事后可追溯",离线期间的数据必须补传。
传统做法是在 RAM 里开个环形缓冲区暂存,但 RAM 一断电就全没了。必须把待补发数据持久化到非易失存储中。 这就是 comm_reissuedata_manage 组件诞生的背景------RTU 离线时把上报数据存入 Flash,网络恢复后按通道逐步补发,实现离线数据零丢失。 它是合格 RTU 的标准插件。
二、整体设计思路:Flash 上的持久化 FIFO 队列
核心思想一句话概括:
在 SPI Flash 上构建一个持久化的 FIFO 数据队列,支持掉电不丢、断网暂存、来网补发。
就像一个"数据保险箱":网络断开时存进去,网络恢复后取出来补发,数据写在 Flash 上断电也不丢。

系统分三层:业务层 (数据采集、规约组包、网络通道管理)→ 补发数据管理中间件 (push/pop/peek 队列接口 + 完整性校验 + 掉电恢复)→ 硬件存储层(W25Q128 Flash,1MB 专用区)。
三、Flash 存储区的物理布局
管理器在 W25Q128 的 16MB 地址空间中,划出一块 1MB 专用区域 (0x100000 ~ 0x200000)存储待补发数据。

这块区域的切分逻辑经过精心计算:
| 参数 | 值 | 说明 |
|---|---|---|
| 存储区域 | 0x100000 ~ 0x200000 |
1MB 容量,避开程序区 |
| Flash 扇区大小 | 4096 字节 | W25Q128 擦除最小单位 |
| 总扇区数 | 256 个 | 1MB ÷ 4KB |
| 数据块大小 | 1280 字节(1.25KB) | 头部 32B + 载荷 1248B |
| 每扇区数据块数 | 3 个 | 4096 ÷ 1280 = 3.2,取整 3 |
| 最大数据块数 | 800 个 | 256 × 3 |
| 理论总容量 | 1000KB | 有效利用率 ~78%,载荷利用率 97.5% |
为什么数据块选 1280 字节? 这是存储效率与扇区对齐的权衡:太小则头部占比高、扇区浪费严重;太大则一帧报文(通常几百字节)用不满。1280B 让每扇区正好放 3 个完整块(仅浪费 256B),且 1248B 载荷足以容纳一帧完整水文规约报文。
定长块 + 线性编址 让寻址变成简单乘法:addr = 基址 + 索引 × 1280,无需维护地址映射表。
四、数据块结构设计
每个数据块由**头部(Header,32B)和 载荷(Payload,1248B)**组成,固定 1280 字节。

c
typedef struct {
uint32_t magic_number; // 魔数 0x52454953 ("REIS")
uint32_t block_id; // 块唯一标识符
uint32_t timestamp; // Unix 时间戳
uint16_t data_length; // 实际数据长度
uint8_t priority; // 数据优先级
uint8_t status; // 数据状态
uint32_t sequence_number; // 序列号(FIFO 排序依据)
uint16_t checksum; // 校验和
uint8_t reserved[10]; // 保留字段
} reissue_data_header_t;
几个关键字段的工程意义:
-
magic_number :数据块的"身份证"。有效块
0x52454953("REIS"),已删除块0xDEADBEEF,空闲块0xFFFFFFFF(Flash 擦除默认值)。三态魔数让管理器一眼区分块状态。 -
sequence_number :FIFO 队列排序的核心。push 时自增,上电恢复时扫描全盘找最小序列号作队列头、最大序列号作队列尾。无需专门索引表,仅凭数据本身就能重建队列顺序。
-
priority:四级优先级(LOW/NORMAL/HIGH/URGENT),为多通道优先级调度补发预留能力。
-
checksum:对头部和载荷的累加校验和,防止 Flash 位翻转导致的静默损坏。
-
reserved10:为通道 ID、重试次数等未来需求预留扩展空间。
载荷区存放实际业务数据------一帧完整的规约报文,1248B 上限完全够用。
五、核心操作:数据入队(push)
网络离线时,业务模块调用 reissue_data_push() 将待补发数据存入 Flash 队列:
#mermaid-svg-Lhx8uYPnjw9UZpWL{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Lhx8uYPnjw9UZpWL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Lhx8uYPnjw9UZpWL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Lhx8uYPnjw9UZpWL .error-icon{fill:#552222;}#mermaid-svg-Lhx8uYPnjw9UZpWL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Lhx8uYPnjw9UZpWL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Lhx8uYPnjw9UZpWL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Lhx8uYPnjw9UZpWL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Lhx8uYPnjw9UZpWL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Lhx8uYPnjw9UZpWL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Lhx8uYPnjw9UZpWL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Lhx8uYPnjw9UZpWL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Lhx8uYPnjw9UZpWL .marker.cross{stroke:#333333;}#mermaid-svg-Lhx8uYPnjw9UZpWL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Lhx8uYPnjw9UZpWL p{margin:0;}#mermaid-svg-Lhx8uYPnjw9UZpWL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Lhx8uYPnjw9UZpWL .cluster-label text{fill:#333;}#mermaid-svg-Lhx8uYPnjw9UZpWL .cluster-label span{color:#333;}#mermaid-svg-Lhx8uYPnjw9UZpWL .cluster-label span p{background-color:transparent;}#mermaid-svg-Lhx8uYPnjw9UZpWL .label text,#mermaid-svg-Lhx8uYPnjw9UZpWL span{fill:#333;color:#333;}#mermaid-svg-Lhx8uYPnjw9UZpWL .node rect,#mermaid-svg-Lhx8uYPnjw9UZpWL .node circle,#mermaid-svg-Lhx8uYPnjw9UZpWL .node ellipse,#mermaid-svg-Lhx8uYPnjw9UZpWL .node polygon,#mermaid-svg-Lhx8uYPnjw9UZpWL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Lhx8uYPnjw9UZpWL .rough-node .label text,#mermaid-svg-Lhx8uYPnjw9UZpWL .node .label text,#mermaid-svg-Lhx8uYPnjw9UZpWL .image-shape .label,#mermaid-svg-Lhx8uYPnjw9UZpWL .icon-shape .label{text-anchor:middle;}#mermaid-svg-Lhx8uYPnjw9UZpWL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Lhx8uYPnjw9UZpWL .rough-node .label,#mermaid-svg-Lhx8uYPnjw9UZpWL .node .label,#mermaid-svg-Lhx8uYPnjw9UZpWL .image-shape .label,#mermaid-svg-Lhx8uYPnjw9UZpWL .icon-shape .label{text-align:center;}#mermaid-svg-Lhx8uYPnjw9UZpWL .node.clickable{cursor:pointer;}#mermaid-svg-Lhx8uYPnjw9UZpWL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Lhx8uYPnjw9UZpWL .arrowheadPath{fill:#333333;}#mermaid-svg-Lhx8uYPnjw9UZpWL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Lhx8uYPnjw9UZpWL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Lhx8uYPnjw9UZpWL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Lhx8uYPnjw9UZpWL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Lhx8uYPnjw9UZpWL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Lhx8uYPnjw9UZpWL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Lhx8uYPnjw9UZpWL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Lhx8uYPnjw9UZpWL .cluster text{fill:#333;}#mermaid-svg-Lhx8uYPnjw9UZpWL .cluster span{color:#333;}#mermaid-svg-Lhx8uYPnjw9UZpWL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Lhx8uYPnjw9UZpWL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Lhx8uYPnjw9UZpWL rect.text{fill:none;stroke-width:0;}#mermaid-svg-Lhx8uYPnjw9UZpWL .icon-shape,#mermaid-svg-Lhx8uYPnjw9UZpWL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Lhx8uYPnjw9UZpWL .icon-shape p,#mermaid-svg-Lhx8uYPnjw9UZpWL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Lhx8uYPnjw9UZpWL .icon-shape .label rect,#mermaid-svg-Lhx8uYPnjw9UZpWL .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Lhx8uYPnjw9UZpWL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Lhx8uYPnjw9UZpWL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Lhx8uYPnjw9UZpWL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 异常
通过
push: data, len, priority
参数校验 & 存储空间检查
返回错误码
查找空闲块地址
扫描 FFFFFFFF / DEADBEEF 标记
malloc 组装数据块:
填充头部 + 复制载荷 + 计算校验和
写入 Flash 1280B
更新队列:
tail 指向新块 / 序列号自增 / total_items++
返回 OK + block_id
关键设计点:
-
空闲块原地复用 :
_find_free_block_address()线性扫描,遇到空闲块(0xFFFFFFFF)或已删除块(0xDEADBEEF)即返回其地址,让已 pop 的空间能被重新利用。 -
动态内存缓冲 :先
malloc在 RAM 中组装完整数据块(头部+载荷+校验和),再一次性写入 Flash,避免多次小写入。 -
序列号自增:即使物理地址不连续,通过序列号也能恢复正确的 FIFO 顺序。
六、核心操作:数据出队(pop)
网络恢复后,通信模块调用 reissue_data_pop() 从队列头部取出数据补发:
#mermaid-svg-lgbFgII6Mq9yYfhC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-lgbFgII6Mq9yYfhC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lgbFgII6Mq9yYfhC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lgbFgII6Mq9yYfhC .error-icon{fill:#552222;}#mermaid-svg-lgbFgII6Mq9yYfhC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lgbFgII6Mq9yYfhC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lgbFgII6Mq9yYfhC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lgbFgII6Mq9yYfhC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lgbFgII6Mq9yYfhC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lgbFgII6Mq9yYfhC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lgbFgII6Mq9yYfhC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lgbFgII6Mq9yYfhC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lgbFgII6Mq9yYfhC .marker.cross{stroke:#333333;}#mermaid-svg-lgbFgII6Mq9yYfhC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lgbFgII6Mq9yYfhC p{margin:0;}#mermaid-svg-lgbFgII6Mq9yYfhC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-lgbFgII6Mq9yYfhC .cluster-label text{fill:#333;}#mermaid-svg-lgbFgII6Mq9yYfhC .cluster-label span{color:#333;}#mermaid-svg-lgbFgII6Mq9yYfhC .cluster-label span p{background-color:transparent;}#mermaid-svg-lgbFgII6Mq9yYfhC .label text,#mermaid-svg-lgbFgII6Mq9yYfhC span{fill:#333;color:#333;}#mermaid-svg-lgbFgII6Mq9yYfhC .node rect,#mermaid-svg-lgbFgII6Mq9yYfhC .node circle,#mermaid-svg-lgbFgII6Mq9yYfhC .node ellipse,#mermaid-svg-lgbFgII6Mq9yYfhC .node polygon,#mermaid-svg-lgbFgII6Mq9yYfhC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lgbFgII6Mq9yYfhC .rough-node .label text,#mermaid-svg-lgbFgII6Mq9yYfhC .node .label text,#mermaid-svg-lgbFgII6Mq9yYfhC .image-shape .label,#mermaid-svg-lgbFgII6Mq9yYfhC .icon-shape .label{text-anchor:middle;}#mermaid-svg-lgbFgII6Mq9yYfhC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-lgbFgII6Mq9yYfhC .rough-node .label,#mermaid-svg-lgbFgII6Mq9yYfhC .node .label,#mermaid-svg-lgbFgII6Mq9yYfhC .image-shape .label,#mermaid-svg-lgbFgII6Mq9yYfhC .icon-shape .label{text-align:center;}#mermaid-svg-lgbFgII6Mq9yYfhC .node.clickable{cursor:pointer;}#mermaid-svg-lgbFgII6Mq9yYfhC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-lgbFgII6Mq9yYfhC .arrowheadPath{fill:#333333;}#mermaid-svg-lgbFgII6Mq9yYfhC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lgbFgII6Mq9yYfhC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lgbFgII6Mq9yYfhC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lgbFgII6Mq9yYfhC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-lgbFgII6Mq9yYfhC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lgbFgII6Mq9yYfhC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-lgbFgII6Mq9yYfhC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lgbFgII6Mq9yYfhC .cluster text{fill:#333;}#mermaid-svg-lgbFgII6Mq9yYfhC .cluster span{color:#333;}#mermaid-svg-lgbFgII6Mq9yYfhC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-lgbFgII6Mq9yYfhC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lgbFgII6Mq9yYfhC rect.text{fill:none;stroke-width:0;}#mermaid-svg-lgbFgII6Mq9yYfhC .icon-shape,#mermaid-svg-lgbFgII6Mq9yYfhC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lgbFgII6Mq9yYfhC .icon-shape p,#mermaid-svg-lgbFgII6Mq9yYfhC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-lgbFgII6Mq9yYfhC .icon-shape .label rect,#mermaid-svg-lgbFgII6Mq9yYfhC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lgbFgII6Mq9yYfhC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lgbFgII6Mq9yYfhC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lgbFgII6Mq9yYfhC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 空
非空
验证失败
验证通过
pop: 读取队头数据
队列非空?
返回 STORAGE_EMPTY
读取 head 块
验证魔数 + 校验和
返回 DATA_CORRUPTED
返回载荷数据
标记 magic=0xDEADBEEF
仅写 32B 头部
更新队列:
重新扫描找最小序列号块为新 head
返回 OK
关键设计点:
-
软删除而非物理擦除 :pop 时不擦除 Flash 扇区(4KB 擦除太慢且损耗寿命),仅将头部魔数改写为
0xDEADBEEF标记"已删除",后续 push 可复用该空间。将 Flash 擦除次数降到最低。 -
仅写头部 32 字节:标记删除时只覆写头部,不重写整个 1280B 块,减少 Flash 写入量。
-
队列指针更新 :pop 后通过
_update_queue_pointers()重新全盘扫描,按序列号找最小值作为新 head------以时间换空间简洁性。
七、掉电恢复机制:全盘扫描重建队列
RTU 工作在野外,断电重启是家常便饭。管理器在 RAM 中维护的队列状态(head/tail/total_items/next_block_id)断电即失,必须在每次上电时自动恢复,否则已存储的补发数据就会"失联"。
reissue_data_manager_init() 初始化时调用 _scan_storage_for_queue_state(),对 1MB 存储区完整扫描:
#mermaid-svg-HVkuxDXfYfTzfLsL{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HVkuxDXfYfTzfLsL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HVkuxDXfYfTzfLsL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HVkuxDXfYfTzfLsL .error-icon{fill:#552222;}#mermaid-svg-HVkuxDXfYfTzfLsL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HVkuxDXfYfTzfLsL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HVkuxDXfYfTzfLsL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HVkuxDXfYfTzfLsL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HVkuxDXfYfTzfLsL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HVkuxDXfYfTzfLsL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HVkuxDXfYfTzfLsL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HVkuxDXfYfTzfLsL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HVkuxDXfYfTzfLsL .marker.cross{stroke:#333333;}#mermaid-svg-HVkuxDXfYfTzfLsL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HVkuxDXfYfTzfLsL p{margin:0;}#mermaid-svg-HVkuxDXfYfTzfLsL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HVkuxDXfYfTzfLsL .cluster-label text{fill:#333;}#mermaid-svg-HVkuxDXfYfTzfLsL .cluster-label span{color:#333;}#mermaid-svg-HVkuxDXfYfTzfLsL .cluster-label span p{background-color:transparent;}#mermaid-svg-HVkuxDXfYfTzfLsL .label text,#mermaid-svg-HVkuxDXfYfTzfLsL span{fill:#333;color:#333;}#mermaid-svg-HVkuxDXfYfTzfLsL .node rect,#mermaid-svg-HVkuxDXfYfTzfLsL .node circle,#mermaid-svg-HVkuxDXfYfTzfLsL .node ellipse,#mermaid-svg-HVkuxDXfYfTzfLsL .node polygon,#mermaid-svg-HVkuxDXfYfTzfLsL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HVkuxDXfYfTzfLsL .rough-node .label text,#mermaid-svg-HVkuxDXfYfTzfLsL .node .label text,#mermaid-svg-HVkuxDXfYfTzfLsL .image-shape .label,#mermaid-svg-HVkuxDXfYfTzfLsL .icon-shape .label{text-anchor:middle;}#mermaid-svg-HVkuxDXfYfTzfLsL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HVkuxDXfYfTzfLsL .rough-node .label,#mermaid-svg-HVkuxDXfYfTzfLsL .node .label,#mermaid-svg-HVkuxDXfYfTzfLsL .image-shape .label,#mermaid-svg-HVkuxDXfYfTzfLsL .icon-shape .label{text-align:center;}#mermaid-svg-HVkuxDXfYfTzfLsL .node.clickable{cursor:pointer;}#mermaid-svg-HVkuxDXfYfTzfLsL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HVkuxDXfYfTzfLsL .arrowheadPath{fill:#333333;}#mermaid-svg-HVkuxDXfYfTzfLsL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HVkuxDXfYfTzfLsL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HVkuxDXfYfTzfLsL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HVkuxDXfYfTzfLsL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HVkuxDXfYfTzfLsL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HVkuxDXfYfTzfLsL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HVkuxDXfYfTzfLsL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HVkuxDXfYfTzfLsL .cluster text{fill:#333;}#mermaid-svg-HVkuxDXfYfTzfLsL .cluster span{color:#333;}#mermaid-svg-HVkuxDXfYfTzfLsL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HVkuxDXfYfTzfLsL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HVkuxDXfYfTzfLsL rect.text{fill:none;stroke-width:0;}#mermaid-svg-HVkuxDXfYfTzfLsL .icon-shape,#mermaid-svg-HVkuxDXfYfTzfLsL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HVkuxDXfYfTzfLsL .icon-shape p,#mermaid-svg-HVkuxDXfYfTzfLsL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HVkuxDXfYfTzfLsL .icon-shape .label rect,#mermaid-svg-HVkuxDXfYfTzfLsL .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HVkuxDXfYfTzfLsL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HVkuxDXfYfTzfLsL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HVkuxDXfYfTzfLsL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
上电 init
全盘扫描 0x100000~0x200000
遍历每个 1280B 块
magic == REIS?
记录序列号:
最小→head, 最大→tail
更新 used_count / next_block_id
恢复全局状态:
used/free/序列号/head/tail 指针
队列状态 100% 重建
断电前数据完整可用
恢复策略的精髓------数据自描述: 队列的顺序信息完全内嵌在数据块自身中,不依赖任何外部索引表或元数据。序列号决定 FIFO 顺序,魔数决定块状态,block_id 决定下一块编号。即使 RAM 状态全部丢失,只要 Flash 上的数据块还在,就能 100% 重建队列。数据本身就是索引------这是对 Flash 掉电不丢特性的精准利用。
八、多网络通道分离存储
RTU 通常挂载多个网络通道(4G、以太网、北斗短报文),各通道网络状态独立------4G 断了不代表以太网也断了。这就要求补发数据管理能按通道分离存储,避免通道间数据混淆。
#mermaid-svg-gjDv2JNzDc40jeNa{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gjDv2JNzDc40jeNa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gjDv2JNzDc40jeNa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gjDv2JNzDc40jeNa .error-icon{fill:#552222;}#mermaid-svg-gjDv2JNzDc40jeNa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gjDv2JNzDc40jeNa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gjDv2JNzDc40jeNa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gjDv2JNzDc40jeNa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gjDv2JNzDc40jeNa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gjDv2JNzDc40jeNa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gjDv2JNzDc40jeNa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gjDv2JNzDc40jeNa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gjDv2JNzDc40jeNa .marker.cross{stroke:#333333;}#mermaid-svg-gjDv2JNzDc40jeNa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gjDv2JNzDc40jeNa p{margin:0;}#mermaid-svg-gjDv2JNzDc40jeNa .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-gjDv2JNzDc40jeNa .cluster-label text{fill:#333;}#mermaid-svg-gjDv2JNzDc40jeNa .cluster-label span{color:#333;}#mermaid-svg-gjDv2JNzDc40jeNa .cluster-label span p{background-color:transparent;}#mermaid-svg-gjDv2JNzDc40jeNa .label text,#mermaid-svg-gjDv2JNzDc40jeNa span{fill:#333;color:#333;}#mermaid-svg-gjDv2JNzDc40jeNa .node rect,#mermaid-svg-gjDv2JNzDc40jeNa .node circle,#mermaid-svg-gjDv2JNzDc40jeNa .node ellipse,#mermaid-svg-gjDv2JNzDc40jeNa .node polygon,#mermaid-svg-gjDv2JNzDc40jeNa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gjDv2JNzDc40jeNa .rough-node .label text,#mermaid-svg-gjDv2JNzDc40jeNa .node .label text,#mermaid-svg-gjDv2JNzDc40jeNa .image-shape .label,#mermaid-svg-gjDv2JNzDc40jeNa .icon-shape .label{text-anchor:middle;}#mermaid-svg-gjDv2JNzDc40jeNa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gjDv2JNzDc40jeNa .rough-node .label,#mermaid-svg-gjDv2JNzDc40jeNa .node .label,#mermaid-svg-gjDv2JNzDc40jeNa .image-shape .label,#mermaid-svg-gjDv2JNzDc40jeNa .icon-shape .label{text-align:center;}#mermaid-svg-gjDv2JNzDc40jeNa .node.clickable{cursor:pointer;}#mermaid-svg-gjDv2JNzDc40jeNa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gjDv2JNzDc40jeNa .arrowheadPath{fill:#333333;}#mermaid-svg-gjDv2JNzDc40jeNa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gjDv2JNzDc40jeNa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gjDv2JNzDc40jeNa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gjDv2JNzDc40jeNa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gjDv2JNzDc40jeNa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gjDv2JNzDc40jeNa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gjDv2JNzDc40jeNa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gjDv2JNzDc40jeNa .cluster text{fill:#333;}#mermaid-svg-gjDv2JNzDc40jeNa .cluster span{color:#333;}#mermaid-svg-gjDv2JNzDc40jeNa div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-gjDv2JNzDc40jeNa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gjDv2JNzDc40jeNa rect.text{fill:none;stroke-width:0;}#mermaid-svg-gjDv2JNzDc40jeNa .icon-shape,#mermaid-svg-gjDv2JNzDc40jeNa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gjDv2JNzDc40jeNa .icon-shape p,#mermaid-svg-gjDv2JNzDc40jeNa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gjDv2JNzDc40jeNa .icon-shape .label rect,#mermaid-svg-gjDv2JNzDc40jeNa .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gjDv2JNzDc40jeNa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gjDv2JNzDc40jeNa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gjDv2JNzDc40jeNa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} W25Q128 Flash
补发数据存储区 1MB
网络通道路由层
业务数据源
4G离线
以太网离线
北斗离线
采集数据帧
网络状态判断
通道 A: 4G/EC20
通道 B: 以太网
通道 C: 北斗
通道A队列
独立序列号
独立push/pop
通道B队列
独立序列号
独立push/pop
通道C队列
独立序列号
独立push/pop
0x100000 - 0x200000
实现方式:
-
优先级字段区分通道 :头部
priority字段可标识数据所属网络通道,实现逻辑隔离。 -
独立队列实例 :为每个通道创建独立管理器实例,配置不同存储地址区间。如通道 A 用
0x100000~0x155000,通道 B 用0x155000~0x1AA000,通道 C 用0x1AA000~0x200000,各自独立 FIFO 队列和序列号。 -
按通道独立补发 :某通道网络恢复时,仅补发该通道队列数据,不影响其他通道存储状态。一个通道的网络故障不会影响其他通道的数据上报。
九、数据完整性保障:魔数 + 校验和双重防线
Flash 存储虽然掉电不丢,但存在位翻转 和写入中断风险。管理器通过双重机制保障完整性:
第一道防线------魔数校验: 每个数据块头部 4 字节魔数三态分明------0x52454953(有效)、0xDEADBEEF(已删除)、0xFFFFFFFF(空闲)。上电扫描和 pop 读取时首先检查魔数,非 REIS 直接跳过。
第二道防线------校验和验证: _validate_data_block() 读取数据后进行三重验证:
#mermaid-svg-qGdUvy0rc5WBbZLG{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qGdUvy0rc5WBbZLG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qGdUvy0rc5WBbZLG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qGdUvy0rc5WBbZLG .error-icon{fill:#552222;}#mermaid-svg-qGdUvy0rc5WBbZLG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qGdUvy0rc5WBbZLG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qGdUvy0rc5WBbZLG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qGdUvy0rc5WBbZLG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qGdUvy0rc5WBbZLG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qGdUvy0rc5WBbZLG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qGdUvy0rc5WBbZLG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qGdUvy0rc5WBbZLG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qGdUvy0rc5WBbZLG .marker.cross{stroke:#333333;}#mermaid-svg-qGdUvy0rc5WBbZLG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qGdUvy0rc5WBbZLG p{margin:0;}#mermaid-svg-qGdUvy0rc5WBbZLG .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qGdUvy0rc5WBbZLG .cluster-label text{fill:#333;}#mermaid-svg-qGdUvy0rc5WBbZLG .cluster-label span{color:#333;}#mermaid-svg-qGdUvy0rc5WBbZLG .cluster-label span p{background-color:transparent;}#mermaid-svg-qGdUvy0rc5WBbZLG .label text,#mermaid-svg-qGdUvy0rc5WBbZLG span{fill:#333;color:#333;}#mermaid-svg-qGdUvy0rc5WBbZLG .node rect,#mermaid-svg-qGdUvy0rc5WBbZLG .node circle,#mermaid-svg-qGdUvy0rc5WBbZLG .node ellipse,#mermaid-svg-qGdUvy0rc5WBbZLG .node polygon,#mermaid-svg-qGdUvy0rc5WBbZLG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qGdUvy0rc5WBbZLG .rough-node .label text,#mermaid-svg-qGdUvy0rc5WBbZLG .node .label text,#mermaid-svg-qGdUvy0rc5WBbZLG .image-shape .label,#mermaid-svg-qGdUvy0rc5WBbZLG .icon-shape .label{text-anchor:middle;}#mermaid-svg-qGdUvy0rc5WBbZLG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qGdUvy0rc5WBbZLG .rough-node .label,#mermaid-svg-qGdUvy0rc5WBbZLG .node .label,#mermaid-svg-qGdUvy0rc5WBbZLG .image-shape .label,#mermaid-svg-qGdUvy0rc5WBbZLG .icon-shape .label{text-align:center;}#mermaid-svg-qGdUvy0rc5WBbZLG .node.clickable{cursor:pointer;}#mermaid-svg-qGdUvy0rc5WBbZLG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qGdUvy0rc5WBbZLG .arrowheadPath{fill:#333333;}#mermaid-svg-qGdUvy0rc5WBbZLG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qGdUvy0rc5WBbZLG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qGdUvy0rc5WBbZLG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qGdUvy0rc5WBbZLG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qGdUvy0rc5WBbZLG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qGdUvy0rc5WBbZLG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qGdUvy0rc5WBbZLG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qGdUvy0rc5WBbZLG .cluster text{fill:#333;}#mermaid-svg-qGdUvy0rc5WBbZLG .cluster span{color:#333;}#mermaid-svg-qGdUvy0rc5WBbZLG div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qGdUvy0rc5WBbZLG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qGdUvy0rc5WBbZLG rect.text{fill:none;stroke-width:0;}#mermaid-svg-qGdUvy0rc5WBbZLG .icon-shape,#mermaid-svg-qGdUvy0rc5WBbZLG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qGdUvy0rc5WBbZLG .icon-shape p,#mermaid-svg-qGdUvy0rc5WBbZLG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qGdUvy0rc5WBbZLG .icon-shape .label rect,#mermaid-svg-qGdUvy0rc5WBbZLG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qGdUvy0rc5WBbZLG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qGdUvy0rc5WBbZLG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qGdUvy0rc5WBbZLG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
否
是
否
是
读取数据块
① 魔数 == REIS?
DATA_CORRUPTED
② 长度 <= 1248?
③ 校验和验证
checksum置0→累加→比对→恢复
匹配?
验证通过
校验和验证有一个值得注意的工程细节 :计算时需先将 checksum 字段置零(因为写入时也是 checksum=0 状态下计算的),计算完再恢复原值。源码中专门注释了这一点------若不恢复,同一对象后续被持久化或再次校验会引发数据错误。这种"临时置零→计算→恢复"模式确保了校验逻辑一致性,同时不破坏数据块对象完整性。
十、统计与诊断
管理器内置完整的统计诊断体系,便于开发调试和现场运维:
#mermaid-svg-CseDQnqGGaxkGbsi{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CseDQnqGGaxkGbsi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CseDQnqGGaxkGbsi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CseDQnqGGaxkGbsi .error-icon{fill:#552222;}#mermaid-svg-CseDQnqGGaxkGbsi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CseDQnqGGaxkGbsi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CseDQnqGGaxkGbsi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CseDQnqGGaxkGbsi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CseDQnqGGaxkGbsi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CseDQnqGGaxkGbsi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CseDQnqGGaxkGbsi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CseDQnqGGaxkGbsi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CseDQnqGGaxkGbsi .marker.cross{stroke:#333333;}#mermaid-svg-CseDQnqGGaxkGbsi svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CseDQnqGGaxkGbsi p{margin:0;}#mermaid-svg-CseDQnqGGaxkGbsi .edge{stroke-width:3;}#mermaid-svg-CseDQnqGGaxkGbsi .section--1 rect,#mermaid-svg-CseDQnqGGaxkGbsi .section--1 path,#mermaid-svg-CseDQnqGGaxkGbsi .section--1 circle,#mermaid-svg-CseDQnqGGaxkGbsi .section--1 polygon,#mermaid-svg-CseDQnqGGaxkGbsi .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .section--1 text{fill:#ffffff;}#mermaid-svg-CseDQnqGGaxkGbsi .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-CseDQnqGGaxkGbsi .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .edge-depth--1{stroke-width:17;}#mermaid-svg-CseDQnqGGaxkGbsi .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled,#mermaid-svg-CseDQnqGGaxkGbsi .disabled circle,#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:lightgray;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:#efefef;}#mermaid-svg-CseDQnqGGaxkGbsi .section-0 rect,#mermaid-svg-CseDQnqGGaxkGbsi .section-0 path,#mermaid-svg-CseDQnqGGaxkGbsi .section-0 circle,#mermaid-svg-CseDQnqGGaxkGbsi .section-0 polygon,#mermaid-svg-CseDQnqGGaxkGbsi .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-CseDQnqGGaxkGbsi .section-0 text{fill:black;}#mermaid-svg-CseDQnqGGaxkGbsi .node-icon-0{font-size:40px;color:black;}#mermaid-svg-CseDQnqGGaxkGbsi .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-CseDQnqGGaxkGbsi .edge-depth-0{stroke-width:14;}#mermaid-svg-CseDQnqGGaxkGbsi .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled,#mermaid-svg-CseDQnqGGaxkGbsi .disabled circle,#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:lightgray;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:#efefef;}#mermaid-svg-CseDQnqGGaxkGbsi .section-1 rect,#mermaid-svg-CseDQnqGGaxkGbsi .section-1 path,#mermaid-svg-CseDQnqGGaxkGbsi .section-1 circle,#mermaid-svg-CseDQnqGGaxkGbsi .section-1 polygon,#mermaid-svg-CseDQnqGGaxkGbsi .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .section-1 text{fill:black;}#mermaid-svg-CseDQnqGGaxkGbsi .node-icon-1{font-size:40px;color:black;}#mermaid-svg-CseDQnqGGaxkGbsi .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .edge-depth-1{stroke-width:11;}#mermaid-svg-CseDQnqGGaxkGbsi .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled,#mermaid-svg-CseDQnqGGaxkGbsi .disabled circle,#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:lightgray;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:#efefef;}#mermaid-svg-CseDQnqGGaxkGbsi .section-2 rect,#mermaid-svg-CseDQnqGGaxkGbsi .section-2 path,#mermaid-svg-CseDQnqGGaxkGbsi .section-2 circle,#mermaid-svg-CseDQnqGGaxkGbsi .section-2 polygon,#mermaid-svg-CseDQnqGGaxkGbsi .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .section-2 text{fill:#ffffff;}#mermaid-svg-CseDQnqGGaxkGbsi .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-CseDQnqGGaxkGbsi .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .edge-depth-2{stroke-width:8;}#mermaid-svg-CseDQnqGGaxkGbsi .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled,#mermaid-svg-CseDQnqGGaxkGbsi .disabled circle,#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:lightgray;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:#efefef;}#mermaid-svg-CseDQnqGGaxkGbsi .section-3 rect,#mermaid-svg-CseDQnqGGaxkGbsi .section-3 path,#mermaid-svg-CseDQnqGGaxkGbsi .section-3 circle,#mermaid-svg-CseDQnqGGaxkGbsi .section-3 polygon,#mermaid-svg-CseDQnqGGaxkGbsi .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .section-3 text{fill:black;}#mermaid-svg-CseDQnqGGaxkGbsi .node-icon-3{font-size:40px;color:black;}#mermaid-svg-CseDQnqGGaxkGbsi .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .edge-depth-3{stroke-width:5;}#mermaid-svg-CseDQnqGGaxkGbsi .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled,#mermaid-svg-CseDQnqGGaxkGbsi .disabled circle,#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:lightgray;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:#efefef;}#mermaid-svg-CseDQnqGGaxkGbsi .section-4 rect,#mermaid-svg-CseDQnqGGaxkGbsi .section-4 path,#mermaid-svg-CseDQnqGGaxkGbsi .section-4 circle,#mermaid-svg-CseDQnqGGaxkGbsi .section-4 polygon,#mermaid-svg-CseDQnqGGaxkGbsi .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .section-4 text{fill:black;}#mermaid-svg-CseDQnqGGaxkGbsi .node-icon-4{font-size:40px;color:black;}#mermaid-svg-CseDQnqGGaxkGbsi .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .edge-depth-4{stroke-width:2;}#mermaid-svg-CseDQnqGGaxkGbsi .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled,#mermaid-svg-CseDQnqGGaxkGbsi .disabled circle,#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:lightgray;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:#efefef;}#mermaid-svg-CseDQnqGGaxkGbsi .section-5 rect,#mermaid-svg-CseDQnqGGaxkGbsi .section-5 path,#mermaid-svg-CseDQnqGGaxkGbsi .section-5 circle,#mermaid-svg-CseDQnqGGaxkGbsi .section-5 polygon,#mermaid-svg-CseDQnqGGaxkGbsi .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .section-5 text{fill:black;}#mermaid-svg-CseDQnqGGaxkGbsi .node-icon-5{font-size:40px;color:black;}#mermaid-svg-CseDQnqGGaxkGbsi .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .edge-depth-5{stroke-width:-1;}#mermaid-svg-CseDQnqGGaxkGbsi .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled,#mermaid-svg-CseDQnqGGaxkGbsi .disabled circle,#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:lightgray;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:#efefef;}#mermaid-svg-CseDQnqGGaxkGbsi .section-6 rect,#mermaid-svg-CseDQnqGGaxkGbsi .section-6 path,#mermaid-svg-CseDQnqGGaxkGbsi .section-6 circle,#mermaid-svg-CseDQnqGGaxkGbsi .section-6 polygon,#mermaid-svg-CseDQnqGGaxkGbsi .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .section-6 text{fill:black;}#mermaid-svg-CseDQnqGGaxkGbsi .node-icon-6{font-size:40px;color:black;}#mermaid-svg-CseDQnqGGaxkGbsi .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .edge-depth-6{stroke-width:-4;}#mermaid-svg-CseDQnqGGaxkGbsi .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled,#mermaid-svg-CseDQnqGGaxkGbsi .disabled circle,#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:lightgray;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:#efefef;}#mermaid-svg-CseDQnqGGaxkGbsi .section-7 rect,#mermaid-svg-CseDQnqGGaxkGbsi .section-7 path,#mermaid-svg-CseDQnqGGaxkGbsi .section-7 circle,#mermaid-svg-CseDQnqGGaxkGbsi .section-7 polygon,#mermaid-svg-CseDQnqGGaxkGbsi .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .section-7 text{fill:black;}#mermaid-svg-CseDQnqGGaxkGbsi .node-icon-7{font-size:40px;color:black;}#mermaid-svg-CseDQnqGGaxkGbsi .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .edge-depth-7{stroke-width:-7;}#mermaid-svg-CseDQnqGGaxkGbsi .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled,#mermaid-svg-CseDQnqGGaxkGbsi .disabled circle,#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:lightgray;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:#efefef;}#mermaid-svg-CseDQnqGGaxkGbsi .section-8 rect,#mermaid-svg-CseDQnqGGaxkGbsi .section-8 path,#mermaid-svg-CseDQnqGGaxkGbsi .section-8 circle,#mermaid-svg-CseDQnqGGaxkGbsi .section-8 polygon,#mermaid-svg-CseDQnqGGaxkGbsi .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .section-8 text{fill:black;}#mermaid-svg-CseDQnqGGaxkGbsi .node-icon-8{font-size:40px;color:black;}#mermaid-svg-CseDQnqGGaxkGbsi .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .edge-depth-8{stroke-width:-10;}#mermaid-svg-CseDQnqGGaxkGbsi .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled,#mermaid-svg-CseDQnqGGaxkGbsi .disabled circle,#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:lightgray;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:#efefef;}#mermaid-svg-CseDQnqGGaxkGbsi .section-9 rect,#mermaid-svg-CseDQnqGGaxkGbsi .section-9 path,#mermaid-svg-CseDQnqGGaxkGbsi .section-9 circle,#mermaid-svg-CseDQnqGGaxkGbsi .section-9 polygon,#mermaid-svg-CseDQnqGGaxkGbsi .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .section-9 text{fill:black;}#mermaid-svg-CseDQnqGGaxkGbsi .node-icon-9{font-size:40px;color:black;}#mermaid-svg-CseDQnqGGaxkGbsi .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .edge-depth-9{stroke-width:-13;}#mermaid-svg-CseDQnqGGaxkGbsi .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled,#mermaid-svg-CseDQnqGGaxkGbsi .disabled circle,#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:lightgray;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:#efefef;}#mermaid-svg-CseDQnqGGaxkGbsi .section-10 rect,#mermaid-svg-CseDQnqGGaxkGbsi .section-10 path,#mermaid-svg-CseDQnqGGaxkGbsi .section-10 circle,#mermaid-svg-CseDQnqGGaxkGbsi .section-10 polygon,#mermaid-svg-CseDQnqGGaxkGbsi .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .section-10 text{fill:black;}#mermaid-svg-CseDQnqGGaxkGbsi .node-icon-10{font-size:40px;color:black;}#mermaid-svg-CseDQnqGGaxkGbsi .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .edge-depth-10{stroke-width:-16;}#mermaid-svg-CseDQnqGGaxkGbsi .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled,#mermaid-svg-CseDQnqGGaxkGbsi .disabled circle,#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:lightgray;}#mermaid-svg-CseDQnqGGaxkGbsi .disabled text{fill:#efefef;}#mermaid-svg-CseDQnqGGaxkGbsi .section-root rect,#mermaid-svg-CseDQnqGGaxkGbsi .section-root path,#mermaid-svg-CseDQnqGGaxkGbsi .section-root circle,#mermaid-svg-CseDQnqGGaxkGbsi .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-CseDQnqGGaxkGbsi .section-root text{fill:#ffffff;}#mermaid-svg-CseDQnqGGaxkGbsi .section-root span{color:#ffffff;}#mermaid-svg-CseDQnqGGaxkGbsi .section-2 span{color:#ffffff;}#mermaid-svg-CseDQnqGGaxkGbsi .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-CseDQnqGGaxkGbsi .edge{fill:none;}#mermaid-svg-CseDQnqGGaxkGbsi .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-CseDQnqGGaxkGbsi :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 诊断接口
存储统计
used/free_blocks: 已用/空闲块数
storage_utilization: 利用率
push/pop_operations: 入队/出队次数
failed_operations: 失败次数
current_sequence: 当前序列号
队列状态
head/tail_block_id: 队头/队尾块ID
total_items: 队列元素数
head/tail_address: 物理地址
调试输出
print_manager_status: 管理器状态
print_queue_info: 队列详情
error_to_string: 错误码转字符串
其中 failed_operations 是重要健康指标:持续增长说明 Flash 可能出现硬件故障或数据损坏,需运维介入。storage_utilization 反映补发数据积压程度:长期居高不下说明网络质量差或补发速度跟不上,需扩大存储区或优化补发策略。
十一、完整工作流程:离线→存储→恢复→补发
以一次完整的网络中断与恢复场景为例:
网络(4G) Flash 补发管理器 业务模块 网络(4G) Flash 补发管理器 业务模块 #mermaid-svg-s55aPVvWlyxprVU4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-s55aPVvWlyxprVU4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-s55aPVvWlyxprVU4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-s55aPVvWlyxprVU4 .error-icon{fill:#552222;}#mermaid-svg-s55aPVvWlyxprVU4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-s55aPVvWlyxprVU4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-s55aPVvWlyxprVU4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-s55aPVvWlyxprVU4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-s55aPVvWlyxprVU4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-s55aPVvWlyxprVU4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-s55aPVvWlyxprVU4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-s55aPVvWlyxprVU4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-s55aPVvWlyxprVU4 .marker.cross{stroke:#333333;}#mermaid-svg-s55aPVvWlyxprVU4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-s55aPVvWlyxprVU4 p{margin:0;}#mermaid-svg-s55aPVvWlyxprVU4 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-s55aPVvWlyxprVU4 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-s55aPVvWlyxprVU4 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-s55aPVvWlyxprVU4 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-s55aPVvWlyxprVU4 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-s55aPVvWlyxprVU4 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-s55aPVvWlyxprVU4 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-s55aPVvWlyxprVU4 .sequenceNumber{fill:white;}#mermaid-svg-s55aPVvWlyxprVU4 #sequencenumber{fill:#333;}#mermaid-svg-s55aPVvWlyxprVU4 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-s55aPVvWlyxprVU4 .messageText{fill:#333;stroke:none;}#mermaid-svg-s55aPVvWlyxprVU4 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-s55aPVvWlyxprVU4 .labelText,#mermaid-svg-s55aPVvWlyxprVU4 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-s55aPVvWlyxprVU4 .loopText,#mermaid-svg-s55aPVvWlyxprVU4 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-s55aPVvWlyxprVU4 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-s55aPVvWlyxprVU4 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-s55aPVvWlyxprVU4 .noteText,#mermaid-svg-s55aPVvWlyxprVU4 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-s55aPVvWlyxprVU4 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-s55aPVvWlyxprVU4 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-s55aPVvWlyxprVU4 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-s55aPVvWlyxprVU4 .actorPopupMenu{position:absolute;}#mermaid-svg-s55aPVvWlyxprVU4 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-s55aPVvWlyxprVU4 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-s55aPVvWlyxprVU4 .actor-man circle,#mermaid-svg-s55aPVvWlyxprVU4 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-s55aPVvWlyxprVU4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 网络中断 → 数据暂存 断电重启 网络恢复 → 逐帧补发 loop队列未空 离线数据零丢失 push(data1) → 写入Flash block_id=1push(data2) → 写入Flash block_id=2init() → 全盘扫描 → 重建队列(head=块1, tail=块2)pop() → 读取+验证+标记删除补发报文 → 发送成功
十二、设计亮点小结
| 设计点 | 具体体现 | 解决的问题 |
|---|---|---|
| Flash 持久化队列 | 1MB 专用区 + 800 个定长块 | 断电后补发数据不丢失 |
| 定长块 + 线性编址 | 1280B/块,地址 = 基址 + 索引×块大小 | 寻址简单,无需地址映射表 |
| 序列号 FIFO 排序 | push 时自增 sequence_number | 无需队列索引表即可重建顺序 |
| 数据自描述恢复 | 序列号 + 魔数内嵌于数据块 | 上电全盘扫描即可重建队列状态 |
| 软删除机制 | pop 时 magic→0xDEADBEEF | 避免频繁擦除Flash扇区,延长寿命 |
| 魔数三态设计 | REIS / DEADBEEF / FFFFFFFF | 一眼区分有效/已删/空闲状态 |
| 双重完整性校验 | 魔数 + 累加校验和 | 防止 Flash 位翻转导致静默损坏 |
| 多通道分离存储 | 优先级字段 + 独立队列实例 | 不同网络通道数据互不干扰 |
| 完整统计诊断 | push/pop/failed 计数 + 利用率 | 现场运维可感知存储健康状态 |
十三、总结
comm_reissuedata_manage 是 RTU 固件中一个务实而可靠的组件。它没有用复杂的文件系统或数据库,而是用最朴素的"定长块 + 序列号 + 魔数标记"三板斧,在裸 Flash 上构建了一个掉电安全的持久化 FIFO 队列。
它的核心智慧在于------让数据块自身携带所有恢复所需的信息。序列号决定顺序,魔数决定状态,校验和保障完整性。不需要任何额外的元数据表,也就不存在"元数据与数据不一致"的风险。上电时全盘扫描一遍,队列状态自然浮现。这种"数据即索引"的设计哲学,在 Flash 寿命和恢复可靠性之间找到了最佳平衡点。
配合多通道分离存储 和软删除原地复用 机制,这个组件让 RTU 在网络时断时续的恶劣环境下,依然能做到离线数据零丢失、来网自动补发------这正是合格 RTU 的核心能力。
对于做水文、工业 RTU 开发的同学,这套思路完全可以迁移到其他场景:只要有非易失存储(SPI Flash、EEPROM、内部 Flash 分区),都可以套用"定长块 + 序列号 + 魔数"的模式,快速搭建自己的补发数据管理系统。
欢迎留言交流,如果觉得有帮助,点个关注不迷路 ✌️
本文基于 RTU 固件 stm32_driver_framework 框架中的 comm_reissuedata_manage 组件源码整理。