edgeCore 工程铁律与恢复细则(操作层)

版本 日期 状态
v2.4 2026-10-16 现行

变更记录:v1.1 增补先关后开强化与退出顺序细则;v1.2 新增重连约束矩阵 R-0~R-9;v2.0 全面重构为 CONN/REC/BUD/SCAN/SAFE/SHUTDOWN/OBS/DRV/TEST 编号体系;v2.1 所有者裁定恢复「设备未全部离线禁止重建」为必须级铁律(REC-002);v2.2 REC-002 增补恢复路径分工;v2.3 纳入工业现场专项评审:重建准入四条件合取、CONN-006 ReleaseUncertain、BUD-004 单资源节流、附录 A 风险登记;v2.4 纳入其他章节专项评审:REC-005 错误分类改为「阶段+证据+传输健康+范围」联合判定、BUD-005 写操作幂等性、BUD-006 共享连接 deadline 协调、REC-012 异步连接约束、SCAN-005/006 时间戳拆分与任务合并语义、SAFE-002/003 panic 三态与进度监控、SHUTDOWN 生产者/消费者屏障、OBS-002/003 指标基数与数据预算、DRV 能力字段与豁免生命周期、TEST 量化门禁与写结果不确定测试,附录 B 风险登记


0. 工程铁律

# 铁律 最小判据 违反的典型形态
1 任何性能优化不得以牺牲稳定性为代价 ① 不得移除或弱化细则 1 / 细则 2 的保留防线;② 不得新增无超时、无退避、无 recover 边界的 IO / 调度路径;③ 性能类变更须附 L3 故障注入复跑结论 为降延迟 / 提吞吐削弱超时、退避、重试、隔离、兜底等稳定性防线;Benchmark 收益以新增故障面换取
2 任何架构优化不得增加系统恢复复杂度——禁止的是恢复退化,不是禁止增强自愈 ① 恢复的人工介入度、外部依赖、步骤数不增加;② 恢复能力不退化为依赖外部组件;③ 判定口径为「恢复复杂度净退化」 恢复路径变长 / 变分支;故障自愈退化为依赖人工重启;「先稳定闭环、再工业验证、后性能优化」的 Phase 顺序被倒置

冲突仲裁:稳定性口径与现场合理诉求冲突时,走参数化 / 配置化解决,不得以削弱保留防线满足诉求。

工业现场资源保护总纲(v2.3 依专项评审确立):工业网关不得把频繁重连当作默认自愈方式。恢复优先级为——优先复用现有连接 → 优先恢复单个设备的采集(防饿死探测)→ 仅在满足严格准入条件时重建共享链路 → 任何情况下不得以反复拨号掩盖资源释放失败。现场依据:PLC / 仪表 / 嵌入式控制器连接槽位有限且未必及时释放异常会话;本机 Close() 成功不等于远端已释放槽位;共享链路反复拆建会波及仍正常工作的设备;连接资源耗尽后表现为「接口在线、数据长期停更」的静默饿死。

0.2 规范用语

  • 必须:不满足则违反工程规则或阻断发布。
  • 禁止:不允许出现的行为。
  • 应当:默认必须遵循;例外需给出理由并留痕。
  • 建议:推荐实践,不直接作为阻断条件。

0.3 编号体系与迁移表

编号稳定,不因章节调整变化。评审、故障单、发布记录直接引用编号。

旧编号(v1.2) 新编号 说明
R-0 REC-002 必须级铁律,强制保留(v2.1 依所有者裁定恢复;v2.3 升级为四条件合取准入);实施保障见 REC-004
R-1 REC-005 设备级错误不拆共享链路
R-2 REC-006 链路级重连须有断链证据
R-3 REC-007 Health()==Unknown 不算断链
R-4 REC-008 看门狗防抖 / 冷却 / single-flight
(v2.0 新增) REC-001 故障五分类契约
(v2.0 新增) REC-003 恢复范围与故障证据范围一致
(v2.0 新增) REC-004 在线状态同步义务(保证 REC-002 门槛可达)
R-5 CONN-001~005 先关后开升级为连接生命周期契约
R-6 REC-009 全局重连限流
R-7 REC-010 单 PLC 单连接
R-8 REC-011 断网区分
R-9 SHUTDOWN-001 关联 API 路径禁同步拨号

0.4 机制映射表

机制 约束 代码锚点
先关后开(铁律 2 / 细则 2) 链路重建 / 重连 / 配置变更引起的重新建立链接,拨号前须确认关闭旧链接(同 fd/socket) internal/driver/connection_manager.go(runConnect 持 linkMu 包 dial/install)、各驱动 connectOnce
重建准入(REC-002 铁律) 一链路多从站时设备未全部离线禁止重建;单设备链路离线即放行;链路有存活设备期间,离线设备由防饿死机制探测恢复;v2.3 起准入为四条件合取——当前代码只判链路层,未实现,见 §10 缺口 C-2 internal/core/channel_manager.go(checkChannelHealth、GetChannelStats 在线计数)
退出顺序(细则 3) 先关南向接口,最后关日志 cmd/main.go(SIGTERM/Web 重启同路径)、ChannelManager.Shutdown、logger.Close
防饿死(细则 1) 五项保留防线 SCAN-001~004
恢复预算 全局限流 + 单资源节流 BUD-001~004

0.5 铁律约束不变量,不永久绑定具体实现

保留防线清单:细则 1 的五项防线 + DriverCircuitBreaker(故障链路有界隔离与自动复核)+ bbolt 隔离区 quarantine(加载失败通道隔离)+ 循环级 recover(panic 隔离)+ 看门狗防抖。铁律保护的是这些机制保障的不变量(有界隔离、自动复核、隔离不静默删除、panic 不打死循环),不是某一个具体函数或实现类——允许经正式验证的等价或更强实现替换,替换必须证明等价性并通过相同故障门禁。行号仅作审计入口,长期证据应当同时记录文件符号名与经验证的 commit SHA / 版本标签。

0.6 恢复路径定位:进程内主路径 + 外部兜底

正常可预期的设备 / 链路故障,恢复必须在进程内闭环——禁止把重启进程当常规恢复路径(细则 1 可观测红线「不接受重启后正常结案」即为此设)。但 OS、驱动库、进程死锁或不可恢复状态可能超出进程自愈能力:外部 supervisor 应当作为进程失去服务能力、优雅关停失败或达到有界恢复上限后的最后兜底,且必须带重启频率限制、故障记录与防重启风暴机制。本定位不弱化细则 1,仅明确分层保护边界。

0.7 配置治理

每个关键参数必须有默认值、合法范围、单位和作用域;启动时必须校验组合约束并拒绝不安全值;配置变更必须记录旧值 / 新值 / 来源 / 生效时间;危险参数变更禁止立即拆除现有健康连接(除非配置语义明确要求且符合 REC-002 重建门槛);运行时应当有回退值与「当前生效配置」诊断。参数化不是默认安全——它把风险从代码转移到现场配置,必须按上条治理。


1. 细则 1 —— 防止设备饿死(SCAN)

任何设备不得出现「接口显示在线、数据却长期停更」的静默饿死。以下防线为保留项,任何优化不得移除或弱化:

  • IO 层绝对超时:context.WithTimeout(ReadPoint 5s / ReadAllPoints 10s / Connect 10s)+ transport 层 SetDeadline——卡死的读必然在超时内返回错误,触发阶梯退避重连自愈。
  • 调度状态机兜底:processReadyTasks Running 卡死巡检(Interval>30s 取 10×Interval,否则 5min)强制重置重派;enforceAntiStarvation 防饿死抖动钳制。
  • 循环级 panic 边界:调度 / 看门狗 / worker / 重连循环顶层 recover(存活保障见 SAFE-001)。
  • 分家识别:CheckChannelHealth 识别链路状态分家后 30s 防抖即投递重建。
  • 可观测红线:Skipped / 饿死必须有日志(限频不静默);commTotalCnt=0、「链接时长持续增长 + 健康度 Offline」并存即饿死现场指纹,不接受「重启后正常」结案。

