edgeCore 工程铁律合规评审与 2026 Q4 改进计划
| 版本 |
日期 |
状态 |
| v1.1 |
2026-10-19 |
现行(增补索引与任务速查) |
| v1.0 |
2026-10-17 |
初版:系统评审结论 + Q4 任务清单 |
评审基准:《工程铁律与恢复细则(操作层)v2.4》(2026-10-16 发布)
评审方法:四组并行只读代码审查(CONN 连接生命周期 / REC 南向恢复 / SCAN+SAFE+SHUTDOWN+OBS / DRV+TEST+BUD-005),覆盖 internal/ 全部 14 个协议驱动、internal/core 采集调度与通道管理、cmd/main.go 退出路径、internal/pkg/logger、docs/RELEASE_GATE.md 与 CI 工作流;每条结论带代码证据(文件:行号),未依赖文档自评。
评审声明:本评审基于仓库当前代码(commit 4737ea86 之后的 dev 工作区)与文档记录,未涉及真实设备与现场网络;「未实现」指代码中无对应机制,不等于功能必然失效,但按铁律 TEST-004 口径未验证不得视为合规。
索引
章节索引
任务速查(28 项)
| 编号 |
任务 |
优先级 |
角色 |
周期 |
| Q4-P0-01 |
REC-002 准入钩子(未全部确认离线禁拨号) |
P0 |
R2 |
W43 |
| Q4-P0-02 |
严格口径与单设备链路放行分支 |
P0 |
R2 |
W43–W44 |
| Q4-P0-03 |
连接代际 generation(准入三件套前置) |
P0 |
R1+R2 |
W43–W44 |
| Q4-P0-04 |
并发回写测试(旧结果不得翻回在线) |
P0 |
R4 |
W44 |
| Q4-P0-05 |
写幂等性拆分(结果待确认三态) |
P0 |
R1 |
W44–W45 |
| Q4-P0-06 |
写结果不确定测试(禁重复控制动作) |
P0 |
R4 |
W45 |
| Q4-P0-07 |
ethercat 先关后开 + t.master 互斥 |
P0 |
R1 |
W44 |
| Q4-P0-08 |
opcua Disconnect 锁外 Close |
P0 |
R1 |
W44 |
| Q4-P0-09 |
共享连接 deadline 协调专项(三驱动) |
P0 |
R1 |
W45–W46 |
| Q4-P0-10 |
ReleaseUncertain 资源释放状态 |
P0 |
R1 |
W45–W46 |
| Q4-P0-11 |
close_before_open 逐驱动测试 |
P0 |
R4+R1 |
W46 |
| Q4-P0-12 |
logger 关闭链路 + Draining 屏障 + 全局关停预算 |
P0 |
R3 |
W46–W47 |
| Q4-P0-13 |
阶段集成验证(故障注入 + 槽位耗尽 + soak) |
P0 |
R4 |
W47 |
| Q4-P1-01 |
错误联合分类(阶段 + 证据 + 健康 + 范围) |
P1 |
R1 |
W48 |
| Q4-P1-02 |
FailureUnknown 终态策略 |
P1 |
R1+R2 |
W48 |
| Q4-P1-03 |
四超时语义拆分与租约所有权回收 |
P1 |
R1 |
W48–W49 |
| Q4-P1-04 |
退避复位双条件 + 单资源节流 |
P1 |
R1 |
W49 |
| Q4-P1-05 |
目标路径网络判定与迟滞恢复 |
P1 |
R1+R2 |
W49 |
| Q4-P1-06 |
受控异步连接(删裸 go driver.Connect()) |
P1 |
R2+R3 |
W49 |
| Q4-P1-07 |
卡死巡检恢复(强制重置重派) |
P1 |
R2 |
W50 |
| Q4-P1-08 |
任务取消隔离(channelMu 带超时) |
P1 |
R2 |
W50 |
| Q4-P1-09 |
时间戳族与数据质量五态 |
P1 |
R2 |
W50–W51 |
| Q4-P1-10 |
任务分类与合并语义(四类显式化) |
P1 |
R2 |
W51 |
| Q4-P1-11 |
驱动统一重连(omron/mitsubishi/ice104/snmp) |
P1 |
R1 |
W51 |
| Q4-P1-12 |
Unknown 有界核实 |
P1 |
R2 |
W51 |
| Q4-P2-01 |
panic 三态与 goroutine 异常边界 |
P2 |
R1+R2 |
W52 |
| Q4-P2-02 |
进度监控(区分存活与推进) |
P2 |
R2 |
W52 |
| Q4-P2-03 |
OBS 补齐(字段助手 / suppressed 计数 / LRU) |
P2 |
R2+R3 |
W52 |
| Q4-P2-04 |
RELEASE_GATE 升级(未验证态 + 量化四行) |
P2 |
R4 |
W52 |
| Q4-P2-05 |
PR 检查表落地(九项 + 铁律编号) |
P2 |
R4 |
W52 |
| Q4-P2-06 |
文档一致性修正(§四 6 处批量) |
P2 |
R5 |
W52–W53 |
| Q4-P2-07 |
G-Industrial 自动化 nightly |
P3 |
R4 |
W53 |
| Q4-P2-08 |
编号证据链与季度复盘 |
P3 |
R5+负责人 |
W53 |
任务详情(交付物、关联规则、验收标准)见 5.3 / 5.4 / 5.5;关键路径与并行说明见 5.6。
一、评审结论摘要
1.1 九大规则族合规总览
| 规则族 |
合规判定 |
已落地(证据) |
主要缺口 |
| CONN 连接生命周期 |
部分合规 |
runConnect 持 linkMu 包 dial/install 且退避不持锁;各驱动 t.mu 仅包引用交换;bacnet 旧协程退出等待为最佳实践 |
7/7 驱动「先关后开」零错误检查;ethercat 丢弃引用直接拨号(违规);generation、ReleaseUncertain 均零实现 |
| REC 南向恢复 |
部分合规 |
REC-006 断链证据回写已单测;REC-007 Unknown 不判断;全局限流 10/s + single-flight |
REC-002 四条件合取未实现(关键);并发回写无防护;错误分类纯字符串匹配;5min 超时语义混用;断网无目标路径判定 |
| BUD 恢复预算 |
部分合规 |
指数退避 + 冷却 + 滑窗限流;任务级 context 超时 |
拨号成功即复位退避(违规);单资源最小间隔缺失;写操作默认重发 4 次(高危);共享连接 deadline 三驱动无协调 |
| SCAN 采集调度 |
部分合规(偏弱) |
三层并发预算(全局/每链路串行/每设备信号量);单条目防重入 |
卡死巡检已被移除(enforceAntiStarvation 不存在,与文档记载不符);任务取消后底层 I/O 继续占锁;时间戳族仅 LastCollectTime |
| SAFE panic 保障 |
部分合规 |
scan_engine 与看门狗为单次迭代 recover 结构(正确范式) |
dispatchLoop 其余迭代体无 recover;无三态分类、无 panic 限频;无进度监控(假存活不可观测) |
| SHUTDOWN 关停 |
部分合规(偏弱) |
关停函数顺序正确(调度→南向→pipeline→store) |
logger.Close 完全不存在;无 Draining 屏障;无阶段/全局预算;cm.Shutdown 未停调度引擎(消费者已关、生产者仍写) |
| OBS 可观测性 |
部分合规 |
panic 日志含组件+堆栈;backpressure reason 为枚举 |
重连日志缺 event/generation/reason/attempt/elapsed_ms 字段;限频无 suppressed 计数 |
| DRV 驱动矩阵 |
部分合规 |
9/14 驱动接入 ConnectionManager;omron/mitsubishi/snmp 已补 LinkMutexBinder(文档记载过时) |
omron/mitsubishi/snmp/ice104 无统一重连编排(ice104 断线后无自动重连);能力矩阵未建档 |
| TEST 验收门禁 |
部分合规 |
RELEASE_GATE.md 已有 Lag P95/失败率/heap 等量化阈值;并发重连、状态机、故障注入测试齐备 |
缺「未验证」态与发布阻断规则;写结果不确定测试、槽位耗尽测试、并发回写测试零覆盖;PR 检查表无流程载体 |
1.2 关键数字
- 缺口清单 C-1~C-17 全部确认存在(其中 C-3 描述已过时,见 §四)。
- 新发现代码级问题 14 项(N-1~N-14,见 §三),其中直接违反必须级条款 6 项。
- 313 个
_test.go 中,TEST-001 验收矩阵 13 行仅 5 行完整覆盖、3 行部分、5 行未覆盖。
- 文档与代码/铁律冲突 6 处(见 §四)。
1.3 总体判断
项目在并发控制、超时、退避、限流、断链证据回写等基础机制上质量良好(linkMu 锁范围、三层并发预算、单次迭代 recover 均为正确实践)。核心风险集中在三处:① 恢复准入缺少「设备全部离线」铁律校验(C-2),多从站链路存在被反复重建、耗尽连接资源的风险;② 控制写入默认重发(BUD-005),timeout 丢响应场景会重复执行现场动作;③ 关停链路的 logger 从未接入关闭路径且调度引擎未停(SHUTDOWN-001),升级场景存在日志丢失与「消费者已关、生产者仍写」窗口。 上述三项与铁律四条件合取、写幂等性、关停屏障共同构成 Q4 第一阶段主体。
二、分族合规详评
每族仅列与铁律条款的差距,已合规项见 §1.1。行号引用为评审时点代码位置,按 §0.5 属审计入口。
2.1 CONN 连接生命周期
| 规则 |
现状 |
证据与差距 |
| CONN-001/002 先关后开 |
部分 |
7 个使用 ConnectionManager 的驱动均为「换引用前调 Close 但丢弃返回值」:modbus/transport.go:474-489、s7/transport.go:369-372、dlt645/transport.go:137-139、ethernetip/transport.go:179-181、bacnet/bacnet.go:314-341(质量最高,含旧协程等待但关闭错误仍忽略)、opcua/opcua.go:135-148(重连前无显式旧会话关闭);ethercat 为直接违规——t.master = newUDPMaster() 覆盖旧引用且 handleCycleError 不停 cycle 线程直接重连(ethercat/transport.go:100-106,243-251),旧 UDP fd 泄漏 + 旧/新 cycle goroutine 与 t.master 数据竞争(refreshTxSnapshots:231 无锁读) |
| CONN-003 锁三职责 |
部分 |
connection_manager.go:439-448 退避不持锁(合规);违规点:opcua/opcua.go:136-144 Disconnect 持 d.mu 执行 c.Client.Close(ctx) 网络 I/O,与同文件 :748-750 注释自相矛盾,Close 阻塞将连带阻塞 Health() 等状态读;ethercat connectOnce 完全无 t.mu |
| CONN-004 generation |
未实现 |
全仓库驱动层零命中;s7/transport.go:583-593 withRetry 持旧 client 引用执行 RPC,重连换 client 后旧结果无标识回写 |
| CONN-005 并发重建 |
已实现 |
single-flight CAS(connection_manager.go:453)+ linkMu 绑定 7 驱动 |
| CONN-006 ReleaseUncertain |
未实现 |
全仓零命中;s7/transport.go:422-428、ethernetip/transport.go:207-214 Close 失败即 SetState(StateDisconnected),违反「禁止把状态直接重置为 Disconnected」 |
2.2 REC 南向恢复
| 规则 |
现状 |
证据与差距 |
| REC-002 重建准入(铁律) |
未实现(关键) |
channel_manager.go:160-204 看门狗仅判 isChannelLinkUp + 30s 冷却;驱动 ScheduleReconnect(connection_manager.go:452)无设备状态校验。在线计数(channel_manager.go:483-539)为二元制——NodeStateQuarantine 计入 offline(:505-509),不满足「已确认离线」严格口径;单设备链路无显式等价分支(无注释无测试) |
| REC-002 并发防护(C-10) |
未实现 |
准入快照(channel_manager.go:168-179 RLock 后即释放)与回写(finalizeScanCollect→onCollectSuccess,node_status.go:211-231,1 次成功即翻在线)之间无原子衔接——「检查时全离线、检查后被旧结果翻回在线」竞争窗口真实存在 |
| REC-004 状态同步 |
部分 |
链路确认断开同步置离线已实现(channel_manager.go:423);「数据停更超阈值 + 链路已断 → 强制置离线」未实现(全库无周期陈旧巡检,仅事件驱动);回写无 generation 校验 |
| REC-005 错误分类(C-12) |
部分 |
modbus/transport.go:197-217 纯 strings.Contains 匹配且 i/o timeout 一律设备级;无重新分类判据——共享传输所有从站连续超时均不累计 collectFailCount,永不升级链路级,形成「连接未断、链路不可用」的永久停采(评审预言的风险在代码中成立) |
| REC-006/007 |
已实现/部分 |
REC-006 与代码一致;REC-007 缺「Unknown 有界核实」——看门狗对 Unknown 直接跳过(:183),仅靠 connMgr 内部 64 次重试兜底 |
| REC-008 超时语义 |
部分 |
channelReconnectTimeout=5min(channel_manager.go:124)实际圈住 EnsureConnected 全部退避轮次(connection_manager.go:462-465),充当恢复总预算/租约,四种语义未拆分;租约到期后无「先确认旧 goroutine 退出再清标志」的所有权回收 |
| REC-009/BUD-004 限流 |
部分 |
全局限流 + single-flight 已实现;单资源最小间隔缺失(新故障从 baseDelay=100ms 重新起退);退避复位违规——拨号成功即 RecordSuccess 复位(connection_manager.go:159-166,413-414),正是 BUD-003 禁止的「仅因握手成功即复位」 |
| REC-011 断网判定 |
未实现 |
断网判定完全依赖驱动 Health() 与错误字符串;无目标地址/接口/路由域判定、无迟滞恢复、无按目标低频探测 |
| REC-012 异步连接 |
部分(形式违规) |
channel_manager.go:923-933 与 :2511 为裸 go driver.Connect() 无主 goroutine——无有界队列、无重复请求合并、关停不可回收;仅因 modbus Connect 内部转发 EnsureConnected 间接受益于退避/限流 |
2.3 BUD 恢复预算
| 规则 |
现状 |
证据与差距 |
| BUD-001 重试职责 |
部分 |
多层重试独立叠加风险存在(驱动 withRetry × 采集层);读/写未区分策略 |
| BUD-002 时间预算 |
部分 |
ReadPoint 5s/ReadAllPoints 10s/Connect 10s 存在;T_task 六段拆分未落实;驱动层 context 取消依赖参差(拨号 client.Open() 不可取消,仅受 t.timeout 约束,transport.go:433) |
| BUD-003 退避冷却限流 |
部分 |
机制齐备但复位条件违规(见 REC-009);无按资源敏感度的策略档位 |
| BUD-004 单资源节流(C-9) |
未实现 |
无单资源最小重连间隔、无显式单资源最大并发拨号 |
| BUD-005 写幂等性(C-16) |
未实现(高危) |
写主路径三层零重试(server.go:1622→channel_manager.go:1486→driver_executor.go:237/293),唯一重试在 modbus 驱动层:transport.go:589-664 withRetry 对 timeout/crc/exception 不 break 继续重发,maxRetries 默认 3 → timeout 丢响应时写命令默认重发最多 4 次;全仓无幂等声明/去重标识/回读确认;失败日志无三态区分 |
| BUD-006 deadline 协调(C-13) |
部分(三驱动违规) |
profinetio/rpc.go:39-93 有 c.mu 串行化但响应不校验 callID、deadline 设后无清理(:52,84);mitsubishi/transport.go:155-193 transact 无连接级锁,并发请求互相破坏 deadline 且残留响应必被误关联;knxnetip/transport.go:413-467 无锁读,并发写后各自读必然错配;ice104/transport.go:155-228 后台 readLoop 与前台握手共享 conn 双读者互毁 deadline |
2.4 SCAN 采集调度
| 规则 |
现状 |
证据与差距 |
| SCAN-001 端到端链条 |
部分 |
链条可观测性依赖单点 LastCollectTime |
| SCAN-002/005 时间戳族(C-6) |
未实现 |
全库仅 LastCollectTime(model/metrics.go:110、channel_manager.go:31);LastValidSampleAt/LastValueChangedAt/LastCollectSuccessAt/LastCommitAt/LastErrorAt 均不存在;点位时间戳为执行时间(各驱动 TS: time.Now(),如 omron/udp_scheduler.go:125),数值不变时无法区分「值未变但采样成功」;质量态仅 Good/Bad,缺五态 |
| SCAN-003 负载过载 |
部分(与文档记载不符) |
processReadyTasks(scan_engine.go:438-464)无卡死巡检;enforceAntiStarvation 全库不存在(仅历史测试注释提及),RecordStarvationRescue(scan_engine_metrics.go:116)无生产调用点——文档 §0.4/细则 1 称「已实现」与代码不符;execution_layer.go:300-307 任务超时仅返回 ErrTimeout 而 ReadFunc 仍在跑并持 channelMu(:473-476),同链路后续任务级联阻塞=链路级饿死,违反 SCAN-003⑤ |
| SCAN-004 阈值族 |
未实现 |
2×PollPeriod 为文档口径,无分协议五参数配置 |
| SCAN-006 任务合并语义 |
未实现 |
写/事件类任务不经调度器(channel_manager.go:1486 直写),无独立队列/保序/丢弃策略声明 |
2.5 SAFE panic 保障
| 规则 |
现状 |
证据与差距 |
| SAFE-001 四层语义(C-5) |
部分 |
单次迭代 recover 仅 2 处(scan_engine.go:387-400 safeProcessReadyTasks、channel_manager.go:151-158 看门狗);dispatchLoop 的 recover 未覆盖同迭代 scheduleWake/nextReadyTime(:369/:372),这两处 panic 仍会杀死唯一调度 goroutine;无三态分类;重复 panic 无限频/退避;驱动自发重连 goroutine(connection_manager.go:462 起)无自身 recover |
| SAFE-002 进度监控 |
未实现 |
存活证据仅状态计数(GetActiveTaskCount)与 15s 链路巡检;「goroutine 活着但阻塞在 channelMu」不可观测 |
2.6 SHUTDOWN 关停
| 规则 |
现状 |
证据与差距 |
| SHUTDOWN-001 硬序契约 |
部分(阶段④断裂) |
顺序 ①②③ 正确(main.go:389-393);logger.Close 全链不存在——logger 包无导出 Close(仅 AsyncRotateWriter.Close,logger.go:287,且 InitLogger 不持有 writer 引用),main 无调用;无 Draining 屏障,驱动自发重连 goroutine 在关停中仍可触发新重连(违反阶段④);cm.Shutdown(channel_manager.go:1786-1803)未调 scanEngineAdapter.Stop()——dispatchLoop/executionLayer 在关停后继续运行,在途 executeTaskAsync 仍可写已 defer 关闭的 shadow/store(消费者已关、生产者仍写) |
| SHUTDOWN-002 期限与结果(C-7/C-14) |
部分 |
仅 srv 5s / pprof 2s 超时;cm.Shutdown 无超时(一个驱动卡死阻塞全部);无全局总预算;AsyncRotateWriter.Close(logger.go:287-293)close(closeCh) 后立即 file.Close() 不等待 loop 排空=丢日志/写已关文件,二次 Close panic;InitLogger 被调用两次(main.go:68→129),首个 writer loop goroutine 泄漏 |
2.7 OBS 可观测性
| 规则 |
现状 |
证据与差距 |
| OBS-001 字段契约 |
部分 |
重连日志仅 driver+error(connection_manager.go:468-471),缺 event/channel_id/device_id/reason/attempt/elapsed_ms;generation 不存在故无该字段;reason 部分为字符串枚举(backpressure_controller.go:188) |
| OBS-002 指标基数 |
部分 |
指标为内存 map(model/metrics.go:144-161)非 Prometheus,高基数风险暂不存在;但 deviceMetrics/pointMetrics 按 ID 全量驻留无上限无保留期 |
| OBS-003 数据预算 |
部分 |
LogThrottle(reconnect.go:47-66)无 suppressed 计数;AsyncRotateWriter dropCnt 仅每 1000 次写 stderr 不入指标;bacnet/scheduler.go:385-398 逐点静默无计数——违反「禁止完全静默」 |
2.8 DRV 驱动矩阵
| 驱动 |
ConnectionManager |
LinkMutex |
重连编排 |
判定 |
| bacnet/modbus/s7/dlt645/ethernetip/opcua/knxnetip/ethercat |
已接入(8) |
5/8 已绑 |
connMgr 统一 |
基本合规(关闭语义见 CONN) |
| profinetio |
半接入(new 未调 Ensure/Schedule) |
未绑 |
自管计数 |
缺口 |
| omron-fins |
无 |
已绑 |
Connect 内直拨,无退避/限流/单飞,无后台重连 |
缺口 |
| mitsubishi-slmp |
无 |
已绑 |
Connect 内线性重试(attempt×100ms 无封顶),失败后无自愈 |
缺口 |
| snmp |
无 |
已绑 |
UDP 现建现用 |
可接受(无连接语义)需豁免建档 |
| ice104 |
无 |
未绑 |
readLoop 失败即退出 goroutine,无任何自动重连 |
缺口(最重) |
看门狗侧 ReconnectScheduler 为可选接口,未实现驱动被静默跳过(channel_manager.go:189-192)——「静默跳过」本身违反 DRV 豁免须显式建档的要求。
2.9 TEST 验收门禁
| 规则 |
现状 |
证据与差距 |
| TEST-001 矩阵 |
5/13 完整覆盖 |
已覆盖:时序(connection_manager_linkmu_test.go:10)、并发重连(connection_manager_reconnect_test.go:55)、状态机(connection_manager_test.go:31,277)、故障注入(integration/fault_propagation_test.go:50)、资源 soak(soak_test.go:21,//go:build soak 默认不执行);未覆盖:并发回写(依赖 generation)、槽位耗尽(C-11)、写结果不确定、生命周期旧代际覆盖;部分:关闭异常(无 close_before_open 专项)、关停(缺「I/O 执行中收信号」场景) |
| TEST-002 恢复判据 |
未落实 |
无按协议定义的恢复成功判据文档与断言 |
| TEST-003 PR 检查表 |
未实现 |
无 PULL_REQUEST_TEMPLATE.md、无 CONTRIBUTING.md,九项检查表无流程载体 |
| TEST-004 判定分类 |
部分 |
RELEASE_GATE.md 有 PASS/WARN/BLOCK 与发布矩阵(:144-153),但缺「未验证」态;WARN≠有条件豁免(无限定/范围/责任人/风险缓解要素) |
| TEST-005 量化门禁(C-15) |
部分 |
已有 Lag P95<150ms/drift<80ms/失败率<0.1%/heap drift<8%/GC<30ms(:116-127);缺:连接尝试次数/单位时间、单资源最大并发拨号、活跃 fd/socket 稳态、违规事件(关闭失败/旧代际回写/未确认释放后拨号)=0 四行;G-Industrial 未自动化(人工+报告模板),CI 仅跑 G-Stability(ci.yml:69-74) |
三、新发现问题清单(文档未登记)
| 编号 |
位置 |
问题 |
违反规则 |
风险 |
| N-1 |
ethercat/transport.go:100-106,143,231,243-251 |
connectOnce 丢弃旧 master 引用直接拨号,不停 cycle 线程;t.master 无锁读写 |
CONN-001/003(直接违规) |
旧 UDP fd 泄漏 + goroutine 数据竞争,反复故障后资源耗尽 |
| N-2 |
opcua/opcua.go:136-144 |
Disconnect 持 d.mu 执行 Close 网络 RPC |
CONN-003 |
Close 阻塞连带阻塞 Health 等所有状态读,看门狗连带卡死 |
| N-3 |
mitsubishi/transport.go:155-193、knxnetip/transport.go:413-467 |
transact 无连接级串行化,共享 conn 并发读写 |
BUD-006 |
响应错配(张冠李戴)→ 脏数据写 shadow;deadline 互毁 |
| N-4 |
ice104/transport.go:226-227,241-242 |
readLoop 读失败仅置 connected=false 退出,无自动重连 |
REC-003/DRV |
断线后须人工介入,恢复退化 |
| N-5 |
channel_manager.go:923-933,2511 |
裸 go driver.Connect() 无主 goroutine |
REC-012 |
绕过退避/限流/关停回收;重复启动无界排队 |
| N-6 |
channel_manager.go:1786-1803 |
cm.Shutdown 未停 scanEngineAdapter(scan_engine_compat.go:161 生产零调用) |
SHUTDOWN-001⑤ |
消费者已关、生产者仍写 shadow/store |
| N-7 |
logger.go:287-293、main.go:68,129 |
logger.Close 不存在;AsyncRotateWriter.Close flush-then-close 竞态 + 二次 Close panic;InitLogger 双调用泄漏首个 loop |
SHUTDOWN-001④ |
升级/退出丢日志;goroutine 泄漏 |
| N-8 |
modbus/transport.go:589-664,748,852,866 |
写命令经 withRetry 对 timeout/crc 继续重发,默认最多 4 次 |
BUD-005(高危) |
timeout 丢响应=重复执行现场控制动作(脉冲/启停/复位) |
| N-9 |
connection_manager.go:159-166,413-414 |
拨号成功即 RecordSuccess 复位退避至最低间隔 |
BUD-003/004 |
闪断场景退避失效,重连风暴 |
| N-10 |
scan_engine.go:438-464、scan_engine_metrics.go:116 |
enforceAntiStarvation 已移除、卡死巡检不存在、RecordStarvationRescue 无调用点 |
SCAN-003;文档 §0.4/细则1「已实现」记载与代码不符 |
任务 Running 卡死后永久蒸发(饿死形态),无巡检兜底 |
| N-11 |
execution_layer.go:300-307,473-476 |
任务取消后 ReadFunc 仍在跑并持 channelMu |
SCAN-003⑤ |
同链路后续任务级联超时=链路级饿死 |
| N-12 |
profinetio/rpc.go:52,84,125-140 |
响应不校验 callID;deadline 设后无清理 |
BUD-006 |
残留响应误关联;deadline 污化后续请求 |
| N-13 |
omron/fins.go:97-106 等 |
omron/mitsubishi/snmp 已实现 LinkMutexBinder 并由 channel_manager.go:857 调用 |
文档 C-3 与架构11 §52-56/174/345 记载过时(正向) |
误导后续评审基线 |
| N-14 |
docs/RELEASE_GATE.md |
文件存在且已有部分量化阈值 |
C-15 范围收窄(正向) |
— |
四、文档一致性修正项(6 处)
| # |
位置 |
修正内容 |
| 1 |
架构 11 §52-56/174 |
omron/mitsubishi/snmp「未绑定 LinkMutexBinder」→ 改为「已绑 linkMu、未接入 ConnectionManager、无 ReconnectScheduler」 |
| 2 |
架构 11 §345 |
「补 LinkMutexBinder」建议 → 改为「引入 ConnectionManager + ReconnectScheduler」 |
| 3 |
南向采集通道决策方案:494 |
「链路级重连经 driver.ConnectionManager」→ 补充 omron/mitsubishi/snmp/ice104 不成立的现状说明 |
| 4 |
南向采集通道决策方案:498 |
补充 REC-002 四条件合取要求与现状(准入只判链路层) |
| 5 |
南向采集通道决策方案:517 |
「RecordFailure 5 次失败重连」→ 现路径为 watchdog 30s 冷却 + ScheduleReconnect + 设备级错误不计入 |
| 6 |
铁律文档 C-3 / §0.4 |
更新 omron/mitsubishi/snmp linkMu 已绑的事实;enforceAntiStarvation 相关表述改为「已移除,卡死巡检待恢复」(N-10) |
五、2026 Q4 改进任务清单
5.1 角色定义
| 角色 |
职责范围 |
| R1 南向内核 |
internal/driver/**、connection_manager.go、reconnect_limiter |
| R2 采集调度 |
internal/core/scan_engine.go、execution_layer、channel_manager 准入与状态回写 |
| R3 平台工程 |
cmd/main.go、logger、pipeline、storage、server 关停 |
| R4 质量保障 |
测试体系、CI/nightly、RELEASE_GATE、PR 流程 |
| R5 文档治理 |
docs/ 全部文档与铁律维护 |
| 负责人 |
任务分派、豁免审批、阶段评审与发布判定 |
5.2 阶段划分与周次对照
| 阶段 |
周次 |
日期 |
目标 |
| 一:正确性缺口(P0) |
W43–W47 |
10/19–11/22 |
消除错误恢复、连接资源泄漏、重复控制动作、关停断裂四类风险 |
| 二:跨协议统一(P1) |
W48–W51 |
11/23–12/20 |
故障分类、预算、调度、驱动能力统一 |
| 三:治理与验证(P2/P3) |
W52–W53 |
12/21–12/31 |
量化门禁、发布阻断、编号证据链、季度复盘 |
5.3 第一阶段任务(P0,W43–W47)
| 编号 |
任务与交付物 |
关联 |
优先级 |
角色 |
周期 |
验收标准 |
| Q4-P0-01 |
REC-002 准入钩子:checkChannelHealth 与 ConnectionManager.ScheduleReconnect 之间增加统一准入点,按链路归组统计设备状态,未全部确认离线即拒绝拨号(仅降级/冷却) |
REC-002/C-2 |
P0 |
R2 |
W43 |
状态机测试:多从站链路 1 台在线时触发重连→断言无拨号;onlineCount==0→放行 |
| Q4-P0-02 |
严格口径与单设备分支:Unknown/Quarantine/过期/回写中 不计入在线与确认离线;单设备链路离线即放行的显式分支 + 注释 + 测试固化 |
REC-002/C-2 |
P0 |
R2 |
W43–W44 |
单元测试覆盖四种状态计数;单设备链路测试断言立即放行 |
| Q4-P0-03 |
连接代际 generation:ChannelManager/transport 增加 per-channel 递增代际,采集回写前校验,旧代结果丢弃 |
CONN-004/C-4/C-10 |
P0 |
R1+R2 |
W43–W44 |
生命周期测试:旧请求延迟返回→断言不覆盖新状态(TEST-001 矩阵行) |
| Q4-P0-04 |
并发回写测试:准入检查通过后注入旧连接迟到成功结果 |
REC-002 并发防护/TEST-001 |
P0 |
R4 |
W44 |
-race 下断言设备不被翻回在线、重建流程不被绕过 |
| Q4-P0-05 |
写幂等性拆分:写路径独立 writeWithRetry,仅发送前错误(未建立/拨号失败)可重试;发送后 timeout/crc 返回「执行结果待确认」三态错误码;点位配置增补幂等声明 |
BUD-005/C-16/N-8 |
P0 |
R1 |
W44–W45 |
mock 驱动吞响应→断言不重发且 API/状态机呈现待确认;失败日志三态可查 |
| Q4-P0-06 |
写结果不确定测试:写已执行但响应丢失场景 |
TEST-001/BUD-005 |
P0 |
R4 |
W45 |
write_uncertain_test.go:无重复控制动作(强制项) |
| Q4-P0-07 |
ethercat 先关后开:connectOnce 入口停 cycle 线程 + 关旧 master,确认释放后装新;t.master 加互斥 |
CONN-001/003/N-1 |
P0 |
R1 |
W44 |
close_before_open 测试(关闭失败禁拨)+ -race 无数据竞争 |
| Q4-P0-08 |
opcua Disconnect 锁外 Close:锁内摘取 clients map、锁外逐个 Close |
CONN-003/N-2 |
P0 |
R1 |
W44 |
测试:Close 阻塞不阻塞 Health 读取 |
| Q4-P0-09 |
共享连接 deadline 协调专项:mitsubishi/knxnetip transact 加连接级互斥 + 响应序号关联;profinetio 响应校验 callID + 超时 drain + deadline 四出口清理;ice104 前台握手与后台 readLoop 隔离 |
BUD-006/C-13/N-3/N-12 |
P0 |
R1 |
W45–W46 |
并发 transact 测试:响应不错配;超时后下一请求不误关联残留响应 |
| Q4-P0-10 |
ReleaseUncertain 状态:ConnState 增加释放不确定态,Close 失败/协程未退出即进入;期间禁止同资源快速新建、重连间隔≥普通退避上限;bacnet 试点后推广 |
CONN-006/C-8 |
P0 |
R1 |
W45–W46 |
关闭失败测试:不断言 Disconnected、禁拨号、低频核验日志可查 |
| Q4-P0-11 |
close_before_open 逐驱动测试:s7/modbus/ethernetip/dlt645/opcua(mock Close 返错→断言 factory 未调用、保留引用) |
CONN-001/002/C-1 |
P0 |
R4+R1 |
W46 |
每驱动 1 个表驱动用例,-race 全绿 |
| Q4-P0-12 |
logger 关闭链路:导出 logger.Close()(doneCh 等待 loop 排空后关文件,幂等);修复双 InitLogger 泄漏;main.go 增加 Draining 状态 + 全局关停预算(建议 30s)+ cm.Shutdown 先停 scanEngineAdapter 再逐驱动带超时 Disconnect + logger.Close 置末 |
SHUTDOWN-001/002/C-7/C-14/N-6/N-7 |
P0 |
R3 |
W46–W47 |
关停测试:I/O 执行中发信号→有界关闭、顺序正确、退出前 fd 采样无残留;无「向已关 logger 写日志」竞态 |
| Q4-P0-13 |
阶段集成验证:故障注入(单设备异常/共享链路断开/网络断开)+ 槽位耗尽仿真(transport 层错误注入)+ 72h soak short |
TEST-001/C-11 |
P0 |
R4 |
W47 |
恢复作用范围正确;单资源节流生效;无永久拨号循环;资源趋势稳态 |
5.4 第二阶段任务(P1,W48–W51)
| 编号 |
任务与交付物 |
关联 |
优先级 |
角色 |
周期 |
验收标准 |
| Q4-P1-01 |
错误联合分类:transport 增加共享链路健康证据(同连接其他从站近期成功数);连续失败且健康证据丢失→超时重分类为 TransportReadTimeout 链路级并累计;按阶段表拆分子类型 |
REC-005/C-12 |
P1 |
R1 |
W48 |
测试:全从站连续超时→升级链路级并触发受控重建;单从站超时→仅隔离设备 |
| Q4-P1-02 |
FailureUnknown 终态:有界诊断窗口 + 限频探测 + 「不可判定故障」可观测告警 |
REC-001 |
P1 |
R1+R2 |
W48 |
状态机测试:Unknown 不永久悬置、不误判 LinkDown |
| Q4-P1-03 |
四超时语义拆分:dialTimeout/recoveryAttemptBudget/reconnectWaitTimeout/recoveryLeaseTimeout 具名化;租约到期先确认旧 goroutine 退出再清标志 |
REC-008 |
P1 |
R1 |
W48–W49 |
测试:租约超时后无新旧重连流程并发 |
| Q4-P1-04 |
退避复位双条件:握手成功 + 稳定连接时长 + 成功采集次数才逐步复位;单资源最小重连间隔 + 显式最大并发拨号 |
BUD-003/004/C-9/N-9 |
P1 |
R1 |
W49 |
测试:闪断场景退避不失效;单资源拨号间隔≥配置值 |
| Q4-P1-05 |
目标路径网络判定:按目标地址/接口/路由域维护低频探测;恢复判定连续成功/迟滞 |
REC-011 |
P1 |
R1+R2 |
W49 |
测试:管理网断但设备网通→不暂停设备网采集 |
| Q4-P1-06 |
受控异步连接:删除裸 go driver.Connect()(channel_manager.go:923/2511),改 ReconnectScheduler 幂等提交 + 有界队列 + 同链路请求合并 + 关停可回收 |
REC-012/N-5 |
P1 |
R2+R3 |
W49 |
测试:队列满返回「恢复已排队」;关停时无残留 goroutine |
| Q4-P1-07 |
卡死巡检恢复:ScanTask 增加 runningSince;dispatchLoop 每轮按 10×Interval/5min 规则强制重置重派并记日志 |
SCAN-003/N-10 |
P1 |
R2 |
W50 |
测试:mock 永不返回任务→被强制重派且 Skipped 日志可追踪 |
| Q4-P1-08 |
任务取消隔离:SerialQueueManager 记录 in-flight 句柄,外层超时后置取消标志并隔离该队列 worker;channelMu 带超时获取 |
SCAN-003/N-11 |
P1 |
R2 |
W50 |
测试:驱动忽略 ctx 时同链路后续任务不被级联阻塞 |
| Q4-P1-09 |
时间戳族与质量五态:ShadowPoint 增加 lastValidSampleAt/lastValueChangedAt/lastCollectSuccessAt/lastCommitAt/lastErrorAt;新鲜度以有效样本判定;质量态五枚举 |
SCAN-002/005/C-6 |
P1 |
R2 |
W50–W51 |
测试:值不变但采样成功→不报陈旧;部分点位失败→关键点位停更可判 |
| Q4-P1-10 |
任务分类与合并语义:周期采样(可合并)/状态核验(可降频不可饿死)/控制写入(保序独立队列)/事件告警(不可覆盖)四类显式化 |
SCAN-006 |
P1 |
R2 |
W51 |
测试:写入任务不因周期合并被丢弃;事件不塌缩 |
| Q4-P1-11 |
驱动统一重连:omron/mitsubishi/ice104 实现 ReconnectScheduler(委托 connMgr);ice104 补 BindLinkMutex;profinetio 拨号改 EnsureConnected;snmp 走正式豁免 |
DRV/C-3/N-4/N-13 |
P1 |
R1 |
W51 |
每驱动:断线→自动恢复测试;snmp 豁免六要素+生命周期字段建档 |
| Q4-P1-12 |
Unknown 有界核实:Unknown 持续超 N 个巡检周期→有限诊断或升级告警 |
REC-007 |
P1 |
R2 |
W51 |
测试:Unknown 不永久跳过 |
5.5 第三阶段任务(P2/P3,W52–W53)
| 编号 |
任务与交付物 |
关联 |
优先级 |
角色 |
周期 |
验收标准 |
| Q4-P2-01 |
panic 三态与 goroutine 边界:recover 上移覆盖 dispatchLoop 整个迭代体;RecoveredAndContinue/RestartWorker/ComponentFailed 三态落地;重复 panic 限频退避;每 goroutine 异常边界清查 |
SAFE-001/C-5 |
P2 |
R1+R2 |
W52 |
测试:迭代体内任意位置 panic→循环存活;重复 panic→ComponentFailed 报警不风暴 |
| Q4-P2-02 |
进度监控:ScanEngine/ExecutionLayer/SerialQueueManager 增加 lastProgressAt,诊断接口输出 now-lastProgressAt 超阈报警 |
SAFE-002 |
P2 |
R2 |
W52 |
测试:goroutine 阻塞→进度指标超阈可观测 |
| Q4-P2-03 |
OBS 补齐:关键事件字段助手(event/channel_id/device_id/generation/reason/attempt/elapsed_ms 单调时钟);LogThrottle 增 suppressed 计数;pointMetrics LRU 上限 |
OBS-001/002/003 |
P2 |
R2+R3 |
W52 |
测试:限频事件 suppressed 计数可查;单条日志长度有上限 |
| Q4-P2-04 |
RELEASE_GATE 升级:判定矩阵增设「未验证」态且强制门禁未验证默认 BLOCK;阈值表补重连速率/单资源并发拨号/fd-socket 稳态/违规事件=0 四行;WARN 改造为有条件豁免(范围/期限/责任人/风险缓解) |
TEST-004/005/C-15/N-14 |
P2 |
R4 |
W52 |
门禁文档版本化;数值有测试环境记录 |
| Q4-P2-05 |
PR 检查表落地:新增 .github/PULL_REQUEST_TEMPLATE.md(TEST-003 九项 + 铁律编号字段);ci.yml 增加 PR 体校验 |
TEST-003 |
P2 |
R4 |
W52 |
模板生效;缺失铁律编号的 PR 被标记 |
| Q4-P2-06 |
文档一致性修正:§四 6 处批量更新(架构 11、南向方案、铁律 C-3 与 §0.4) |
文档治理/N-10/N-13 |
P2 |
R5 |
W52–W53 |
审查清单逐项核对通过 |
| Q4-P2-07 |
G-Industrial 自动化:nightly workflow 增加仿真器 job 承接协议分级表 + S1–S6 + 量化阈值,输出报告归档 |
TEST-005 |
P3 |
R4 |
W53 |
nightly 跑通并产出带数值的报告 |
| Q4-P2-08 |
编号证据链与季度复盘:建立「铁律→不变量→代码符号→测试→门禁」映射表(记录 commit SHA);Q4 复盘:未验证项与豁免项纳入版本记录;形成 2027 Q1 输入 |
§0.5/TEST-004/阶段三 |
P3 |
R5+负责人 |
W53 |
映射表随版本发布;复盘报告经负责人签发 |
5.6 排期依赖与并行说明
- 关键路径:Q4-P0-03(generation)是 Q4-P0-01/02/04 的前置;Q4-P0-12(关停)依赖 Q4-P0-10 的关闭语义统一,两者在 W46 需协同。
- 可并行:R1(驱动层)与 R2(调度层)、R3(平台层)任务按周并行;Q4-P0-04/06/11(测试)随对应实现周交付,采用 TDD 时序(先写失败测试)。
- 每周五阶段站会:对表验收,未过项不留到下周(对齐 TEST-004「未验证≠通过」)。
- 阶段门:W47 末第一阶段评审(负责人签发后才进入第二阶段);W53 末季度复盘并裁定 2027 Q1 计划。
5.7 与发布门禁的衔接
- 本计划全部 P0 项完成并验证前,涉及连接生命周期、控制写入、关停路径的发布默认阻断(TEST-004 阻断规则)。
- Q4-P2-04 落地后,「未验证」态与量化阈值纳入
RELEASE_GATE.md,与 CI/nightly 形成自动判定。
- 每项任务的测试证据(测试名 + 运行记录 + commit SHA)登记进 §0.5 要求的编号映射表,作为版本发布记录的一部分。
附录:核查证据索引
| 主题 |
主要证据位置 |
| 重连准入与看门狗 |
internal/core/channel_manager.go:121-204, 397-441, 483-539, 886-933, 1786-1803, 2511 |
| 设备状态 |
internal/core/channel_device_state.go:11-47, 112-161, 168、internal/core/node_status.go:211-300 |
| 连接管理 |
internal/driver/connection_manager.go:78, 145-166, 233-266, 341-346, 402-474、internal/driver/reconnect_limiter.go |
| 驱动 connectOnce/Close |
modbus/transport.go:186-279, 474-489, 589-664、s7/transport.go:363-428, 583-593、bacnet/bacnet.go:221, 314-361、dlt645/transport.go:131-147、ethernetip/transport.go:173-218、opcua/opcua.go:135-148, 738-795、ethercat/transport.go:100-155, 231, 243-251 |
| deadline 协调 |
profinetio/rpc.go:39-140、mitsubishi/transport.go:155-193、knxnetip/transport.go:413-467、ice104/transport.go:155-228、dlt645/transport.go:322-389 |
| 调度与饿死 |
internal/core/scan_engine.go:49-77, 369-420, 438-464, 523-614, 745-760、execution_layer.go:230-355, 445-476、backpressure_controller.go:66-188、scan_engine_metrics.go:116 |
| 退出与日志 |
cmd/main.go:55, 68, 129, 389-393、internal/pkg/logger/logger.go:230-293, 337、internal/server/server.go:288-292 |
| 测试与门禁 |
internal/**/313 个 _test.go、integration/soak_test.go:14-31、docs/RELEASE_GATE.md:29-153, 228-238、.github/workflows/ci.yml:69-100 |