饿死定级口径:单设备 / 单接口数据停更超过 2×PollPeriod 且无自愈恢复 → P1(严重);多设备或整口停更、或停更持续至人工介入 → P0(致命)。该阈值为告警基线,不是全协议统一恢复时间目标(拆分口径见 SCAN-004)。

SCAN-001 端到端数据新鲜度链条

防饿死的完整判定对象是链条:计划调度 → 成功入队 → 获得执行机会 → 执行 I/O → 返回响应 → 解析数据 → 写入实时状态 → 更新采集时间戳。每一步都可能造成「数据长期停更」,必须能按阶段定位故障位置,而非只观察设备在线状态或任务数量。

SCAN-002 时间戳族与数据质量状态(v2.4 拆分修订)

必须区分以下语义独立的时间戳:LastValidSampleAt(最近一次成功取得并验证有效样本,不要求数值变化)、LastValueChangedAt(最近一次数值实际变化)、LastCollectSuccessAt(采集流程成功完成)、LastCommitAt(结果成功写入运行时数据状态)、LastErrorAt(最近失败)。数据新鲜度必须以 LastValidSampleAt 为依据;值变化检测用于变化事件与死值诊断等不同用途。设备级与点位级时间戳分别定义,禁止部分点位成功掩盖关键点位长期停更。另应当定义数据质量状态:有效 / 无效 / 未知 / 过期 / 替代值(缓存值)——过期数据即使仍展示也不得标识为新鲜。

SCAN-003 负载过载策略

① 同一设备禁止无限累计已过期的周期性采集任务;② 只需最新状态的设备应当合并重复待执行任务而非补跑历史周期;③ 单设备长时间运行禁止占尽共享 worker;④ 每条共享链路、每个驱动与全局 worker 池应当设置并发 / 资源预算;⑤ 设备执行超时后必须有明确的取消、隔离与退出路径,禁止任务标失败但后台 I/O 继续占用资源。公平性定义:持续负载下每种优先级及每台启用设备都要满足其配置的最大调度延迟与数据新鲜度目标。EDF 与防饿死钳制的优先级关系、过期任务丢弃策略、关键任务保留规则、共享串口的串行预算、每设备最大并发任务数、任务取消后底层 I/O 已停止的确认方式,应当显式定义。

SCAN-004 阈值族拆分(修订 2×PollPeriod 口径)

分开定义并可按协议 / 设备类型 / 采集周期配置:数据陈旧阈值(多久未更新告警)、单次操作超时、故障识别窗口、自动恢复预算、故障升级阈值。

严重性综合判定(v2.4):2×PollPeriod 保留为初始告警基线,但不得单独决定所有场景 P0/P1——单个关键联锁 / 供电 / 制冷控制点停更,可能比大量非关键环境点停更更严重;维护期间明确停用的设备不应持续触发同等级故障。严重性应当按「数据陈旧程度 + 影响范围 + 点位关键性 + 是否影响控制闭环 + 持续时间」综合判定,阈值分 Warning / Critical / RecoveryBudget 三档;所有阈值必须有合理上下限、启动时校验与实际生效值日志,禁止错误配置导致告警永不触发或持续误报。

SCAN-005 采样时间与数值变化分离(必须,v2.4 新增)

数据新鲜度必须依据最近一次有效样本时间判定,不得要求数值必须变化才刷新新鲜度(温度 / 电压等稳定量连续成功采样可能数值恒定,按值变化判陈旧会误报)。最近有效采样时间、最近数值变化时间、最近采集流程成功时间、最近状态提交时间应当语义独立(见 SCAN-002)。恢复判据兼容「值未变化但采样成功」:恢复成功的表述是「取得有效新样本并成功提交,采样时间戳与质量状态已更新」,数值是否变化单独记录为 LastValueChangedAt。

SCAN-006 任务合并语义约束(必须,v2.4 新增)

任务合并只适用于明确允许丢弃中间周期的周期采样任务。以下任务不得因周期合并策略被覆盖或无声丢弃:控制写入 / 事务任务(保序、独立队列上限与拒绝策略);边沿触发动作;不可丢失事件 / 告警(多次事件不得简单覆盖成一次最新值,除非数据模型明确支持聚合);状态核验 / 健康探测任务(可过载降频但禁止完全饿死)。各类任务必须有独立的队列 / 并发上限、过载策略、取消语义与诊断指标。


2. 细则 2 —— 先关后开,升级为连接生命周期契约(CONN)

目标状态:旧连接未确认关闭成功前禁止拨号。凡因链路异常、健康看门狗触发、配置变更(改 IP / 端口 / 从站号 / 波特率)引起的重新建立链接,必须在拨号新链路之前完成旧链接确认关闭。丢弃引用照常拨号 = 串口 fd / 从站连接位(现场常见仅 8 / 16 个)逐步泄漏,最终永远无法建立新连接。

落实分级:核心机制(ConnectionManager + 各协议 Disconnect)已强制;逐驱动「先关旧再开新」缺专项边界测试(仅 BACnet 有等价 restart_race_test.go),见缺口 C-1。

CONN-001 「连接已关闭」是可验证状态,不是一次调用

调用 Disconnect() 不等于旧连接生命周期结束。必须区分五个事件:① 停止接收新 I/O → ② 在途 I/O 已结束或被明确取消 → ③ 底层资源已释放 → ④ 旧连接后台任务(读循环 / 监听循环 / 异步回调)已退出或受有效隔离 → ⑤ 生命周期切换完成。仅事件 ⑤ 满足后才允许安装新连接。UDP 长驻监听、串口读取循环、OPC UA 会话等实现的证明方式可以不同,但外部契约必须一致。

关闭失败的保守语义(修订「同一 fd 反复重试关闭」表述):保留连接生命周期对象及其状态,禁止因关闭错误直接丢弃连接管理信息;由 transport 层给出明确的关闭结果与资源释放状态,禁止只靠含义模糊的布尔值;仅允许符合具体 transport 语义的幂等关闭 / 取消 / 释放操作;无法确认资源已释放时,禁止安装新的同资源连接,进入有限重试、重新核实或资源异常状态(衔接 CONN-006);底层资源无法自动确认释放时必须留明确故障记录,禁止把状态直接重置为 Disconnected。

CONN-002 旧连接隔离

生命周期流程固定为:禁止新增 I/O → 协调在途操作 → 终止旧连接 → 确认资源释放 → 隔离旧生命周期 → 安装新连接 → 恢复调度。

CONN-003 锁的三职责边界

锁 / 机制 负责 禁止
生命周期互斥 串行化连接创建、关闭及替换 同一链路并发安装多个有效连接
状态互斥 保护状态、引用、计数器一致性 持锁执行网络 RPC、长时间等待
I/O 协调 协议要求的请求串行化、在途统计、取消顺序 无边界等待;因锁竞争无限阻塞其他设备

统一生命周期契约,不强迫所有驱动使用相同 I/O 锁模型(有的协议允许并发读,共享串口总线必须严格串行)。等待 / 退避 / 冷却期间禁止持有会阻塞状态更新的互斥锁;禁止在持有通道状态锁时调用可能无限等待的外部代码。

CONN-004 连接代际标识(generation)

每次成功切换后的连接应当分配递增 generation。旧采集任务晚于重连返回时:generation(旧) != currentGeneration → 禁止旧结果覆盖新一代连接状态。验收:除证明新建连接前旧连接已正确释放外,还必须证明旧一代连接的迟到事件不能更改新一代状态。

CONN-005 并发重建约束

同一链路的并发重连请求必须合并(single-flight);生命周期互斥保证同一链路不会产生竞争性重复连接。

CONN-006 资源释放确认与 ReleaseUncertain 状态(v2.3 新增)

必须区分三层释放对象:本地 socket(net.Conn.Close() 只保证解除本机阻塞读写与本地 fd 释放)、驱动后台任务(读循环 / 监听协程退出)、远端设备内部连接会话(PLC / 仪表的连接槽位,本机无法直接证明其已清理)。Go 的 Close() 成功不得作为远端设备已释放连接槽位的通用证明。

连接生命周期应当增加 ReleaseUncertain(释放不确定)状态:

  • 进入条件:本地关闭返回错误、后台任务未按期退出、或协议语义无法确认远端会话已清理。
  • 约束:处于 ReleaseUncertain 的资源,禁止对同一资源快速创建新连接;改为低频核验 + 受控重试 + 保留诊断信息,直至确认释放或进入人工可查的资源异常状态。
  • 与 BUD-004 单资源最小重连间隔联动:ReleaseUncertain 期间的重连间隔必须不低于普通退避上限。
  • 成熟度:已定义(未实现,见缺口 C-8)。

3. 南向恢复约束(REC)

REC-001 故障五分类契约

类别 典型表现 处理者
设备级故障 从站号错误、对象不存在、设备忙、响应异常 设备采集与隔离机制
请求级故障 单次超时、响应格式错误、请求被拒 当前请求与设备级策略
链路级故障 socket 断开、串口失效、连接状态不可恢复 连接管理器
网络级故障 网卡断开、路由不可用、网关网络路径失效 网络诊断与受控全局恢复
内部运行故障 panic、后台循环退出、状态不一致、资源异常 组件自恢复或明确故障隔离

五类不完全互斥(网络不可达可表现为一次请求超时),必须依据底层证据归类,禁止仅凭错误字符串含 timeout 判级。驱动负责按协议响应码 / 传输错误 / 连接健康证据分类;统一恢复层按分类决定动作。禁止让 ConnectionManager 单凭一个字符串错误推断所有协议的故障层级。分类不确定时必须进入有限诊断流程,禁止无限重试或无限等待。

FailureUnknown 终态策略(v2.4):诊断流程结束后必须收敛到合法结果之一——① 有新证据,归入设备 / 请求 / 链路 / 网络 / 内部故障;② 仍无法确定,保持保守状态 + 限频探测;③ 达到告警阈值,报告「不可判定故障」。「无法确定」是合法但可观测的结果。诊断窗口、最大并发探测数、探测频率、升级告警条件应当显式定义;禁止为推进状态机把 Unknown 强行改判 LinkDown,也禁止 Unknown 永久挂起、无日志、无探测。

REC-002 南向链路重建准入铁律:设备未全部离线禁止重建(必须级,强制保留)

规则说明:一条链路上挂接多台设备时,只有该链路全部设备均离线才允许重新建立连接;链路上仍有任一设备在线 / 未确认离线时,禁止重建链路。此为工程铁律(必须级),任何优化、重构、评审豁免不得移除或弱化(2026-10-16 所有者裁定,强制保留;v2.0 曾按评审意见以证据准入替代,v2.1 撤销该修订)。

  • 适用场景 / 链路类型:Modbus TCP 多从站共享单 TCP(最典型,如一条链路挂 8 台从站)、Modbus RTU / RTUOverTCP 串口总线、S7 类 PLC 单链路;泛指一切「一链路多从站」的共享链路。
  • 链路归组口径(v2.3 增补):设备必须按实际共享的物理连接资源归组(同一 TCP 连接、同一串口、同一 commInterface),禁止不加区分地把整个通道当成一条链路——通道内多条独立链路互不影响各自的准入判定。
  • 触发条件:链路级故障(isChannelLinkError 且 !isChannelLinkUp,或 transport Health()==Bad、collectFailCount ≥ maxFailCount)触发重连请求时。
  • 判定逻辑(v2.3 升级为四条件合取):

    允许重建 = 链路故障已确认 ∧ 全部关联设备已确认离线 ∧ 旧连接生命周期已终止(CONN-001 事件 ⑤) ∧ 重连预算允许(BUD)

    • 条件 2 的「已确认离线」是严格口径:onlineCount 只统计处于确认离线状态的设备;Unknown(短暂重连中)、状态过期(超过陈旧阈值未刷新)、并发回写中的状态均不计入在线也不计入确认离线——状态未知不得让计数失真地放行或永久拒绝。
    • 并发防护(v2.3 增补):准入检查与采集状态回写必须原子衔接——检查通过后、重建完成前,旧连接的迟到采集结果禁止把设备回写为在线(复用 CONN-004 generation 或等效状态锁),防止「检查时全离线、检查后被旧结果翻回在线」的竞争。
    • 单设备链路特例:该链路仅 1 台设备时,该设备离线即等价「全部离线」→ 允许立即重建(仍须过其余三个条件)。
  • 恢复路径分工(v2.2 增补,所有者裁定):链路上存在存活在线设备期间,个别离线设备的恢复由防饿死机制承担,不得以重建链路替代:
    • 链路未重建时,离线设备的恢复路径 = 防饿死调度(processReadyTasks / enforceAntiStarvation,见 SCAN-003)+ 周期采集探测——设备随链路恢复通信即自然回写在线,禁止为单台设备离线而触发链路级重建;
    • 防饿死探测与链路重建是两条独立恢复通道:前者修设备,后者修链路;在线设备存活期间永远只走前者。
  • 长连接复用优先(v2.3 增补):共享链路仍有设备正常工作时,必须复用现有连接继续采集,离线设备走调度探测恢复;仅当链路级故障确认、关联设备状态同步失效(REC-004)、旧连接释放与重连预算检查全部通过后,才进入重建流程。
  • 防耗尽动机:未全离线即照常重建 = 反复拆除仍在服务的共享连接,串口 fd / 从站连接位(现场常见仅 8 / 16 个)与 PLC 连接槽位被逐步耗尽,最终永远无法建立新连接——链路上有存活设备时重建,还会把存活设备的通信一并打断,等于用链路级动作制造新的设备级故障。
  • 门槛下限属性:本铁律是下限——协议可以更严格(如某物理总线拓扑确实要求所有从站先独立停机,应作为该协议附加约束并注明硬件原因),不得更松。
  • 成熟度:存在缺口(当前 checkChannelHealth 与各驱动 ScheduleReconnect 只判链路层 Health / collectFailCount,未校验四条件合取,见 §10 缺口 C-2 / C-9 / C-10)。实现禁止以「证据分级」替代本门槛(状态滞后风险由 REC-004 状态同步义务化解)。

REC-003 恢复范围与故障证据范围一致(审查红线)

恢复操作的影响范围不得大于已有故障证据所支持的范围,除非扩大范围是为了安全隔离共享资源并有明确记录。对应关系:

  • 设备级错误,禁止重建健康的共享链路(反例:单个 Modbus 从站返回异常响应就拆整条 TCP = 恢复范围扩大)。
  • 链路级错误,禁止仅把某台设备标记离线来假装恢复(反例:整条 TCP 已确认断开却只重试某台从站 = 恢复范围不足)。
  • 网络级错误,可以减少不必要的独立拨号,但必须保留周期探测和恢复触发条件。
  • 内部运行故障,禁止仅记录 panic 后就假定组件继续工作(见 SAFE-001)。
  • 故障分类不确定时,必须进入有限诊断流程,禁止无限重试或无限等待。

配套故障处理策略分级(辅助执行细则,不替代 REC-002 准入门槛):

当前证据 处理策略
单台设备返回异常从站号 / 设备忙 / 单设备超时,且共享链路仍健康 不拆除共享链路,只处理当前设备
链路状态 Unknown,无充分断链证据 不立即重建,进入有限时长的观察 / 探测 / 状态核实
已确认共享链路故障,且满足 REC-002 四条件合取 停止该链路新 I/O → 处理在途任务 → 关闭旧连接 → 完成生命周期隔离 → 进入重建流程
旧连接仍有未完成的关闭或 I/O 生命周期 禁止安装替代连接,继续有界等待 / 清理 / 报告资源释放异常
新连接建立成功但设备仍未成功采集 不得认定设备已恢复,继续诊断和退避
反复重建仍无法恢复 进入受控冷却或隔离,同时保留自动恢复路径和诊断证据

允许进入重建流程 ≠ 立即拨号:是否允许新连接安装,还必须通过旧连接释放(CONN-001 / CONN-002)、在途 I/O、并发拨号(CONN-005)和资源预算(BUD)检查。

REC-004 实施保障:在线状态同步义务(保证铁律门槛可达)

REC-002 的门槛依赖准确的设备在线状态。为化解评审识别的「状态滞后」风险(底层链路已失效但设备仍显示上次采集成功的在线态,导致计数长期拒绝重建),必须落实以下状态同步义务——以此化解风险,而非弱化门槛:

  • 链路确认断开时,必须同步失效该链路全部设备在线状态(markChannelDevicesOffline 已实现,channel_device_state.go:139),使「全部离线」条件可达。
  • 设备显示在线但其数据停更超过 SCAN-004 陈旧阈值、且链路已确认断开 → 必须强制置离线,禁止让在线状态长期滞后于链路事实(否则准入条件依赖只能通过恢复操作才能更新的状态,形成自愈死锁)。
  • 状态同步后的判定须在有限时间内收敛:禁止因状态刷新延迟使重连准入无限悬置(配合 REC-008 看门狗 15s 巡检节奏)。
  • 采集状态回写必须接受 CONN-004 generation 校验:迟到 / 旧代结果禁止回写在线状态(v2.3 与 REC-002 并发防护条款呼应)。
  • 单设备链路等价放行语义应当以注释 + 测试显式固化(原 G-4),避免后续误改为「必须多设备才放行」。

修订记录:v2.0 曾按 2026-10-16 专业评审意见以「基于故障证据的重建准入」取代本铁律;同日所有者裁定撤销该修订——「设备未全部离线禁止重建」为必须级铁律强制保留(REC-002),评审风险转由本条状态同步义务承接。

REC-005 错误分类联合判定:设备级错误不拆共享链路(原 R-1)

Modbus TCP / RTU 多从站:isDeviceLevelModbusError 命中 → 不累计 collectFailCount、不 Disconnect、不 ScheduleReconnect。成熟度:已单测。代码:internal/driver/modbus/transport.go:197(判据)、:219(RecordFailure 早退)、:634(withRetry 保持 TCP);modbus/isolation_test.go;文档 docs/南向采集通道决策方案.md §关键实现1、§已修复#1#2。

v2.4 修订——「i/o timeout 统一视为设备级错误」不成立:同一超时表象可来自不同层级,必须按「协议阶段 + 响应证据 + transport 健康 + 失败范围」联合判定,至少区分:

类别 判定要点
DeviceResponseTimeout 单设备未及时有效响应,但共享传输仍有健康证据(同连接其他从站通信正常)
TransportReadTimeout / TransportWriteTimeout 底层 I/O 超时,必须结合共享连接状态与其他从站结果判断,禁止永远按设备级处理
ConnectTimeout 连接建立阶段失败
ProtocolException 设备已返回明确协议异常
FrameDecodeError 收到数据但无法按协议解码
LinkClosed / TransportUnusable 存在明确链路不可用证据

重新分类判据(必须):同一共享传输连续失败达到阈值且共享传输健康证据丢失(同连接多从站连续无响应 / 连接健康检查失败)时,必须将错误重新分类为链路级并进入链路恢复——否则形成「连接状态未明确断开、共享链路事实上不可用」的永久停采。反向红线:禁止所有 timeout 一律触发重建。判产实现状态:已定义(当前 isDeviceLevelModbusError 为错误字符串分类,见缺口 C-12)。

REC-006 链路级重连须有断链证据(原 R-2)

采集回写 finalizeScanCollect:仅 isChannelLinkError(err) && !isChannelLinkUp(driver) 才 markChannelDevicesOffline;链路仍通则只对当前设备 FinalizeCollect。成熟度:已单测。代码:internal/core/channel_manager.go:397-441;internal/core/channel_device_state.go:11;core/channel_device_state_test.go:142/162。

REC-007 Health()==Unknown 不算断链(原 R-3)

仅显式 Bad 视链路断;Unknown(短暂重连中)跳过。成熟度:已实现。代码:internal/core/channel_device_state.go:40-47。与 REC-002 的「有限时长探测核实」衔接:Unknown 不重建但必须有界核实,不得永久悬置。

REC-008 看门狗防抖 / 冷却 / single-flight(原 R-4)

channelHealthLoop 每 channelHealthInterval=15s;channelReconnectCool=30s 冷却抑制重复;channelReconnectTimeout=5min;只对 ReconnectScheduler 驱动生效。成熟度:已单测(core/channel_health_watchdog_test.go)。代码:internal/core/channel_manager.go:121-204。

超时语义必须分别定义(v2.4):channelReconnectTimeout=5min 不可混用为多种含义——必须区分 dialTimeout(单次拨号最大时间)、recoveryAttemptBudget(一次恢复尝试允许经历的阶段与总时间)、reconnectWaitTimeout(调用方等待恢复结果的最长时限,调用方通常不应同步等待完整恢复)、recoveryLeaseTimeout(恢复工作所有权租约,超时后必须先确认旧恢复 goroutine 已被取消或隔离,禁止直接清空 reconnectRunning 标志启动第二个并发恢复任务——否则新旧两个重连流程并发运行)。

REC-009 全局重连限流(原 R-6)

MaxGlobalReconnectRate=10(每秒)+ single-flight reconnectRunning CAS,防重连风暴打满连接槽。成熟度:已单测。代码:internal/driver/reconnect_limiter.go;connection_manager.go:78/233-266/453;connection_manager_test.go:268、connection_manager_reconnect_test.go:55。参数属性见 BUD-003;该全局限流是总体保护,不替代单资源节流(BUD-004)。

REC-010 单 PLC 单连接(原 R-7)

S7 等单设备高价值链路:每 PLC 最多 1 连接;长连接复用,避免频繁 Dial 耗尽端口。来源:docs/drivers/S7_Connection_Lifecycle.md:42-51、:514-520。成熟度:已定义(通用实现由各 driver 承担)。

REC-011 断网按目标网络路径判定(原 R-8,v2.4 修订)

工业网关可能同时存在管理网、设备网、无线链路与多个以太网接口。默认路由失效不代表 PLC 所在本地网段不可达;一个设备网可用不代表另一个可用。网络级恢复策略必须按目标地址 / 接口 / 路由域判定,禁止只依赖「网关在线 / 离线」全局布尔状态:每个目标链路必须保留低频健康探测与恢复触发条件;网络恢复判定应当使用连续成功或迟滞机制,避免链路抖动导致全量重连反复启停;网络不可达时暂停的是不必要的高频拨号,不是停止所有健康检查——恢复探测必须有界、轻量。来源:S7_Connection_Lifecycle.md:64、:299。成熟度:已定义,实现程度协议而异。

REC-012 异步连接不得变成无主 goroutine(v2.4 新增)

「API 路径禁止同步拨号」方向正确,但异步不等于安全。API 只能向受控的 ConnectionManager / ReconnectScheduler 提交幂等请求;实际连接工作必须由有界队列、single-flight(CONN-005)与连接生命周期所有者统一执行。禁止每次 API 请求直接 go driver.Connect()(会导致无限排队、并发拨号、绕过退避、绕过全局限流、关停时无法回收)。队列满时应当合并同一链路的重复请求或返回明确的「恢复已排队 / 正在恢复」结果,禁止无界创建 goroutine;关停时必须停止新提交并取消或排空已排队工作(衔接 SHUTDOWN-001)。


4. 恢复预算(BUD)

BUD-001 重试职责划分

请求级重试由谁负责、链路级重连由谁负责、哪类错误禁止自动重试、重试是否共享同一总 deadline、何时重置连续失败计数——必须显式定义。统一恢复层管理链路重建的退避与冷却;驱动可保留有界的协议内部重试,但禁止绕过总操作期限、设备隔离或全局连接预算。禁止多层重试独立叠加(驱动 3 次 × 采集层 3 次 × 重连多轮 = 实际请求次数与恢复时间远超预期)。读请求与写请求不得采用完全相同的重试策略——写操作幂等性约束见 BUD-005。

BUD-002 时间预算关系

每个采集任务应当有整体执行期限,单次 I/O 期限不超过剩余任务预算:T_IO ≤ min(T_协议上限, T_任务剩余预算)。对 Go 实现:底层驱动必须真正响应 context 取消——若阻塞 I/O 不理会 context,仅 context.WithTimeout 不能保证按期结束,必须有传输层 deadline 或明确的关闭 / 取消机制。

任务整体预算拆分(v2.4):T_task = T_queue + T_lock + T_protocol_retry + T_IO + T_decode + T_commit——任务整体预算必须覆盖排队、锁等待、协议重试、I/O、解析、状态回写,不只是网络读写时间。这是预算拆分而非固定耗时相加;任何阶段耗尽预算必须向上返回可分类的超时结果,禁止在下一层重新获取完整新预算。

BUD-003 退避 / 冷却 / 限流职责表

机制 目标
指数退避 同一故障持续存在时逐渐降低重试频率
冷却时间 避免短时间重复触发同一恢复流程
全局限流 防止大量设备同时故障形成重连风暴
单资源节流(BUD-004) 保护单台设备的连接槽位与释放速度
最大并发拨号数 限制同时占用的拨号 / 握手 / 连接资源
单链路 single-flight 合并同一链路的并发重连请求

现有 MaxGlobalReconnectRate=10 是当前策略参数,不是永久值。大量设备同时离线后恢复应当使用有界指数退避 + 随机抖动。退避应当在恢复条件充分满足后逐步复位(同时观察稳定连接时间与成功采集次数),禁止仅因 TCP 握手成功就立即复位到最低间隔。

按资源敏感度配置策略档位(v2.4):普通可快速释放的 TCP 设备、连接槽位极少的 PLC、串口共享总线、无连接状态的 UDP 发现协议不得强行使用相同重试间隔。HalfOpen 探测必须控制探测数量,禁止每个离线设备同时发起独立探测。设备持续异常应当转入低频探测而非永远加速重试;也禁止因达到最大次数就永久停止自愈。

BUD-004 单资源重连节流(v2.3 新增)

全局限流(REC-009)不替代单链路、单设备的重连间隔控制。必须增加:

  • 单资源最小重连间隔:同一物理链路 / 同一设备两次拨号之间的最小间隔,必须不低于该资源的退避上限约束;对连接槽位有限、会话释放缓慢的现场设备(PLC / 仪表),间隔取保守值。
  • 单资源最大并发拨号:同一资源上同时进行的拨号 / 握手数量上限(与 CONN-005 single-flight 叠加生效)。
  • ReleaseUncertain 状态(CONN-006)下的重连间隔必须不低于普通退避上限,并伴低频核验。
  • 重试失败后的路径区分(评审 P1 项):普通重试(同资源有界退避)→ 链路重建(REC-002 四条件合取)→ 资源释放异常(CONN-006 ReleaseUncertain)→ 隔离等待(受控冷却 + 自动恢复保留)。四条路径禁止混用,禁止任何一条退化为无限拨号。
  • 成熟度:已定义(未实现,见缺口 C-9)。

BUD-005 控制写入重试安全性(必须,v2.4 新增)

写操作必须根据协议及设备语义声明其幂等性。分类:幂等设值(重复写同一目标值通常可接受,仍需确认协议语义);非幂等动作(脉冲、复位、计数器递增、启停触发、告警确认、切换运行模式);结果不确定的写入(请求已发送但响应丢失,网关无法知道设备是否已执行)。

  • 只有明确幂等、协议支持事务标识 / 去重、或有安全的状态回读确认机制时,才允许按已定义预算自动重试;对结果不确定的非幂等命令,必须进入「执行结果待确认」流程,禁止无条件重发。
  • 写入失败日志必须区分「尚未发送」「发送结果明确失败」「可能已执行但确认丢失」,禁止只记一个笼统的 timeout。
  • 测试要求见 TEST-001(写结果不确定测试为强制项)。

BUD-006 共享连接 deadline 协调(必须,v2.4 新增)

Go net.Conn.SetDeadline 影响该连接未来及当前阻塞中的全部 I/O。多请求共享同一 TCP 连接时,各请求分别修改同一 deadline 会互相破坏超时边界。使用共享 net.Conn 的驱动必须确认:

  • 严格串行化完整过程「设定 deadline → 写请求 → 读响应 → 清理 / 刷新 deadline」,或由连接级协调器统一管理 deadline(禁止每请求任意覆盖);
  • timeout 后必须处理可能残留的未读响应,禁止下一设备请求误关联上一请求的响应;
  • deadline 清理路径必须覆盖成功、失败、取消与 panic 四条出口;
  • 串口 / UDP / 会话型协议按各自 transport 语义单独审计,context.WithTimeout 不得当作底层阻塞 I/O 必然可取消的证明。

5. panic 存活保障(SAFE)

SAFE-001 recover 四层语义

recover 阻止 panic 传播但不会让程序回到 panic 点继续执行——只在最外层包一层 defer recover() 时,常驻循环 panic 后仍然会退出,形成「进程正常、看门狗已消失」的假健康。

  • 单次任务层:单个任务 panic 不影响其他任务,明确记录失败。
  • 工作协程层:panic 后评估工作能力是否丢失,是否需要受控重建。
  • 常驻循环层:必须采用单次迭代可恢复结构(每轮迭代 recover + 记录完整调用栈与组件标识 + 明确错误处理流程),并评估 panic 后组件内部状态是否仍可信——禁止假设任何 panic 都可安全忽略。
  • 关键组件层:组件整体失效时如何隔离、尝试恢复及报告不可恢复故障。

必须监控关键工作协程的存活与工作能力(不只是进程存活);已退出的协程必须有受控重建策略或明确的组件故障状态。工程原则:recover 是故障隔离手段,不是业务错误处理的替代品,也不能直接等同于自愈完成。规范表述:所有承担采集调度、健康检查、连接恢复或关键后台服务职责的 goroutine,必须具有明确的 panic 隔离策略、失败记录和存活保障。

panic 后三态分类(v2.4):recover 只能捕获同一 goroutine 内向上展开的 panic——每个由驱动、worker、监听循环、回调新建的 goroutine 必须有自己的异常边界或明确退出契约(禁止只检查几个顶层入口)。捕获 panic 后必须落入三类结果之一:RecoveredAndContinue(已确认任务边界隔离、状态不变量仍成立才可继续);RestartWorker(状态不可信但组件可安全重建,按冷却与次数预算受控重建);ComponentFailed(不能安全恢复,停止对外服务、置明确故障状态并报警)。对重复 panic 必须限频 / 退避,禁止 panic—立即重启—再次 panic 风暴。recover 后记录组件、任务、连接代际与堆栈,并确保敏感数据不经堆栈 / 日志泄露。

SAFE-002 进度监控:区分「goroutine 活着」与「功能在推进」(必须,v2.4 新增)

worker 可能存在但阻塞在锁、驱动调用、通道发送或永不返回的 I/O 上——禁止只统计 goroutine 数量或任务状态作为存活证据。关键后台 goroutine 应当用低开销 heartbeat / progress 标识记录「最近一次成功推进时间」(阶段进度:最近排队 / 最近启动 / 最近 I/O 完成 / 最近成功回写,衔接 SCAN 防饿死),并与任务期限结合判定。监控机制不得依赖被监控的同一条已卡死执行路径;不宜为每个循环叠加大量独立看门狗——优先统一轻量组件存活表 + 进度指标,异常后由明确的生命周期所有者处理。监控「有进展」而非仅「线程活着」。


6. 细则 3 —— 程序退出 / 重启关闭顺序(SHUTDOWN)

SHUTDOWN-001 硬序契约

① 停 Web / 边缘计算与调度 → ② cm.Shutdown() 逐驱动 Disconnect 关南向(fd/socket/UDP 归零,关闭事件落盘)→ ③ pipeline.Stop / ShadowCore / bbolt flush → ④ 最后 logger.Close()(先 flush 再退写协程)。logger.Close 必须排在所有南向 Disconnect 之后;禁止「向已关 logger 写日志」竞态。Web 重启端点必须与 SIGTERM 走同一优雅关停路径。

生产者 / 消费者阶段屏障(v2.4 必须):关停必须建立可等待的阶段屏障,而非仅靠函数调用书写顺序:① 进入 Draining 状态——拒绝新配置变更 / 控制命令 / 外部启动请求,重启端点与信号路径统一进入;② 停止新任务生产——停止新扫描入队、新周期任务、新重连任务;③ 排空或取消在途任务——有界等待,超时取消并记录未完成任务,确认底层 I/O 已停止或被隔离;④ 关闭南向连接与驱动后台循环——必须保证关停过程不再触发新重连;⑤ 停止数据管线生产者并排空消费者——确认所有可能写 pipeline / ShadowCore / bbolt 的组件已停止写入后再 flush / close 存储;⑥ 最后关闭日志。禁止消费者已关闭而生产者仍写入。

SHUTDOWN-002 关停有期限、有结果、区分升级交接

阶段 操作 超时后处理
1 停止接入 停止新采集 / 控制 / 管理任务进入 拒绝或明确返回停止中状态
2 排空任务 等待在途任务完成,必要时取消 记录未完成任务,进入强制取消
3 关闭南向 停止新 I/O,关闭各驱动与共享链路 记录具体未释放资源,不无限等待
4 数据落盘 刷新状态、缓存与持久化数据 限期内完成或明确记录失败
5 关闭日志 刷新日志队列并停止写入协程 记录无法完成的刷新
6 进程退出 统一退出路径 由外部监督机制处理超时进程

各驱动必须真正支持取消(单纯 context.WithTimeout 不保证关停按期终止)。必须覆盖:正常退出、网络 I/O 持续阻塞、串口 / socket 关闭返回错误、日志异步队列未刷新、升级触发重复执行、新进程启动时旧进程未释放设备资源、外部 supervisor 提前再拉起。升级场景:旧进程确认退出且串口 / socket 等独占资源满足释放条件后,新进程才能接管连接责任。

预算与失败处理(v2.4):关停必须有全局总预算 + 单阶段预算;某阶段超时后必须明确处置——停止等待、取消操作、记录未释放资源、继续关闭其他独立组件或最终退出进程;禁止因单个驱动卡住阻塞所有其他链路关闭,也禁止把失败伪装成成功。进程退出会由 OS 回收本地 fd,但不保证远端设备立即释放会话槽位——升级系统发现旧进程未在预算内完成关闭时,必须先确保旧进程确已终止,并对资源敏感设备采用保守启动节奏,禁止形成快速重启风暴。验收表述修正:进程退出后无法从该 PID 查询 fd——必须在退出前采样 fd / 资源计数,或在测试驱动中记录关闭确认后再验证进程退出;「/proc/<pid>/fd 无残留」应当写成可实际执行的测试步骤。


7. 可观测性契约(OBS)

OBS-001 关键事件字段与职责划分

重连、断链、资源释放失败、饿死、看门狗触发、panic 等关键日志应当至少包含:event / channel_id / device_id / protocol / generation / old_state / new_state / reason(可枚举)/ attempt / elapsed_ms / error / 关联 ID——关键生命周期事件必须能被关联成完整恢复过程。职责划分:日志记事件、指标记趋势(连接失败率 / 资源数量 / 数据陈旧时间 / 恢复耗时)、内部状态支撑实时决策、诊断接口查询现状(禁止用诊断接口代替恢复本身)。限频禁止丢失故障计数:被合并的事件应当用计数器记录;首次故障、状态升级、恢复成功、关闭失败等关键转折点必须可追踪。

字段值与脱敏规则(v2.4):reason 必须使用稳定枚举码,错误原文另存为可控长度的诊断字段;日志正文应当设长度上限,禁止异常报文或第三方库错误字符串导致单条日志无限增长;连接地址、用户名、证书路径、认证内容、完整控制报文等敏感 / 高频信息禁止无条件写入日志;elapsed_ms 等时长必须用单调时钟计算(系统时间调整只影响展示时间,不影响耗时结论)。

OBS-002 指标基数预算(v2.4)

channel_id / device_id / 每次重连的 trace ID 可用于日志与诊断查询,但禁止在未经容量评估时无条件作为指标标签——设备规模上升会导致时间序列数量与内存开销失控。全局 / 协议级指标应当统计速率、失败率、连接数、资源数与数据陈旧分布;设备级细节优先走诊断接口或结构化事件查询;只有在设备数量与指标后端容量明确允许时才使用设备 ID 作指标标签,并定义上限与保留周期。

OBS-003 可观测数据预算与限频补偿(v2.4)

日志正文、错误字段与事件频率必须有边界(衔接 OBS-001 字段规则)。限频必须有统计补偿:至少保留首次故障、故障升级、周期摘要、恢复成功、关闭 / 释放失败、最终退出隔离等事件;重复日志被抑制时应当增加 suppressed-event 计数或摘要,禁止完全静默。connectionSeconds 持续增长与设备 Offline 并存可作为诊断线索,但不得单独作为饿死唯一指纹——应当同时结合最近有效采样时间、链路健康证据、任务进度与回写状态,减少误报。


8. 驱动能力矩阵(DRV)

能力 统一要求
连接生命周期管理 能区分建立、可用、关闭中、释放完成(含 ReleaseUncertain)
单链路并发保护 同一物理链路不产生竞争性重复连接
I/O 取消与超时 阻塞操作有有效终止路径
故障分类 设备级与链路级错误不混淆(REC-001)
统一重连策略 冷却、退避、限流、单资源节流由统一机制协调
资源释放验证 能确认本地句柄与关联后台任务生命周期;远端会话按 CONN-006 保守处理
状态恢复验证 采集成功后才能恢复数据健康状态
自动化测试 关闭失败、并发重连、超时、故障恢复测试齐备
资源拓扑 / 共享范围(v2.4) 明确一个连接 / listener / 总线实际服务哪些设备(支撑 REC-002 链路归组)
I/O 并发模型(v2.4) 支持并发、单请求串行或整条总线串行
读 / 写幂等性(v2.4) 决定失败后是否允许自动重试控制写入(衔接 BUD-005)
健康证据来源(v2.4) Health / Unknown / Bad 的具体依据与更新时机
恢复成功条件(v2.4) 握手成功、协议响应成功,还是已恢复有效数据采集(衔接 TEST-002)
模拟器 / HIL 条件(v2.4) 可用何种方式验证边界与现场差异

正式豁免六要素(取代「允许偏离」):不适用原因、当前替代机制、未覆盖风险、对应测试证据、责任模块、复审条件。v2.4 增补生命周期字段:生效日期、到期 / 复审日期、风险接受人、触发撤销的条件、升级路径。缺少全局限流或关闭确认的协议不得视作与统一恢复机制具有相同可靠性保证。已知缺口:omron-fins / mitsubishi-slmp / snmp 未绑 LinkMutexBinder、无 ConnectionManager(架构 11 §1.2 已注明)。「统一重连策略」指策略契约统一,不等于所有协议使用相同 transport 或恢复内部实现。


9. 验收门禁(TEST)

TEST-001 验收矩阵

测试类型 注入条件 核心通过条件
单元测试 关闭操作返回异常 不错误清空生命周期状态、不跳过资源释放门禁
时序测试 旧连接 I/O 阻塞,新连接同时请求建立 新旧连接不未经协调同时进入有效状态
并发测试 多 goroutine 同时触发重连 同一链路只执行受控重建流程
状态机测试 Unknown / Bad / 在线状态过期组合 不错误分类故障、不永久禁止恢复
并发回写测试(v2.3) 准入检查通过后旧采集结果迟到 旧结果不把设备翻回在线、不绕过 REC-002 门槛
故障注入 单设备异常 / 共享链路断开 / 网络断开 恢复作用范围正确
生命周期测试 连接重建后旧请求延迟返回 旧一代结果不覆盖新一代状态
资源测试 高频反复建立、关闭、失败、重试 fd / socket / goroutine 无持续泄漏趋势
连接槽位耗尽专项测试(v2.3) 模拟设备端槽位有限且释放缓慢,高频重建 单资源节流生效、不进入永久拨号循环、ReleaseUncertain 正确进入并退出
关停测试 停止信号到达时 I/O 正在执行 关停有界、顺序正确、结果可追踪
长期稳定性测试 24–72h 持续采集 + 反复故障注入 无持续资源增长、无永久停采、无恢复退化
工程回归 完整测试 + -race 无已知并发缺陷,强制门禁全通过
写结果不确定测试(v2.4) 写请求已发送、设备已执行但响应丢失 不产生重复控制动作;API 与状态机明确呈现「执行结果待确认」(控制型网关强制项)

验证分层(v2.4):单元 / 状态机测试(快速确定穷举状态转换)→ transport / 驱动集成测试(真实协议栈、关闭语义、timeout 行为)→ 故障注入测试(慢响应、丢包、断网、重复触发、任务取消失败、部分设备可用、写响应丢失)→ 长稳与资源趋势测试(fd/socket、goroutine、内存、队列长度、重试速率、数据新鲜度)→ HIL / 现场验证(模拟器难以重现的设备固件、连接槽位、串口适配器、非标准协议行为)。24–72h soak 不得单独作为可靠证明;go test -race 不能证明外部设备协议行为正确,不得替代资源释放与现场硬件验证。

TEST-002 恢复成功的严格判据

禁止仅以「连接建立 / 状态切 Connected / 重连返回 nil / 看门狗不再告警」判定恢复。有效判据:① 底层连接建立且有效;② 设备按协议完成一次有效通信;③ 数据成功解析并更新到正确设备 / 测点;④ 数据新鲜度恢复正常;⑤ 旧任务与旧连接不再能覆盖当前状态;⑥ 有连续成功判定机制的满足相应稳定性条件。兼容「值未变化但采样成功」(v2.4):判据表述为「取得有效新样本并成功提交,采样时间戳与质量状态已更新」,数值是否变化单独记录为 LastValueChangedAt(衔接 SCAN-005)。无读取数据的协议按其实际功能设计判据并明确说明。

TEST-003 PR 工程铁律检查表

涉及采集、驱动、并发、连接、恢复的修改必须逐项填写:

  • 是否说明本次修改涉及的铁律与规则编号?
  • 是否存在新增阻塞 I/O、goroutine、锁或后台循环?
  • 是否明确超时、取消、退避和最大等待边界?
  • 是否区分设备级 / 链路级 / 网络级故障?
  • 是否验证连接关闭、资源释放、新连接建立之间的时序(含远端槽位释放的保守处理)?
  • 是否可能出现旧任务覆盖新连接状态的情况?
  • 是否评估慢设备、任务积压和数据新鲜度影响?
  • 是否补充失败路径、并发路径及必要的故障注入测试?
  • 是否具有明确的测试结果、风险说明和回退方案?

TEST-004 发布判定分类

通过 / 失败 / 未验证 / 有条件豁免 四态。未验证不等价于通过;有条件豁免不得记录为已修复。稳定性测试结果纳入版本发布判定(衔接 RELEASE_GATE.md G-Industrial 门禁)。

发布阻断规则(v2.4 必须):① P0 规则相关测试失败,阻断发布;② 关键连接生命周期、控制写入幂等性、永久停采等强制门禁未验证,默认阻断对应范围发布;③ 有条件豁免必须限定影响范围、期限、责任人与风险缓解措施,对资源泄漏、重复控制动作等无法接受的风险禁止无期限豁免;④ 任何测试被跳过或模拟器不能覆盖的行为必须在发布记录写明,禁止归入「全部通过」。

TEST-005 门禁量化(必须,v2.4 新增)

每项强制测试必须注明环境、设备 / 模拟器类型、负载、故障注入条件、观测窗口、通过阈值与结果证据。定性结论(「无泄漏」「恢复有界」「性能未退化」)必须在发布门禁或设备配置档案中有对应数值或可重复判据:固定设备数量 / PollPeriod / 故障注入比例下的 Scan Lag P95/P99 允许值;连接尝试次数 / 单位时间、单资源最大并发拨号数;活跃 fd/socket、goroutine、队列长度、内存稳态波动范围;单点与整链路数据陈旧告警阈值、恢复预算与 P0/P1 判定;关闭失败、旧代际回写、未确认释放后拨号等违规事件必须为 0。数值禁止在铁律主文中编造统一常数——按硬件能力、协议模型、设备数量、轮询周期与现场 SLA 制定,版本化并记录测试环境。


10. 缺口清单

编号 描述 关联规则 成熟度
C-1 逐驱动 connectOnce「先关旧再开新」缺专项边界测试(仓库无 close_before_open_test.go,仅 BACnet 有等价 restart_race_test.go) CONN-001/002 存在缺口
C-2 重建准入只判链路层(Health / collectFailCount),REC-002 四条件合取未实现;看门狗 checkChannelHealth(channel_manager.go:160-204)与各驱动 ScheduleReconnect 均无全部离线校验 REC-002 存在缺口(关键)
C-3 omron-fins / mitsubishi-slmp / snmp 无 ConnectionManager,不受 REC-008/009 保护 DRV 已知缺口(架构 11 注明)
C-4 连接代际 generation 未实现,迟到结果防护缺机制保证 CONN-004 存在缺口
C-5 常驻循环 panic 后存活验证与协程重建策略未落地 SAFE-001 存在缺口
C-6 时间戳族(SCAN-002)与点位级新鲜度未实现,仅有 LastCollectTime SCAN-002 存在缺口
C-7 关停阶段超时与升级交接失败路径未系统化(SHUTDOWN-002) SHUTDOWN-002 存在缺口
C-8 ReleaseUncertain 状态未实现;本地关闭与远端会话释放未分层确认 CONN-006 存在缺口(v2.3 新增)
C-9 单资源最小重连间隔与最大并发拨号未实现,仅全局限流 BUD-004 存在缺口(v2.3 新增)
C-10 准入检查与采集状态回写的并发原子性未保证 REC-002 并发防护 / REC-004 存在缺口(v2.3 新增)
C-11 连接槽位耗尽专项测试缺失 TEST-001 存在缺口(v2.3 新增)
C-12 错误分类为错误字符串匹配,未做「阶段 + 证据 + 传输健康 + 范围」联合判定,无重新分类判据 REC-005 存在缺口(v2.4 新增)
C-13 共享 net.Conn deadline 协调未专项审计(残留响应误关联风险) BUD-006 存在缺口(v2.4 新增)
C-14 关停生产者 / 消费者阶段屏障与全局关停预算未落地 SHUTDOWN-001/002 存在缺口(v2.4 新增)
C-15 RELEASE_GATE.md 缺量化阈值(Scan Lag、重试速率、资源波动、违规事件=0) TEST-005 存在缺口(v2.4 新增)
C-16 写操作幂等性声明与「执行结果待确认」流程未实现 BUD-005 存在缺口(v2.4 新增)
C-17 DRV 能力新字段与豁免生命周期字段未建档 DRV 存在缺口(v2.4 新增)

11. 拟并入《DEVELOPMENT_PRINCIPLES.md》的八项原则(先行生效)

一、故障隔离原则:故障处理影响范围与已确认故障范围一致;单设备故障不得无依据扩大为共享链路故障;共享链路故障不得仅靠重试单设备掩盖;类型不确定必须进入有界诊断。 二、连接生命周期原则:连接必须有创建、使用、关闭、释放边界;旧连接生命周期未终止时禁止安装竞争资源的新连接;不得通过丢弃引用、重置状态或忽略关闭错误绕过资源释放检查。 三、采集公平性与数据新鲜度原则:任何启用设备不得长期失去调度机会;连接状态、最近成功采集、数据新鲜度必须可区分;链条任一环节持续异常必须可检测诊断。 四、恢复有界原则:自动恢复必须有等待上限、重试策略、并发限制、退出条件和失败记录;单次恢复失败不得演变为无界资源占用、无界重试或永久丧失恢复机会。 五、状态一致性原则:重连、配置变更、任务取消、后台异常不得使旧连接 / 旧任务 / 迟到事件无条件覆盖当前有效状态。 六、关闭与退出原则:正常退出、升级重启、运行时重建必须使用明确可验证的资源释放流程;关停必须有超时边界;南向释放、数据处理、日志刷新完成前不得提前终止相应阶段。 七、可观测性原则:关键故障、状态转换、资源释放失败、恢复结果必须可经日志 / 指标 / 诊断状态追踪;日志不得成为恢复前置条件,限频不得丢失故障计数与最终恢复结果。 八、验证先于交付原则:连接管理、并发、调度、恢复的修改必须提供与风险匹配的自动化测试与故障验证证据;设计存在、编译通过、短时正常均不构成满足证明;未验证要求明确标记,不等同通过。


12. 修订路线(三阶段)

第一阶段:修正规则与正确性缺口——落地 REC-002 四条件合取准入(C-2/C-9/C-10),保证链路真实失效时准入可达且不被迟到结果绕过;明确关闭结果 / 资源释放(含 ReleaseUncertain)契约(C-8);审计 linkMu / channelMu 实际锁范围;补关闭失败、重复重连、在途 I/O、迟到结果、槽位耗尽专项测试(C-1/C-11);验证 panic 后常驻循环存活(C-5)。v2.4 追加 P0 项:REC-005 联合判定与重新分类(C-12)、BUD-005 写幂等性(C-16)、BUD-006 共享 deadline 审计(C-13)、REC-011 目标路径网络诊断、SHUTDOWN 阶段屏障(C-14)。

第二阶段:统一跨协议恢复能力——驱动能力矩阵建档;存量协议纳入统一重连或正式豁免;统一故障分类、超时预算、限流与单资源节流;端到端数据新鲜度与恢复成功判据;优雅关停与升级重启失败路径。

第三阶段:持续验证与规则治理——铁律 → 不变量 → 代码检查点 → 测试用例 → 验收指标 → 发布决策的编号映射进 CI;故障注入与长稳固定回归;未验证与豁免项纳入版本记录;每次稳定性复盘后增补规则或测试。


附录 A:v2.2 工业现场专项评审风险登记表(2026-10-16)

评审基于文档及其中记录的实现状态,尚未逐一核对仓库源码与现场设备;评审推断不得直接当作已确认的代码缺陷,落地前须源码审计。

优先级 风险 评审判断 本版处置
P0 全部设备离线的判定口径不够严格 onlineCount == 0 不一定等于所有设备都已确认离线 REC-002「已确认离线」严格口径(Unknown / 过期 / 回写中不计入)+ C-10 并发防护
P0 关闭本机连接与释放远端设备槽位未区分 Close() 成功不能普遍证明远端协议会话已清理 CONN-006 三层释放对象 + ReleaseUncertain 状态(C-8)
P0 重建准入检查与采集状态更新可能并发竞争 需防止旧采集结果在检查后重新写入在线状态 REC-002 并发防护条款 + CONN-004 generation + REC-004 回写校验
P0 全局限流未体现单台设备承受能力 10 次/秒全局限制不能替代单链路 / 单设备重连间隔控制 BUD-004 单资源最小重连间隔 + 最大并发拨号(C-9),全局限流保留为总体保护
P1 重试失败后资源耗尽路径需明确 必须区分普通重试 / 链路重建 / 资源释放异常 / 隔离等待 BUD-004 四路径区分 + REC-002 合取准入 + CONN-006

附录 B:v2.4 其他章节专项评审风险登记表(2026-10-16)

同附录 A:评审基于文档及实现状态记录,未逐一审计源码 / 全部驱动 / 真实设备;「建议检查」不等于确认代码缺陷。

优先级 风险 评审判断 本版处置
P0 i/o timeout 统一视为设备级错误 同一超时表象来自不同层;误判导致永久停采或误拆链路 REC-005 联合判定分类表 + 重新分类判据(C-12)
P0 FailureUnknown 无终态策略 Unknown 可能被强改 LinkDown 或永久挂起 REC-001 终态策略(有界诊断 + 合法「无法确定」可观测)
P0 网关断网按全局布尔判定 多网卡 / 多网段下默认路由失效 ≠ 目标不可达 REC-011 按目标地址 / 接口 / 路由域 + 迟滞恢复
P0 channelReconnectTimeout=5min 语义不明 拨号期限 / 恢复总预算 / 等待上限 / 所有权租约不可混用 REC-008 四超时分别定义 + 租约所有权回收
P0 异步连接变无主 goroutine go driver.Connect() 可绕过退避 / 限流 / 关停回收 REC-012 受控队列 + single-flight + 生命周期所有者
P0 写请求与读请求相同重试策略 非幂等控制动作重复执行是工业现场高危 BUD-005 写幂等性三分 + 禁止无条件重发 + 写结果不确定测试
P0 共享 TCP deadline 互相破坏 SetDeadline 影响连接未来及当前阻塞 I/O BUD-006 deadline 协调五条 + 残留响应防护
P0 时间预算只覆盖网络读写 排队 / 锁 / 重试 / 解析 / 回写可能失控 BUD-002 T_task 六段拆分 + 禁止下层重新获取完整预算
P0 关停靠函数书写顺序 消费者已关而生产者仍写入 SHUTDOWN-001 六阶段屏障 + Draining 状态 + 关停不再触发重连
P0 关停失败被伪装成功 / 无限等 单驱动卡住阻塞全局;重启风暴 SHUTDOWN-002 全局 + 单阶段预算、失败处置、退出前 fd 采样
P1 LastDataUpdateAt 语义歧义 值不变误报陈旧;任务执行刷新又伪装新鲜 SCAN-002/005 时间戳拆分 + 数据质量状态
P1 任务合并无条件应用 控制写入 / 事件可能被无声丢弃 SCAN-006 四类任务禁止合并
P1 recover 存在≠功能恢复 循环 panic 后退出;goroutine 假存活 SAFE-001 三态 + 每 goroutine 边界;SAFE-002 进度监控
P1 高基数标识进指标标签 设备规模上升导致时序膨胀 OBS-002 指标基数预算 + OBS-003 限频补偿
P1 定性验收门禁 「无泄漏 / 恢复有界」不可重复判定 TEST-005 量化门禁 + TEST-004 发布阻断规则
P1 DRV 矩阵缺关键字段 协议差异(拓扑 / 并发 / 取消 / 幂等)未暴露 DRV 八新字段 + 豁免生命周期五字段
P2 铁律绑定具体实现 等价实现被圣物化阻碍演进 §0.5 不变量框架 + commit SHA 证据
P2 「恢复进程内闭环」绝对化 进程死锁时无最后兜底 §0.6 主路径 + supervisor 限速兜底
P2 配置化默认安全 风险转移到现场配置 §0.7 配置治理五条