边缘计算南向采集通道优化与设备画像构建方案 V2

文档状态(Q3 归档): 本文为 2026 Q2 规划归档,保留历史设计细节供查阅。现行交付、差距跟踪与验收Q3 采集优化方案edge/边缘网关架构设计总览TODO/ScanEngine 重构方案 为准。

架构说明: 南向采集已迁移至 ScanEngine 调度驱动内核CollectionScheduler / deviceLoop 已移除)。下文 deviceLoop / collectDevice / CollectionScheduler 等为 Q2 规划态描述,请勿作为现行实现依据。

1. 项目现状分析

1.1 当前架构

当前项目采用三级架构:

  • 通道(Channel):对应一个采集驱动,管理多个设备
  • 设备(Device):具体的设备实例,包含多个点位
  • 点位(Point):设备的具体数据点

核心组件:

  • ChannelManager:管理所有采集通道及其设备
  • CommunicationManageTemplate:管理设备运行状态
  • 多协议驱动:支持Modbus、BACnet、OPC UA、S7等协议

1.2 现有功能

  • 设备状态管理(在线、离线、不稳定等状态)
  • 定时数据采集
  • 基本的设备指标收集
  • 多协议支持
  • 设备扫描和点位发现

1.3 存在的问题

  • 缺乏设备级别的智能适配机制
  • 采集参数固定,无法根据设备特性动态调整
  • 没有统一的设备画像结构
  • 并发采集策略不够优化
  • 批量采集效率有待提高
  • 缺乏RTT自适应和MTU动态协商机制
  • 心跳保活机制不完善,死链检测慢
  • 固定MTU限制大数据块传输
  • 地址分散导致请求碎片化
  • 缺乏心跳保活机制,死链检测慢
  • 异常恢复时间长

1.4 性能瓶颈分析与优化目标

维度 当前痛点 优化目标 关键技术
延迟 (Latency) 静态超时/间隔策略导致长尾延迟;网络抖动引起重传风暴。 99th 百分位延迟 < 10ms RTT 动态自适应窗口;EWMA 预测算法;QoS 优先级队列
吞吐 (Throughput) 固定 MTU (如 Modbus 250B) 限制大数据块传输;地址分散导致请求碎片化。 自适应 MTU (最大支持 4096B) 智能 MTU 动态协商;Gap 空洞合并算法;零拷贝环形缓冲
稳定性 (Stability) 缺乏心跳保活机制,死链检测慢;异常恢复时间长。 故障检测 < 3s;自动重连恢复 智能业务心跳 (Traffic-Aware Heartbeat);链路层状态机

1.5 分层架构重构原则

采用 南向采集 -> 缓存 -> 影子设备 -> 边缘计算 -> 北向上报 的分层处理,并引入影子设备作为唯一内部数据模型中心:

  1. 采集层 (Collection Layer):
    • 驱动适配: 统一封装 Serial, TCP, UDP 等物理通道。
    • 协议适配: 适配 Modbus, S7, BACnet 等工业协议,增加 HeartbeatInterval, BufferSize, Qos 等控制参数。
    • 优化策略: 实现 RTT 统计、MTU 协商状态机与 Gap 合并策略。
  2. 缓存层 (Buffer Layer):
    • 使用零拷贝环形缓冲 (Ring Buffer) 存储原始报文。
    • 实现 ACK 压缩与重复包过滤。
  3. 边缘计算层 (Edge Computing Layer):
    • 执行 TSL 物模型解析、公式计算、数据清洗。
    • 动态调整采集策略 (如降级、加速)。
  4. 影子设备核心层 (Shadow Core Layer):
    • Real Shadow Store:纯内存运行时快照。
    • Virtual Shadow Engine:跨设备公式依赖计算。
    • Edge Compute Engine:差值/步长/滑动平均/阈值计算。
    • Atomic Dispatcher:版本推进 + QoS ACK 机制。
  5. 北向上报层 (Northbound Layer):
    • 数据标准化 JSON/MQTT 上报。
    • 提供 COLLECT_CTRL 指令通道,支持参数热更新(已实现简单的双向控制功能的基础上优化)。
    • 统一鉴权、限流、审计机制。

2. 智能画像方案设计

2.1 设计目标

  1. 统一设备画像:为所有协议设备建立统一的画像结构
  2. 智能适配:根据设备特性自动调整采集策略
  3. 并发优化:合理安排采集顺序,提高并发效率
  4. 批量优化:优化同一总线上的设备采集,减少通信开销
  5. RTT自适应:基于往返时间动态调整采集参数
  6. MTU动态协商:智能调整最大传输单元,提高吞吐量
  7. 可扩展性:模块化设计,易于添加新协议支持
  8. 监控能力:提供设备状态监控和性能统计

2.2 可移植抽象层设计 (HAL)

定义统一的通信驱动接口 CommDriver,屏蔽底层差异:

type CommDriver interface {
    Send(packet []byte) (timestamp time.Time, err error)
    Recv() (packet []byte, timestamp time.Time, err error)
    SetParam(key string, value interface{}) error // 设置 BufferSize, Timeout 等
    GetParam(key string) (interface{}, error)
    GetStats() CommStats // 获取丢包、RTT 等统计
}

2.3 关键参数清单

结合项目已有的协议扩展,以下为关键配置参数:

2.3.1 通用协议参数 (已集成)

针对所有支持的协议 (Modbus, TCP, S7, BACnet, OPC UA, Serial),统一增加以下控制字段:

  • HeartbeatInterval (ms): 应用层心跳间隔。
    • 优化逻辑: 仅在链路空闲且超过该间隔时才发送保活包。
    • 抑制策略: 任何成功的业务通信(如 Modbus 读取响应)都会重置心跳计时器,避免在正常采集时产生额外流量。
  • BufferSize (bytes): 接收缓冲区大小。配合 MTU 协商,支持大数据包接收 (默认 4096)。
  • Qos (0-2): 服务质量等级。
    • 0: 尽力而为 (Fire and Forget)。
    • 1: 至少一次 (At least once, 需要 ACK)。
    • 2: 恰好一次 (Exactly once, 防重)。
  • Interval (ms): 采集轮询间隔 (部分协议如 BACnet/S7/OPC UA 新增/修正)。

2.3.2 RTT 模式参数 (自适应)

  • rtt_sample_window: 采样窗口大小 (默认 20)。
  • rtt_alpha_gain: EWMA (指数加权移动平均) 增益系数 (默认 0.125)。
  • rtt_threshold_high: 高延迟阈值 (触发降级/报警)。
  • rtt_threshold_low: 低延迟阈值 (触发加速采集)。
  • rtt_probe_interval_ms: RTT 探测包发送间隔。

2.3.3 智能 MTU 模式参数

  • mtu_max: 最大支持 MTU (如 Modbus TCP 250, S7 PDU 960)。
  • mtu_min: 最小安全 MTU (如 64)。
  • mtu_step: 探测步长 (二分法或线性递增)。
  • mtu_hysteresis: 抖动滞后因子 (防止频繁切换)。

2.3.4 Gap 合并策略参数 (Batch Read Optimization)

针对 Modbus/PLC 等寄存器读取协议,通过合并相邻地址请求提升总线效率:

  • gap_max_hole: 最大允许地址空洞 (默认 0,表示完全连续才合并;最大受 MTU 限制)。
  • gap_fill_strategy: 空洞填充策略 (0: 填充无用数据读取; 1: 分割请求)。
  • gap_dynamic_enable: 是否启用基于 RTT 的动态 Gap 调整 (RTT 低时增大 Gap,RTT 高时减小 Gap)。

2.4 核心结构体定义

2.4.1 采集通信上下文

管理单条连接的生命周期与状态。

type CollectCommContext struct {
    RTTBuffer    []RTTNode             // RTT 统计环缓冲
    RTTIndex     int                   // 环缓冲当前索引
    MTUState     *MTUFSM               // MTU 协商状态机
    BatchRead    *BatchReadSnapshot    // Gap 合并策略快照
    LastActivity time.Time             // 最后活跃时间 (业务通信成功时刻)
    Stats        CommStats             // 统计信息
    Config       ProtocolConfig        // 协议配置 (含 Heartbeat, BufferSize 等)
}

type ProtocolConfig struct {
    HeartbeatInterval int
    BufferSize        int
    Qos               int
    Interval          int
}

2.4.2 RTT 统计节点

用于计算单次通信质量。

type RTTNode struct {
    SeqNo       uint16    // 序列号
    SendTS      time.Time // 发送时间戳
    RecvTS      time.Time // 接收时间戳
    AckStatus   bool      // 是否收到 ACK
    RTT         int64     // 计算出的 RTT (us)
}

2.4.3 MTU 协商记录

记录 MTU 探测历史,辅助状态机决策。

type MTUNegotiationRecord struct {
    AttemptValue int       // 尝试值
    ResponseTime int64     // 耗时 (us)
    RetryCount   int       // 重试次数
    Success      bool      // 结果
    Timestamp    time.Time // 记录时间
}

2.4.4 Batch Read 快照

记录当前 Gap 合并策略的运行状态。

type BatchReadSnapshot struct {
    CurrentGap      int     // 当前生效的 Gap 值
    MaxGap          int     // 允许的最大 Gap (受 MTU 限制)
    MergedRequests  uint64  // 合并后的请求总数
    SavedRequests   uint64  // 节省的请求数量 (原请求数 - 合并后请求数)
    FillEfficiency  float64 // 填充效率 (有效数据 / 总请求数据)
}

2.5 核心设计

2.5.1 统一设备画像结构

// 设备通信画像结构
type DeviceCommunicationProfile struct {
	DeviceID        string            // 设备唯一标识
	ChannelID       string            // 所属通道ID
	ProtocolType    string            // 协议类型
	SlaveID         interface{}       // 从站ID
	AvgResponseTime time.Duration     // 平均响应时间
	MaxResponseTime time.Duration     // 最大响应时间
	ErrorRate       float64           // 错误率
	StabilityScore  float64           // 稳定性得分 (0-1)
	OptimalTimeout  time.Duration     // 最优超时时间
	OptimalInterval time.Duration     // 最优采集间隔
	RetryCount      int               // 重试次数
	BatchSize       int               // 批量读取大小
	ProtocolParams  map[string]interface{} // 协议特定参数
	LastUpdated     time.Time         // 最后更新时间
	采集成功率       float64           // 采集成功率
	异常点位数量     int               // 异常点位数量
	连续失败次数     int               // 连续失败次数
	// RTT相关字段
	RTTSamples      []int64           // RTT采样数据
	RTTSampleWindow int               // RTT采样窗口大小
	EWMARTT         int64             // 指数加权移动平均RTT
	// MTU相关字段
	CurrentMTU      int               // 当前MTU值
	MaxMTU          int               // 最大MTU值
	MinMTU          int               // 最小MTU值
	// Gap合并相关字段
	CurrentGap      int               // 当前Gap值
	MaxGap          int               // 最大Gap值
	GapFillStrategy int               // Gap填充策略
	// 心跳相关字段
	HeartbeatInterval int             // 心跳间隔
	LastActivity    time.Time         // 最后活跃时间
}

2.2.2 智能适配接口

// 智能适配接口
type DeviceAdapter interface {
	// 获取设备画像
	GetProfile(deviceID string) (*DeviceCommunicationProfile, error)
	
	// 更新设备画像
	UpdateProfile(deviceID string, responseTime time.Duration, err error, successCount, failCount int)
	
	// 获取最优采集参数
	GetOptimalParams(deviceID string) (map[string]interface{}, error)
	
	// 标记设备状态
	MarkDeviceStatus(deviceID string, status string)
	
	// 获取设备状态
	GetDeviceStatus(deviceID string) string
	
	// 批量优化
	OptimizeBatchCollection(devices []string) []string
	
	// RTT统计
	UpdateRTT(deviceID string, rtt int64)
	
	// MTU协商
	NegotiateMTU(deviceID string) error
	
	// Gap优化
	OptimizeGap(deviceID string) int
}

2.2.3 协议适配器接口

// 协议适配器接口
type ProtocolAdapter interface {
	// 协议类型
	ProtocolType() string
	
	// 初始化设备参数
	InitDeviceParams(deviceID string) error
	
	// 调整协议特定参数
	AdjustProtocolParams(profile *DeviceCommunicationProfile) error
	
	// 验证设备响应
	ValidateResponse(deviceID string, response interface{}, err error) bool
	
	// 获取协议默认参数
	GetDefaultParams() map[string]interface{}
	
	// 获取协议MTU范围
	GetMTURange() (min, max int)
	
	// 计算最优Gap值
	CalculateOptimalGap(profile *DeviceCommunicationProfile) int
	
	// 构建心跳包
	BuildHeartbeatPacket(deviceID string) ([]byte, error)
}

2.3 实现架构

2.3.1 核心组件

  1. DeviceAdapterManager:设备适配器管理器,负责管理所有设备的画像和适配策略
  2. ProtocolAdapterRegistry:协议适配器注册表,管理不同协议的适配器
  3. CollectionScheduler:采集调度器,根据设备画像优化采集顺序和策略
  4. BatchOptimizer:批量采集优化器,优化同一总线上的设备采集
  5. RTTManager:RTT管理器,负责RTT统计和分析
  6. MTUManager:MTU管理器,负责MTU动态协商
  7. GapOptimizer:Gap优化器,负责批量读取的Gap合并策略

2.3.2 数据流

  1. 数据采集流程
    • 采集前:获取设备最优参数,包括RTT、MTU、Gap等
    • 采集中:记录响应时间、RTT、错误等信息
    • 采集后:更新设备画像,包括RTT统计、MTU调整、Gap优化
  2. 参数调整流程
    • 基于历史RTT数据计算EWMA
    • 根据RTT动态调整采集间隔和超时时间
    • 基于MTU协商结果调整批量读取大小
    • 根据RTT和MTU动态调整Gap值

2.4 多协议支持

2.4.1 协议差异处理

协议 特有参数 适配策略
Modbus 从站地址、功能码、寄存器范围 批量读取优化、帧间隔调整、RTT自适应、MTU协商
TCP 连接超时、重试次数 连接池管理、并发控制、RTT自适应
BACnet 对象ID、属性ID 批量读属性、COV订阅、RTT自适应
S7 DB块、数据类型、偏移量 批量读取、数据块优化、MTU协商
OPC UA 节点ID、采样间隔 订阅优化、批处理、RTT自适应
Serial 波特率、奇偶校验、停止位 串口参数调整、缓冲区管理、RTT自适应

2.4.2 统一接口实现

2.5 影子设备设计

2.5.1 核心设计理念

  • 统一内部数据面:Shadow Device 作为唯一可读写模型,所有数据操作通过 Shadow Core 进行。
  • 统一控制面:所有写操作进入原子接口与审计链,确保数据一致性。
  • 统一扩展面:南向/北向均可热插拔,系统重启零丢数。

2.5.2 分层架构

┌───────────────────────────────────────────────────────────────────────────┐
│ Northbound Adapters (MQTT / OPC UA / REST / Modbus-TCP Server / Others) │
│  - AuthN/AuthZ  - Rate Limit  - Audit Log  - Dynamic Mount/Unmount       │
└───────────────────────────────┬───────────────────────────────────────────┘
                                │
                         Shadow Service API
               read/write/subscribe/consistency-check
                                │
┌───────────────────────────────┴───────────────────────────────────────────┐
│ Shadow Core (唯一内部模型中心)                                            │
│  - Real Shadow Store (In-Memory)                                            │
│  - Virtual Shadow Engine (公式依赖图 + 增量计算)                           │
│  - Edge Compute Engine (差值/步长/滑动平均/阈值)                           │
│  - Atomic Dispatcher (CAS + 序列号 + QoS ACK)                             │
└───────────────────────────────┬───────────────────────────────────────────┘
                                │
                           Shadow Ingress
                                │
┌───────────────────────────────┴───────────────────────────────────────────┐
│ Points Layer                                                               │
│  - Normalize  - Unit Convert  - Timestamp Align  - Batch Publish QoS0/1/2 │
└───────────────────────────────┬───────────────────────────────────────────┘
                                │
                     Southbound Drivers (Modbus/CAN/IEC/...)

2.5.3 关键流程

  1. 采集入影子时序
    • 驱动采集原始值写入 Points。
    • Points 完成归一化并以统一报文批量发布至 ShadowIngress。
    • Shadow Core 原子落地内存。
    • 触发边缘计算与虚拟公式增量更新。
    • 北向适配器通过统一 API 拉取或订阅结果。
  2. 北向写入控制时序
    • 北向适配器请求授权与限流。
    • 调用 Shadow Core 写入接口。
    • Shadow Core 更新内存并通知订阅者。
    • 记录审计日志并返回版本确认。
  3. 重启恢复时序
    • 进程启动后加载配置(config.db)。
    • ScanEngine 重新采集并填充 Shadow Core 内存快照。
    • 重建虚拟引擎依赖缓存。
    • 恢复北向适配器游标并继续服务。

2.5.4 核心数据结构

2.5.4.1 点位标准报文(Points -> ShadowIngress)
messageId: "uuid"
qos: 0|1|2
deviceId: "physical-device-id"
timestamp: "2026-03-04T12:00:00.000Z"
points:
  - pointId: "pack_voltage"
    value: 742.3
    unit: "V"
    quality: "good"
    samplePeriodMs: 1000
meta:
  source: "modbus-driver"
  sequence: 123456
2.5.4.2 真实影子设备结构
shadowDeviceId: "shadow-rack-01"
physicalDeviceId: "rack-01"
version: 202603040001
updatedAt: "2026-03-04T12:00:00.001Z"
points:
  pack_voltage:
    value: 742.3
    unit: "V"
    rw: "r"
    samplePeriodMs: 1000
2.5.4.3 虚拟影子设备结构
virtualDeviceId: "virtual-energy-mix-01"
version: 202603040010
formulaPoints:
  A: "device1.temp + device2.humidity"
dependencies:
  - "device1.temp"
  - "device2.humidity"
2.5.4.4 设备历史快照结构
ts: 1741084800
data:
  pack_voltage: 742.3
  pack_current: 125.0

2.5.5 一致性校验

  • 接口输入:时间点 t 或版本号区间。
  • 校验对象:真实影子、虚拟影子、北向缓存。
  • 判定条件:三端 value + version + timestamp + quality 全量一致。
  • 输出结果pass/fail、差异点位、差异来源、修复建议。

2.5.6 性能与容量目标

  • 影子单机读写延迟:P99 ≤ 5ms。
  • 虚拟公式计算:P99 ≤ 15ms。
  • 虚拟点位传播延迟:≤ 100ms(单核 2GHz 基准)。
  • 稳态吞吐:5 万点/秒持续 24h 零丢数。

2.6 边缘运维与设备替换(Gossip 分布式配置同步)

2.6.1 阶段目标

  • 建立基于 Gossip 的本地网络配置一致性机制。
  • 每个节点将本机与最近邻节点配置增量同步到本机 bbolt DB。
  • 支持设备替换与离线缓存,网络恢复后自动补同步。
  • 提供冲突检测与自动合并,冲突全过程审计可追溯。

2.6.2 同步数据模型

  • device_uuid:Base58 编码,默认基于 LAN 口 MAC 生成;支持手动指定。
  • config_content:配置文件完整内容(文本或二进制编码)。
  • sync_timestamp:最近一次同步时间戳(UTC,毫秒精度)。
  • config_sha256:配置内容 SHA-256 哈希。
  • source_node:来源节点 UUID。
  • version_vector:可选向量时钟,用于高级冲突定位。

2.6.3 UUID 规则

  • 默认生成uuid = Base58(SHA-256(lan_mac))[0:22]
  • 手工指定:写入本地设备标识配置,重启后生效且优先级高于默认生成。
  • 替换场景:新设备可继承旧 UUID,确保邻居节点将其识别为同一逻辑节点。

2.6.4 架构组件

  • Gossip 通讯模块:节点发现、成员心跳、增量反熵同步。
  • bbolt 持久化层:桶设计包括 configssync_metaoffline_queueconflict_logs
  • 冲突解决引擎:时间戳最新优先(Last-Write-Wins)策略,保留审计轨迹。
  • 离线补同步队列:断网时写入队列,恢复后按时间顺序重放并去重确认。
  • 运维工具与监控 API:UUID 管理、同步状态监控、冲突处理。

2.6.5 同步流程

  1. 本地配置变更后计算 SHA-256 并写入 bbolt。
  2. Gossip 广播摘要;邻居比对后按需请求缺失配置内容。
  3. 接收端执行冲突检测,按最新时间戳合并并记录审计日志。
  4. 合并结果写入 configs 并更新时间戳,触发二次扩散。
  5. 离线节点恢复后,自动从邻居拉取缺失版本并补齐。

2.6.6 异常与恢复策略

  • 网络分区:分区内局部一致,合区后执行冲突归并与去重扩散。
  • 设备掉线:节点状态标记为 suspect/down,保留其最近有效配置快照。
  • 配置回滚:支持指定 config_sha256 或时间点回滚,并通过 Gossip 广播回滚版本。
  • 时钟漂移:启用 NTP 校时;漂移超阈值时标记低可信并告警。

2.6.7 监控 API

  • GET /api/v1/ops/sync/nodes:节点在线状态与最近同步时间。
  • GET /api/v1/ops/sync/status:全局同步进度、队列长度、补同步耗时。
  • GET /api/v1/ops/sync/conflicts:冲突列表、处理结果、责任节点。
  • POST /api/v1/ops/sync/reconcile:触发手动补同步与重算。
  • POST /api/v1/ops/uuid/assign:手工设置设备 UUID(重启后生效)。

2.7 RTT管理器实现

// RTT管理器
type RTTManager struct {
	rttData      map[string][]int64
	ewmaData     map[string]int64
	sampleWindow int
	alpha        float64
	mu           sync.RWMutex
}

func NewRTTManager() *RTTManager {
	return &RTTManager{
		rttData:      make(map[string][]int64),
		ewmaData:     make(map[string]int64),
		sampleWindow: 20,
		alpha:        0.125,
	}
}

func (r *RTTManager) UpdateRTT(deviceID string, rtt int64) {
	r.mu.Lock()
	defer r.mu.Unlock()
	
	if _, exists := r.rttData[deviceID]; !exists {
		r.rttData[deviceID] = make([]int64, 0, r.sampleWindow)
		r.ewmaData[deviceID] = rtt
	}
	
	r.rttData[deviceID] = append(r.rttData[deviceID], rtt)
	if len(r.rttData[deviceID]) > r.sampleWindow {
		r.rttData[deviceID] = r.rttData[deviceID][1:]
	}
	
	// 计算EWMA
	currentEWMA := r.ewmaData[deviceID]
	newEWMA := int64(float64(currentEWMA)*(1-r.alpha) + float64(rtt)*r.alpha)
	r.ewmaData[deviceID] = newEWMA
}

func (r *RTTManager) GetEWMARTT(deviceID string) int64 {
	r.mu.RLock()
	defer r.mu.RUnlock()
	
	if ewma, exists := r.ewmaData[deviceID]; exists {
		return ewma
	}
	return 0
}

2.8 MTU管理器实现

// MTU管理器
type MTUManager struct {
	mtuData       map[string]int
	mtuHistory    map[string][]MTUNegotiationRecord
	minMTU        map[string]int
	maxMTU        map[string]int
	hysteresis    float64
	mu            sync.RWMutex
}

func NewMTUManager() *MTUManager {
	return &MTUManager{
		mtuData:       make(map[string]int),
		mtuHistory:    make(map[string][]MTUNegotiationRecord),
		minMTU:        make(map[string]int),
		maxMTU:        make(map[string]int),
		hysteresis:    0.1,
	}
}

func (m *MTUManager) NegotiateMTU(deviceID string, rtt int64) {
	m.mu.Lock()
	defer m.mu.Unlock()
	
	if _, exists := m.mtuData[deviceID]; !exists {
		m.mtuData[deviceID] = 1500
		m.minMTU[deviceID] = 64
		m.maxMTU[deviceID] = 4096
		m.mtuHistory[deviceID] = make([]MTUNegotiationRecord, 0)
	}
	
	// 简单的MTU协商逻辑
	currentMTU := m.mtuData[deviceID]
	minMTU := m.minMTU[deviceID]
	maxMTU := m.maxMTU[deviceID]
	
	// 根据RTT调整MTU
	if rtt < 10000 { // RTT < 10ms
		// 尝试增加MTU
		newMTU := currentMTU * 2
		if newMTU > maxMTU {
			newMTU = maxMTU
		}
		if newMTU > currentMTU {
			m.mtuData[deviceID] = newMTU
			m.mtuHistory[deviceID] = append(m.mtuHistory[deviceID], MTUNegotiationRecord{
				AttemptValue: newMTU,
				ResponseTime: rtt,
				RetryCount:   0,
				Success:      true,
				Timestamp:    time.Now(),
			})
		}
	} else if rtt > 50000 { // RTT > 50ms
		// 尝试减少MTU
		newMTU := currentMTU / 2
		if newMTU < minMTU {
			newMTU = minMTU
		}
		if newMTU < currentMTU {
			m.mtuData[deviceID] = newMTU
			m.mtuHistory[deviceID] = append(m.mtuHistory[deviceID], MTUNegotiationRecord{
				AttemptValue: newMTU,
				ResponseTime: rtt,
				RetryCount:   0,
				Success:      true,
				Timestamp:    time.Now(),
			})
		}
	}
}

func (m *MTUManager) GetCurrentMTU(deviceID string) int {
	m.mu.RLock()
	defer m.mu.RUnlock()
	
	if mtu, exists := m.mtuData[deviceID]; exists {
		return mtu
	}
	return 1500
}

2.9 Gap优化器实现

// Gap优化器
type GapOptimizer struct {
	gapData     map[string]int
	mu          sync.RWMutex
}

func NewGapOptimizer() *GapOptimizer {
	return &GapOptimizer{
		gapData: make(map[string]int),
	}
}

func (g *GapOptimizer) OptimizeGap(deviceID string, mtu int, rtt int64) int {
	g.mu.Lock()
	defer g.mu.Unlock()
	
	// 根据MTU和RTT计算最优Gap值
	// MTU越大,允许的Gap越大
	// RTT越小,允许的Gap越大
	baseGap := mtu / 100
	if baseGap < 0 {
		baseGap = 0
	}
	if baseGap > 20 {
		baseGap = 20
	}
	
	// 根据RTT调整
	rttFactor := 1.0
	if rtt > 20000 {
		rttFactor = 0.5
	} else if rtt > 10000 {
		rttFactor = 0.8
	}
	
	optimalGap := int(float64(baseGap) * rttFactor)
	if optimalGap < 0 {
		optimalGap = 0
	}
	
	g.gapData[deviceID] = optimalGap
	return optimalGap
}

2.10 并发采集优化

2.10.1 智能调度算法

// 采集调度器
type CollectionScheduler struct {
	adapterManager *DeviceAdapterManager
}

// 优化采集顺序
func (s *CollectionScheduler) OptimizeCollectionOrder(devices []string) []string {
	// 获取所有设备的画像
	profiles := make(map[string]*DeviceCommunicationProfile)
	for _, deviceID := range devices {
		if profile, err := s.adapterManager.GetProfile(deviceID); err == nil {
			profiles[deviceID] = profile
		}
	}
	
	// 根据稳定性得分、响应时间和RTT排序
	sort.Slice(devices, func(i, j int) bool {
		profileI, existsI := profiles[devices[i]]
		profileJ, existsJ := profiles[devices[j]]
		
		// 优先采集稳定设备
		if existsI && existsJ {
			if profileI.StabilityScore != profileJ.StabilityScore {
				return profileI.StabilityScore > profileJ.StabilityScore
			}
			// 稳定性相同时,优先采集响应快的设备
			if profileI.AvgResponseTime != profileJ.AvgResponseTime {
				return profileI.AvgResponseTime < profileJ.AvgResponseTime
			}
			// 响应时间相同时,优先采集RTT小的设备
			return profileI.EWMARTT < profileJ.EWMARTT
		}
		// 没有画像的设备放在最后
		return existsI && !existsJ
	})
	
	return devices
}

// 批量采集优化
func (s *CollectionScheduler) OptimizeBatchCollection(devices []string) [][]string {
	// 根据协议类型和总线分组
	protocolGroups := make(map[string][]string)
	for _, deviceID := range devices {
		if profile, err := s.adapterManager.GetProfile(deviceID); err == nil {
			key := profile.ProtocolType
			// 对于Modbus等协议,按从站地址进一步分组
			if strings.HasPrefix(profile.ProtocolType, "modbus") {
				if slaveID, ok := profile.SlaveID.(string); ok {
					key += ":" + slaveID
				} else if slaveID, ok := profile.SlaveID.(uint8); ok {
					key += ":" + strconv.Itoa(int(slaveID))
				}
			}
			protocolGroups[key] = append(protocolGroups[key], deviceID)
		}
	}
	
	// 为每个协议组创建批处理
	batches := make([][]string, 0)
	for _, group := range protocolGroups {
		// 根据设备特性进一步分组
		stableDevices := make([]string, 0)
		unstableDevices := make([]string, 0)
		
		for _, deviceID := range group {
			if profile, err := s.adapterManager.GetProfile(deviceID); err == nil {
				if profile.StabilityScore >= 0.8 {
					stableDevices = append(stableDevices, deviceID)
				} else {
					unstableDevices = append(unstableDevices, deviceID)
				}
			}
		}
		
		// 稳定设备可以批量采集
		if len(stableDevices) > 0 {
			batches = append(batches, stableDevices)
		}
		
		// 不稳定设备单独采集
		for _, deviceID := range unstableDevices {
			batches = append(batches, []string{deviceID})
		}
	}
	
	return batches
}

2.11 与现有系统集成

2.11.1 集成点

  1. ChannelManager集成:在设备采集循环中集成智能适配逻辑
  2. 驱动层集成:在驱动读取操作中应用最优参数,包括RTT、MTU、Gap等
  3. 状态管理集成:将设备画像与现有状态管理系统结合
  4. Diagnostics 集成:设备画像摘要暴露于现有 diagnostics API,供 UI 轮询与 curl 巡检
  5. 心跳机制集成:实现智能业务心跳,减少不必要的心跳包

2.11.2 集成实现

// 在ChannelManager的deviceLoop中集成智能适配
func (cm *ChannelManager) deviceLoop(dev *model.Device, d drv.Driver, ch *model.Channel) {
	// 初始化设备适配器
	adapter := globalDeviceAdapter
	
	ticker := time.NewTicker(time.Duration(dev.Interval))
	defer ticker.Stop()

	node := cm.stateManager.GetNode(dev.ID)
	if node == nil {
		zap.L().Warn("Device node not found in state manager", zap.String("device", dev.Name))
		return
	}

	var offset time.Duration
	for i := range ch.Devices {
		if ch.Devices[i].ID == dev.ID {
			offset = time.Duration(i) * 12 * time.Millisecond
			break
		}
	}

	for {
		select {
		case <-cm.ctx.Done():
			return
		case <-dev.StopChan:
			return
		case <-ticker.C:
			time.Sleep(offset)
			if !cm.stateManager.ShouldCollect(node) {
				zap.L().Debug("Device skipped collection",
					zap.String("device", dev.Name),
					zap.Any("state", node.Runtime.State),
					zap.Time("next_retry", node.Runtime.NextRetryTime))
				continue
			}

			cm.collectDeviceWithAdapter(dev, d, ch, node, adapter)
		}
	}
}

// 带智能适配的设备采集
func (cm *ChannelManager) collectDeviceWithAdapter(dev *model.Device, d drv.Driver, ch *model.Channel, node *DeviceNodeTemplate, adapter DeviceAdapter) {
	start := time.Now()
	defer func() {
		duration := time.Since(start)
		zap.L().Info("Device collection cycle finished",
			zap.String("device", dev.Name),
			zap.Int("point_count", len(dev.Points)),
			zap.String("duration", fmt.Sprintf("%.3fs", duration.Seconds())),
			zap.Int64("rtt_us", duration.Microseconds()),
		)
	}()

	zap.L().Info("PollStart", zap.String("device", dev.Name), zap.Time("ts", time.Now()))

	// 获取最优参数
	params, err := adapter.GetOptimalParams(dev.ID)
	if err != nil {
		zap.L().Warn("Failed to get optimal params", zap.String("device", dev.Name), zap.Error(err))
	}

	// 应用最优超时时间
	timeout := 5 * time.Second
	if params != nil {
		if timeoutVal, ok := params["timeout"].(time.Duration); ok {
			timeout = timeoutVal
		}
	}
	if node.Runtime.State != NodeStateOnline {
		timeout = 200 * time.Millisecond
	}

	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	// 获取驱动互斥锁
	cm.mu.RLock()
	mu, okMu := cm.driverMus[ch.ID]
	cm.mu.RUnlock()

	if okMu {
		mu.Lock()
		defer mu.Unlock()
	}

	// 设置从机 ID
	if slaveID, ok := dev.Config["slave_id"]; ok {
		if slaveIDUint, ok := slaveID.(float64); ok {
			d.SetSlaveID(uint8(slaveIDUint))
		} else if slaveIDInt, ok := slaveID.(int); ok {
			d.SetSlaveID(uint8(slaveIDInt))
		}
	}

	// 设置设备配置
	config := make(map[string]any)
	for k, v := range dev.Config {
		config[k] = v
	}
	config["_internal_device_id"] = dev.ID

	// 应用MTU和Gap参数
	if params != nil {
		if mtu, ok := params["mtu"].(int); ok {
			config["mtu"] = mtu
		}
		if gap, ok := params["gap"].(int); ok {
			config["gap"] = gap
		}
	}

	if err := d.SetDeviceConfig(config); err != nil {
		zap.L().Error("Failed to set device config", zap.String("device", dev.Name), zap.Error(err))
	}

	// Ensure DeviceID is set on points for the driver
	for i := range dev.Points {
		dev.Points[i].DeviceID = dev.ID
	}

	// 读取点位数据
	results, err := d.ReadPoints(ctx, dev.Points)
	responseTime := time.Since(start)

	// 统计采集结果
	successCount := 0
	failCount := 0
	now := time.Now()

	for _, result := range results {
		// 发送到管道
		val := model.Value{
			ChannelID: ch.ID,
			DeviceID:  dev.ID,
			PointID:   result.PointID,
			Value:     result.Value,
			Quality:   result.Quality,
			TS:        now,
		}
		// 推入数据管道,驱动存储与WebSocket广播
		cm.pipeline.Push(val)

		// 统计成功/失败
		if result.Quality == "Good" {
			successCount++
		} else {
			failCount++
		}
	}

	// 使用 FinalizeCollect 进行状态裁决
	if len(dev.Points) > 0 && len(results) == 0 {
		failCount = len(dev.Points) // 假设所有点位都失败
	}

	// 更新设备画像
	adapter.UpdateProfile(dev.ID, responseTime, err, successCount, failCount)

	// Update Device Metrics
	if mc := model.GetGlobalMetricsCollector(); mc != nil {
		mc.UpdateDeviceMetrics(dev.ID, func(m *model.DeviceMetrics) {
			total := successCount + failCount
			if total > 0 {
				m.PointSuccessRate = float64(successCount) / float64(total)
			} else {
				m.PointSuccessRate = 0
			}
			m.AbnormalPoints = failCount
			if failCount == len(dev.Points) && len(dev.Points) > 0 {
				m.ConsecutiveFailures++
			} else {
				m.ConsecutiveFailures = 0
			}
			m.LastCollectTime = now
			m.State = int(node.Runtime.State)
		})
	}

	collectCtx := &CollectContext{
		TotalCmd:   successCount + failCount,
		SuccessCmd: successCount,
		FailCmd:    failCount,
	}
	cm.stateManager.FinalizeCollect(node, collectCtx)
}

2.12 配置与监控

2.12.1 配置结构

2.13 通用点位降级框架

2.13.1 设计目标

  1. 协议无关性:为所有支持的协议提供统一的点位降级机制
  2. 智能降级:基于点位失败次数和时间的智能降级策略
  3. 动态恢复:当点位恢复正常时自动重新纳入采集
  4. 指数级退避:对降级点位采用指数级间隔探测机制
  5. 与设备画像集成:将点位降级状态与设备画像系统集成
  6. 可扩展性:易于添加新协议的点位降级支持

2.13.2 核心数据结构

// 点位状态管理结构
type PointStatus struct {
    PointID          string            // 点位唯一标识
    DeviceID         string            // 所属设备ID
    ProtocolType     string            // 协议类型
    FailureCount     int               // 连续失败次数
    LastSuccessTime  time.Time         // 最后成功时间
    LastFailureTime  time.Time         // 最后失败时间
    Degraded         bool              // 是否处于降级状态
    LastProbeTime    time.Time         // 最后探测时间
    ProbeInterval    int               // 当前探测间隔(秒)
    MaxProbeInterval int               // 最大探测间隔(秒)
    Status           string            // 点位状态:normal, warning, degraded, recovering
    Metadata         map[string]interface{} // 协议特定元数据
}

// 点位降级管理器
type PointDegradationManager struct {
    pointStatuses      map[string]*PointStatus // 点位状态映射
    protocolAdapters   map[string]ProtocolPointAdapter // 协议点位适配器
    failureThreshold   int               // 失败次数阈值
    maxProbeInterval   int               // 最大探测间隔
    mu                 sync.RWMutex      // 读写锁
}

// 协议点位适配器接口
type ProtocolPointAdapter interface {
    // 协议类型
    ProtocolType() string
    
    // 初始化点位状态
    InitPointStatus(pointID, deviceID string) *PointStatus
    
    // 判定点位是否失败
    IsPointFailure(pointID string, response interface{}, err error) bool
    
    // 处理点位成功
    HandlePointSuccess(status *PointStatus)
    
    // 处理点位失败
    HandlePointFailure(status *PointStatus)
    
    // 检查是否需要探测
    ShouldProbe(status *PointStatus) bool
    
    // 执行点位探测
    ProbePoint(pointID, deviceID string) bool
    
    // 获取协议特定的失败阈值
    GetFailureThreshold() int
}

2.13.3 核心实现

// 创建点位降级管理器
func NewPointDegradationManager() *PointDegradationManager {
    return &PointDegradationManager{
        pointStatuses:    make(map[string]*PointStatus),
        protocolAdapters: make(map[string]ProtocolPointAdapter),
        failureThreshold: 3,
        maxProbeInterval: 1024,
    }
}

// 注册协议点位适配器
func (p *PointDegradationManager) RegisterProtocolAdapter(adapter ProtocolPointAdapter) {
    p.mu.Lock()
    defer p.mu.Unlock()
    p.protocolAdapters[adapter.ProtocolType()] = adapter
}

// 获取点位状态
func (p *PointDegradationManager) GetPointStatus(pointID, deviceID, protocolType string) *PointStatus {
    p.mu.RLock()
    key := fmt.Sprintf("%s:%s", deviceID, pointID)
    status, exists := p.pointStatuses[key]
    p.mu.RUnlock()
    
    if !exists {
        p.mu.Lock()
        defer p.mu.Unlock()
        // 双重检查
        if status, exists = p.pointStatuses[key]; !exists {
            adapter, adapterExists := p.protocolAdapters[protocolType]
            if adapterExists {
                status = adapter.InitPointStatus(pointID, deviceID)
            } else {
                status = &PointStatus{
                    PointID:          pointID,
                    DeviceID:         deviceID,
                    ProtocolType:     protocolType,
                    FailureCount:     0,
                    LastSuccessTime:  time.Time{},
                    LastFailureTime:  time.Time{},
                    Degraded:         false,
                    LastProbeTime:    time.Time{},
                    ProbeInterval:    8,
                    MaxProbeInterval: p.maxProbeInterval,
                    Status:           "normal",
                    Metadata:         make(map[string]interface{}),
                }
            }
            p.pointStatuses[key] = status
        }
    }
    return status
}

// 更新点位状态
func (p *PointDegradationManager) UpdatePointStatus(pointID, deviceID, protocolType string, success bool, response interface{}, err error) {
    key := fmt.Sprintf("%s:%s", deviceID, pointID)
    status := p.GetPointStatus(pointID, deviceID, protocolType)
    
    p.mu.Lock()
    defer p.mu.Unlock()
    
    adapter, exists := p.protocolAdapters[protocolType]
    if exists {
        if success && !adapter.IsPointFailure(pointID, response, err) {
            adapter.HandlePointSuccess(status)
        } else {
            adapter.HandlePointFailure(status)
        }
    } else {
        if success && err == nil {
            // 通用成功处理
            status.FailureCount = 0
            status.LastSuccessTime = time.Now()
            status.Degraded = false
            status.Status = "normal"
            status.ProbeInterval = 8
        } else {
            // 通用失败处理
            status.FailureCount++
            status.LastFailureTime = time.Now()
            if status.FailureCount >= p.failureThreshold {
                status.Degraded = true
                status.Status = "degraded"
            } else if status.FailureCount > 0 {
                status.Status = "warning"
            }
        }
    }
}

// 检查点位是否应该被采集
func (p *PointDegradationManager) ShouldCollectPoint(pointID, deviceID, protocolType string) bool {
    status := p.GetPointStatus(pointID, deviceID, protocolType)
    
    if !status.Degraded {
        return true
    }
    
    // 检查是否需要探测
    adapter, exists := p.protocolAdapters[protocolType]
    if exists {
        return adapter.ShouldProbe(status)
    }
    
    // 通用探测逻辑
    now := time.Now()
    if now.Sub(status.LastProbeTime).Seconds() >= float64(status.ProbeInterval) {
        status.LastProbeTime = now
        // 指数级增加探测间隔
        newInterval := status.ProbeInterval * 2
        if newInterval > status.MaxProbeInterval {
            newInterval = status.MaxProbeInterval
        }
        status.ProbeInterval = newInterval
        return true
    }
    
    return false
}

// 批量检查点位采集状态
func (p *PointDegradationManager) FilterCollectiblePoints(points []model.Point, deviceID, protocolType string) []model.Point {
    var collectiblePoints []model.Point
    
    for _, point := range points {
        if p.ShouldCollectPoint(point.ID, deviceID, protocolType) {
            collectiblePoints = append(collectiblePoints, point)
        }
    }
    
    return collectiblePoints
}

2.13.4 协议特定实现

2.13.4.1 Modbus点位适配器
// Modbus点位适配器
type ModbusPointAdapter struct {
    failureThreshold int
}

func NewModbusPointAdapter() *ModbusPointAdapter {
    return &ModbusPointAdapter{
        failureThreshold: 3,
    }
}

func (m *ModbusPointAdapter) ProtocolType() string {
    return "modbus"
}

func (m *ModbusPointAdapter) InitPointStatus(pointID, deviceID string) *PointStatus {
    return &PointStatus{
        PointID:          pointID,
        DeviceID:         deviceID,
        ProtocolType:     "modbus",
        FailureCount:     0,
        LastSuccessTime:  time.Time{},
        LastFailureTime:  time.Time{},
        Degraded:         false,
        LastProbeTime:    time.Time{},
        ProbeInterval:    8,
        MaxProbeInterval: 1024,
        Status:           "normal",
        Metadata:         make(map[string]interface{}),
    }
}

func (m *ModbusPointAdapter) IsPointFailure(pointID string, response interface{}, err error) bool {
    if err != nil {
        return true
    }
    // 检查响应数据是否有效
    // ...
    return false
}

func (m *ModbusPointAdapter) HandlePointSuccess(status *PointStatus) {
    status.FailureCount = 0
    status.LastSuccessTime = time.Now()
    status.Degraded = false
    status.Status = "normal"
    status.ProbeInterval = 8
}

func (m *ModbusPointAdapter) HandlePointFailure(status *PointStatus) {
    status.FailureCount++
    status.LastFailureTime = time.Now()
    if status.FailureCount >= m.failureThreshold {
        status.Degraded = true
        status.Status = "degraded"
    } else if status.FailureCount > 0 {
        status.Status = "warning"
    }
}

func (m *ModbusPointAdapter) ShouldProbe(status *PointStatus) bool {
    now := time.Now()
    if now.Sub(status.LastProbeTime).Seconds() >= float64(status.ProbeInterval) {
        status.LastProbeTime = now
        newInterval := status.ProbeInterval * 2
        if newInterval > status.MaxProbeInterval {
            newInterval = status.MaxProbeInterval
        }
        status.ProbeInterval = newInterval
        return true
    }
    return false
}

func (m *ModbusPointAdapter) ProbePoint(pointID, deviceID string) bool {
    // 执行Modbus点位探测
    // ...
    return false
}

func (m *ModbusPointAdapter) GetFailureThreshold() int {
    return m.failureThreshold
}
2.13.4.2 BACnet点位适配器
// BACnet点位适配器
type BACnetPointAdapter struct {
    failureThreshold int
}

func NewBACnetPointAdapter() *BACnetPointAdapter {
    return &BACnetPointAdapter{
        failureThreshold: 3,
    }
}

func (b *BACnetPointAdapter) ProtocolType() string {
    return "bacnet"
}

// 其他方法实现...
2.13.4.3 OPC UA点位适配器
// OPC UA点位适配器
type OPCUAPointAdapter struct {
    failureThreshold int
}

func NewOPCUAPointAdapter() *OPCUAPointAdapter {
    return &OPCUAPointAdapter{
        failureThreshold: 3,
    }
}

func (o *OPCUAPointAdapter) ProtocolType() string {
    return "opcua"
}

// 其他方法实现...
2.13.4.4 S7点位适配器
// S7点位适配器
type S7PointAdapter struct {
    failureThreshold int
}

func NewS7PointAdapter() *S7PointAdapter {
    return &S7PointAdapter{
        failureThreshold: 3,
    }
}

func (s *S7PointAdapter) ProtocolType() string {
    return "s7"
}

// 其他方法实现...

2.13.5 与现有系统集成

2.13.5.1 与ChannelManager集成

基于通用点位降级策略的设计,我们优化了接入点,将点位降级逻辑集成到协议驱动的批次构建过程中,并通过抽象层设计确保方案适用于所有协议设备,这样可以更贴近各协议特性,提高降级策略的有效性和通用性。

// 扩展DeviceNodeTemplate结构,添加点位状态管理字段
type DeviceNodeTemplate struct {
    // 现有字段...
    PointFailureMap      map[string]int                    `json:"-"` // 点位失败次数映射
    PointLastSuccessTime map[string]time.Time             `json:"-"` // 点位最后成功时间
    PointDegradedMap     map[string]bool                   `json:"-"` // 点位降级状态映射
    PointLastProbeTime   map[string]time.Time             `json:"-"` // 点位最后探测时间
    PointProbeInterval   map[string]int                    `json:"-"` // 点位当前探测间隔(秒)
    // 其他字段...
}

// 在NewDeviceNode方法中初始化这些字段
func NewDeviceNode() *DeviceNodeTemplate {
    return &DeviceNodeTemplate{
        // 其他初始化...
        PointFailureMap:    make(map[string]int),
        PointLastSuccessTime: make(map[string]time.Time),
        PointDegradedMap:   make(map[string]bool),
        PointLastProbeTime: make(map[string]time.Time),
        PointProbeInterval: make(map[string]int),
        // 其他初始化...
    }
}

// 协议点位适配器接口,为不同协议提供点位降级逻辑
type ProtocolPointAdapter interface {
    // 协议类型
    ProtocolType() string
    // 检查点位是否失败
    IsPointFailure(pointID string, response interface{}, err error) bool
    // 处理点位成功
    HandlePointSuccess(node *DeviceNodeTemplate, pointID string)
    // 处理点位失败
    HandlePointFailure(node *DeviceNodeTemplate, pointID string)
    // 检查点位是否应该被降级
    ShouldDegradePoint(node *DeviceNodeTemplate, pointID string) bool
    // 检查点位是否应该被探测
    ShouldProbePoint(node *DeviceNodeTemplate, pointID string) bool
    // 更新探测间隔
    UpdateProbeInterval(node *DeviceNodeTemplate, pointID string)
    // 构建协议特定的批次
    BuildBatches(points []model.Point) [][]model.Point
}

// 协议适配器注册表
type ProtocolAdapterRegistry struct {
    adapters map[string]ProtocolPointAdapter
}

// 注册协议适配器
func (r *ProtocolAdapterRegistry) RegisterAdapter(adapter ProtocolPointAdapter) {
    r.adapters[adapter.ProtocolType()] = adapter
}

// 获取协议适配器
func (r *ProtocolAdapterRegistry) GetAdapter(protocolType string) ProtocolPointAdapter {
    if adapter, ok := r.adapters[protocolType]; ok {
        return adapter
    }
    // 返回默认适配器
    return &DefaultPointAdapter{}
}

// 默认点位适配器
type DefaultPointAdapter struct {}

func (d *DefaultPointAdapter) ProtocolType() string {
    return "default"
}

func (d *DefaultPointAdapter) IsPointFailure(pointID string, response interface{}, err error) bool {
    return err != nil
}

func (d *DefaultPointAdapter) HandlePointSuccess(node *DeviceNodeTemplate, pointID string) {
    resetPointFailure(node, pointID)
}

func (d *DefaultPointAdapter) HandlePointFailure(node *DeviceNodeTemplate, pointID string) {
    incrementPointFailure(node, pointID)
}

func (d *DefaultPointAdapter) ShouldDegradePoint(node *DeviceNodeTemplate, pointID string) bool {
    return shouldDegradePoint(pointID, node)
}

func (d *DefaultPointAdapter) ShouldProbePoint(node *DeviceNodeTemplate, pointID string) bool {
    return shouldProbePoint(pointID, node)
}

func (d *DefaultPointAdapter) UpdateProbeInterval(node *DeviceNodeTemplate, pointID string) {
    updateProbeInterval(pointID, node)
}

func (d *DefaultPointAdapter) BuildBatches(points []model.Point) [][]model.Point {
    // 默认实现:每个点位一个批次
    var batches [][]model.Point
    for _, point := range points {
        batches = append(batches, []model.Point{point})
    }
    return batches
}

// Modbus点位适配器
type ModbusPointAdapter struct {
    batchSize int
}

func NewModbusPointAdapter() *ModbusPointAdapter {
    return &ModbusPointAdapter{batchSize: 100}
}

func (m *ModbusPointAdapter) ProtocolType() string {
    return "modbus"
}

func (m *ModbusPointAdapter) IsPointFailure(pointID string, response interface{}, err error) bool {
    if err != nil {
        return true
    }
    // Modbus特定的失败检查
    // ...
    return false
}

func (m *ModbusPointAdapter) HandlePointSuccess(node *DeviceNodeTemplate, pointID string) {
    resetPointFailure(node, pointID)
}

func (m *ModbusPointAdapter) HandlePointFailure(node *DeviceNodeTemplate, pointID string) {
    incrementPointFailure(node, pointID)
}

func (m *ModbusPointAdapter) ShouldDegradePoint(node *DeviceNodeTemplate, pointID string) bool {
    return shouldDegradePoint(pointID, node)
}

func (m *ModbusPointAdapter) ShouldProbePoint(node *DeviceNodeTemplate, pointID string) bool {
    return shouldProbePoint(pointID, node)
}

func (m *ModbusPointAdapter) UpdateProbeInterval(node *DeviceNodeTemplate, pointID string) {
    updateProbeInterval(pointID, node)
}

func (m *ModbusPointAdapter) BuildBatches(points []model.Point) [][]model.Point {
    // Modbus特定的批次构建逻辑
    var batches [][]model.Point
    var currentBatch []model.Point
    
    for _, point := range points {
        if len(currentBatch) >= m.batchSize {
            batches = append(batches, currentBatch)
            currentBatch = []model.Point{point}
        } else {
            currentBatch = append(currentBatch, point)
        }
    }
    
    if len(currentBatch) > 0 {
        batches = append(batches, currentBatch)
    }
    
    return batches
}

// 在协议驱动中添加批次构建函数,集成点位降级逻辑
func buildProtocolBatchesWithDegradation(protocolType string, points []model.Point, node *DeviceNodeTemplate, registry *ProtocolAdapterRegistry) [][]model.Point {
    var filtered []model.Point
    adapter := registry.GetAdapter(protocolType)
    
    for _, point := range points {
        // 检查点位是否应该被降级
        if adapter.ShouldDegradePoint(node, point.ID) {
            // 检查是否达到探测间隔
            if adapter.ShouldProbePoint(node, point.ID) {
                // 允许探测
                filtered = append(filtered, point)
                // 更新探测时间和间隔
                adapter.UpdateProbeInterval(node, point.ID)
            } else {
                // 跳过降级点位
                zap.L().Debugf("[点位降级] 跳过点位 %s,失败次数: %d", point.ID, node.PointFailureMap[point.ID])
            }
        } else {
            // 正常点位,加入采集列表
            filtered = append(filtered, point)
        }
    }
    
    // 使用协议适配器构建批次
    return adapter.BuildBatches(filtered)
}

// 检查点位是否应该被降级
func shouldDegradePoint(pointID string, node *DeviceNodeTemplate) bool {
    if node == nil || node.PointFailureMap == nil {
        return false
    }
    
    if failCount, ok := node.PointFailureMap[pointID]; ok && failCount > 3 {
        return true
    }
    return false
}

// 检查点位是否应该被探测
func shouldProbePoint(pointID string, node *DeviceNodeTemplate) bool {
    if node == nil || node.PointLastProbeTime == nil || node.PointProbeInterval == nil {
        return true
    }
    
    lastProbe, ok := node.PointLastProbeTime[pointID]
    if !ok {
        return true
    }
    
    interval, ok := node.PointProbeInterval[pointID]
    if !ok {
        interval = 8 // 默认8秒
    }
    
    return time.Since(lastProbe).Seconds() >= float64(interval)
}

// 更新探测间隔(指数级退避)
func updateProbeInterval(pointID string, node *DeviceNodeTemplate) {
    if node == nil || node.PointProbeInterval == nil || node.PointLastProbeTime == nil {
        return
    }
    
    interval, ok := node.PointProbeInterval[pointID]
    if !ok {
        interval = 8 // 默认8秒
    }
    
    newInterval := interval * 2
    if newInterval > 1024 { // 最大1024秒
        newInterval = 1024
    }
    
    node.PointProbeInterval[pointID] = newInterval
    node.PointLastProbeTime[pointID] = time.Now()
}

// 在ChannelManager的collectDevice中集成点位降级
func (cm *ChannelManager) collectDevice(dev *model.Device, d drv.Driver, ch *model.Channel) {
    start := time.Now()
    defer func() {
        duration := time.Since(start)
        zap.L().Info("Device collection cycle finished",
            zap.String("device", dev.Name),
            zap.Int("point_count", len(dev.Points)),
            zap.String("duration", fmt.Sprintf("%.3fs", duration.Seconds())),
            zap.Int64("rtt_us", duration.Microseconds()),
        )
    }()

    zap.L().Info("PollStart", zap.String("device", dev.Name), zap.Time("ts", time.Now()))

    node := cm.stateManager.GetNode(dev.ID)
    if node == nil {
        zap.L().Warn("Device node not found in state manager", zap.String("device", dev.Name))
        return
    }

    if !cm.stateManager.ShouldCollect(node) {
        zap.L().Debug("Device skipped collection",
            zap.String("device", dev.Name),
            zap.Any("state", node.Runtime.State),
            zap.Time("next_retry", node.Runtime.NextRetryTime))
        return
    }

    // 获取协议类型
    protocolType := "modbus" // 默认协议,实际应从设备配置中获取
    if proto, ok := dev.Config["protocol"]; ok {
        if protoStr, ok := proto.(string); ok {
            protocolType = protoStr
        }
    }

    // 构建带点位降级的采集批次
    // 假设已经初始化了协议适配器注册表
    registry := getProtocolAdapterRegistry()
    batches := buildProtocolBatchesWithDegradation(protocolType, dev.Points, node, registry)
    if len(batches) == 0 {
        zap.L().Warn("No collectible points", zap.String("device", dev.Name))
        return
    }

    // 应用最优超时时间
    timeout := 5 * time.Second
    ctx, cancel := context.WithTimeout(context.Background(), timeout)
    defer cancel()

    // 获取驱动互斥锁
    cm.mu.RLock()
    mu, okMu := cm.driverMus[ch.ID]
    cm.mu.RUnlock()

    if okMu {
        mu.Lock()
        defer mu.Unlock()
    }

    // 设置从机 ID
    if slaveID, ok := dev.Config["slave_id"]; ok {
        if slaveIDUint, ok := slaveID.(float64); ok {
            d.SetSlaveID(uint8(slaveIDUint))
        } else if slaveIDInt, ok := slaveID.(int); ok {
            d.SetSlaveID(uint8(slaveIDInt))
        }
    }

    // 设置设备配置
    config := make(map[string]any)
    for k, v := range dev.Config {
        config[k] = v
    }
    config["_internal_device_id"] = dev.ID

    if err := d.SetDeviceConfig(config); err != nil {
        zap.L().Error("Failed to set device config", zap.String("device", dev.Name), zap.Error(err))
    }

    // 处理每个批次
    successCount := 0
    failCount := 0
    now := time.Now()

    for _, batch := range batches {
        // 确保点位的DeviceID设置正确
        for i := range batch {
            batch[i].DeviceID = dev.ID
        }

        // 读取点位数据
        results, err := d.ReadPoints(ctx, batch)
        
        // 处理读取结果
        adapter := registry.GetAdapter(protocolType)
        for _, result := range results {
            // 发送到管道
            val := model.Value{
                ChannelID: ch.ID,
                DeviceID:  dev.ID,
                PointID:   result.PointID,
                Value:     result.Value,
                Quality:   result.Quality,
                TS:        now,
            }
            // 推入数据管道,驱动存储与WebSocket广播
            cm.pipeline.Push(val)

            // 统计成功/失败并更新点位状态
            if result.Quality == "Good" {
                successCount++
                // 重置失败次数和降级状态
                adapter.HandlePointSuccess(node, result.PointID)
            } else {
                failCount++
                // 增加失败次数,可能触发降级
                adapter.HandlePointFailure(node, result.PointID)
            }
        }

        // 处理未返回结果的点位
        collectedPointIDs := make(map[string]bool)
        for _, result := range results {
            collectedPointIDs[result.PointID] = true
        }

        for _, point := range batch {
            if !collectedPointIDs[point.ID] {
                failCount++
                // 增加失败次数,可能触发降级
                adapter.HandlePointFailure(node, point.ID)
            }
        }
    }

    // 使用 FinalizeCollect 进行状态裁决
    if len(dev.Points) > 0 && successCount+failCount == 0 {
        failCount = len(dev.Points) // 假设所有点位都失败
    }

    // Update Device Metrics
    if mc := model.GetGlobalMetricsCollector(); mc != nil {
        mc.UpdateDeviceMetrics(dev.ID, func(m *model.DeviceMetrics) {
            total := successCount + failCount
            if total > 0 {
                m.PointSuccessRate = float64(successCount) / float64(total)
            } else {
                m.PointSuccessRate = 0
            }
            m.AbnormalPoints = failCount
            if failCount == len(dev.Points) && len(dev.Points) > 0 {
                m.ConsecutiveFailures++
            } else {
                m.ConsecutiveFailures = 0
            }
            m.LastCollectTime = now
            m.State = int(node.Runtime.State)
        })
    }

    collectCtx := &CollectContext{
        TotalCmd:   successCount + failCount,
        SuccessCmd: successCount,
        FailCmd:    failCount,
    }
    cm.stateManager.FinalizeCollect(node, collectCtx)
}

// 重置点位失败状态
func resetPointFailure(node *DeviceNodeTemplate, pointID string) {
    if node == nil {
        return
    }
    
    if node.PointFailureMap != nil {
        node.PointFailureMap[pointID] = 0
    }
    
    if node.PointLastSuccessTime != nil {
        node.PointLastSuccessTime[pointID] = time.Now()
    }
    
    if node.PointDegradedMap != nil {
        node.PointDegradedMap[pointID] = false
    }
    
    if node.PointProbeInterval != nil {
        node.PointProbeInterval[pointID] = 8 // 重置为默认值
    }
}

// 增加点位失败次数
func incrementPointFailure(node *DeviceNodeTemplate, pointID string) {
    if node == nil {
        return
    }
    
    if node.PointFailureMap == nil {
        node.PointFailureMap = make(map[string]int)
    }
    node.PointFailureMap[pointID]++
    
    if node.PointLastSuccessTime != nil {
        // 可以记录失败时间
    }
    
    // 检查是否需要降级
    if node.PointFailureMap[pointID] > 3 {
        if node.PointDegradedMap == nil {
            node.PointDegradedMap = make(map[string]bool)
        }
        node.PointDegradedMap[pointID] = true
        
        // 初始化探测间隔
        if node.PointProbeInterval == nil {
            node.PointProbeInterval = make(map[string]int)
        }
        if _, ok := node.PointProbeInterval[pointID]; !ok {
            node.PointProbeInterval[pointID] = 8
        }
        
        // 初始化探测时间
        if node.PointLastProbeTime == nil {
            node.PointLastProbeTime = make(map[string]time.Time)
        }
        if _, ok := node.PointLastProbeTime[pointID]; !ok {
            node.PointLastProbeTime[pointID] = time.Now()
        }
    }
}

// 全局协议适配器注册表
var globalProtocolAdapterRegistry *ProtocolAdapterRegistry

// 初始化协议适配器注册表
func initProtocolAdapterRegistry() {
    globalProtocolAdapterRegistry = &ProtocolAdapterRegistry{
        adapters: make(map[string]ProtocolPointAdapter),
    }
    
    // 注册默认适配器
    globalProtocolAdapterRegistry.RegisterAdapter(&DefaultPointAdapter{})
    
    // 注册Modbus适配器
    globalProtocolAdapterRegistry.RegisterAdapter(NewModbusPointAdapter())
    
    // 注册BACnet适配器
    globalProtocolAdapterRegistry.RegisterAdapter(NewBACnetPointAdapter())
    
    // 注册OPC UA适配器
    globalProtocolAdapterRegistry.RegisterAdapter(NewOPCUAPointAdapter())
    
    // 注册S7适配器
    globalProtocolAdapterRegistry.RegisterAdapter(NewS7PointAdapter())
}

// 获取协议适配器注册表
func getProtocolAdapterRegistry() *ProtocolAdapterRegistry {
    if globalProtocolAdapterRegistry == nil {
        initProtocolAdapterRegistry()
    }
    return globalProtocolAdapterRegistry
}

// BACnet点位适配器
type BACnetPointAdapter struct {
    batchSize int
}

func NewBACnetPointAdapter() *BACnetPointAdapter {
    return &BACnetPointAdapter{batchSize: 50}
}

func (b *BACnetPointAdapter) ProtocolType() string {
    return "bacnet"
}

func (b *BACnetPointAdapter) IsPointFailure(pointID string, response interface{}, err error) bool {
    if err != nil {
        return true
    }
    // BACnet特定的失败检查
    // ...
    return false
}

func (b *BACnetPointAdapter) HandlePointSuccess(node *DeviceNodeTemplate, pointID string) {
    resetPointFailure(node, pointID)
}

func (b *BACnetPointAdapter) HandlePointFailure(node *DeviceNodeTemplate, pointID string) {
    incrementPointFailure(node, pointID)
}

func (b *BACnetPointAdapter) ShouldDegradePoint(node *DeviceNodeTemplate, pointID string) bool {
    return shouldDegradePoint(pointID, node)
}

func (b *BACnetPointAdapter) ShouldProbePoint(node *DeviceNodeTemplate, pointID string) bool {
    return shouldProbePoint(pointID, node)
}

func (b *BACnetPointAdapter) UpdateProbeInterval(node *DeviceNodeTemplate, pointID string) {
    updateProbeInterval(pointID, node)
}

func (b *BACnetPointAdapter) BuildBatches(points []model.Point) [][]model.Point {
    // BACnet特定的批次构建逻辑
    var batches [][]model.Point
    var currentBatch []model.Point
    
    for _, point := range points {
        if len(currentBatch) >= b.batchSize {
            batches = append(batches, currentBatch)
            currentBatch = []model.Point{point}
        } else {
            currentBatch = append(currentBatch, point)
        }
    }
    
    if len(currentBatch) > 0 {
        batches = append(batches, currentBatch)
    }
    
    return batches
}

// OPC UA点位适配器
type OPCUAPointAdapter struct {
    batchSize int
}

func NewOPCUAPointAdapter() *OPCUAPointAdapter {
    return &OPCUAPointAdapter{batchSize: 200}
}

func (o *OPCUAPointAdapter) ProtocolType() string {
    return "opcua"
}

func (o *OPCUAPointAdapter) IsPointFailure(pointID string, response interface{}, err error) bool {
    if err != nil {
        return true
    }
    // OPC UA特定的失败检查
    // ...
    return false
}

func (o *OPCUAPointAdapter) HandlePointSuccess(node *DeviceNodeTemplate, pointID string) {
    resetPointFailure(node, pointID)
}

func (o *OPCUAPointAdapter) HandlePointFailure(node *DeviceNodeTemplate, pointID string) {
    incrementPointFailure(node, pointID)
}

func (o *OPCUAPointAdapter) ShouldDegradePoint(node *DeviceNodeTemplate, pointID string) bool {
    return shouldDegradePoint(pointID, node)
}

func (o *OPCUAPointAdapter) ShouldProbePoint(node *DeviceNodeTemplate, pointID string) bool {
    return shouldProbePoint(pointID, node)
}

func (o *OPCUAPointAdapter) UpdateProbeInterval(node *DeviceNodeTemplate, pointID string) {
    updateProbeInterval(pointID, node)
}

func (o *OPCUAPointAdapter) BuildBatches(points []model.Point) [][]model.Point {
    // OPC UA特定的批次构建逻辑
    var batches [][]model.Point
    var currentBatch []model.Point
    
    for _, point := range points {
        if len(currentBatch) >= o.batchSize {
            batches = append(batches, currentBatch)
            currentBatch = []model.Point{point}
        } else {
            currentBatch = append(currentBatch, point)
        }
    }
    
    if len(currentBatch) > 0 {
        batches = append(batches, currentBatch)
    }
    
    return batches
}

// S7点位适配器
type S7PointAdapter struct {
    batchSize int
}

func NewS7PointAdapter() *S7PointAdapter {
    return &S7PointAdapter{batchSize: 80}
}

func (s *S7PointAdapter) ProtocolType() string {
    return "s7"
}

func (s *S7PointAdapter) IsPointFailure(pointID string, response interface{}, err error) bool {
    if err != nil {
        return true
    }
    // S7特定的失败检查
    // ...
    return false
}

func (s *S7PointAdapter) HandlePointSuccess(node *DeviceNodeTemplate, pointID string) {
    resetPointFailure(node, pointID)
}

func (s *S7PointAdapter) HandlePointFailure(node *DeviceNodeTemplate, pointID string) {
    incrementPointFailure(node, pointID)
}

func (s *S7PointAdapter) ShouldDegradePoint(node *DeviceNodeTemplate, pointID string) bool {
    return shouldDegradePoint(pointID, node)
}

func (s *S7PointAdapter) ShouldProbePoint(node *DeviceNodeTemplate, pointID string) bool {
    return shouldProbePoint(pointID, node)
}

func (s *S7PointAdapter) UpdateProbeInterval(node *DeviceNodeTemplate, pointID string) {
    updateProbeInterval(pointID, node)
}

func (s *S7PointAdapter) BuildBatches(points []model.Point) [][]model.Point {
    // S7特定的批次构建逻辑
    var batches [][]model.Point
    var currentBatch []model.Point
    
    for _, point := range points {
        if len(currentBatch) >= s.batchSize {
            batches = append(batches, currentBatch)
            currentBatch = []model.Point{point}
        } else {
            currentBatch = append(currentBatch, point)
        }
    }
    
    if len(currentBatch) > 0 {
        batches = append(batches, currentBatch)
    }
    
    return batches
}
2.13.5.2 与设备画像集成

基于Modbus点位降级策略,我们优化了与设备画像的集成,直接从DeviceNodeTemplate中获取点位降级状态,这样可以更高效地更新设备画像。

// 在设备画像更新中集成点位降级状态
func (a *DeviceAdapterManager) UpdateProfile(deviceID string, responseTime time.Duration, err error, successCount, failCount int) {
    // 现有逻辑...
    
    // 集成点位降级状态
    if stateManager != nil {
        // 从设备节点获取点位状态
        node := stateManager.GetNode(deviceID)
        if node != nil {
            degradedPointCount := 0
            totalPointCount := 0
            
            // 统计降级点位数量
            if node.PointDegradedMap != nil {
                for _, degraded := range node.PointDegradedMap {
                    totalPointCount++
                    if degraded {
                        degradedPointCount++
                    }
                }
            }
            
            // 统计失败点位数量
            failurePointCount := 0
            if node.PointFailureMap != nil {
                for _, failCount := range node.PointFailureMap {
                    if failCount > 0 {
                        failurePointCount++
                    }
                }
            }
            
            // 更新设备画像中的点位状态
            if totalPointCount > 0 {
                profile.异常点位数量 = degradedPointCount
                profile.故障点位数量 = failurePointCount
                profile.点位降级率 = float64(degradedPointCount) / float64(totalPointCount)
            }
            
            // 更新设备稳定性评分
            if totalPointCount > 0 {
                stablePointCount := totalPointCount - degradedPointCount - failurePointCount
                profile.稳定性评分 = float64(stablePointCount) / float64(totalPointCount)
            }
        }
    }
    
    // 现有逻辑...
}

// 在设备画像中添加点位降级相关字段
type DeviceProfile struct {
    // 现有字段...
    异常点位数量  int     `json:"abnormal_point_count"`
    故障点位数量  int     `json:"failure_point_count"`
    点位降级率    float64 `json:"point_degradation_rate"`
    稳定性评分    float64 `json:"stability_score"`
    // 其他字段...
}

2.13.6 监控与告警

基于Modbus点位降级策略,我们优化了监控与告警接口,直接从DeviceNodeTemplate中获取点位状态,提供更实时、准确的监控信息。

// 点位状态监控接口
type PointDegradationMonitor interface {
    // 获取点位状态统计
    GetPointStats() map[string]PointStats
    
    // 获取降级点位列表
    GetDegradedPoints() []PointStats
    
    // 获取点位状态历史
    GetPointStatusHistory(pointID, deviceID string, duration time.Duration) []PointStats
    
    // 导出点位状态数据
    ExportPointStatuses() ([]byte, error)
    
    // 获取点位降级建议
    GetDegradationSuggestions() []DegradationSuggestion
    
    // 获取设备点位状态概览
    GetDevicePointStatusOverview(deviceID string) DevicePointStatusOverview
}

// 点位统计信息
type PointStats struct {
    PointID          string    `json:"point_id"`
    DeviceID         string    `json:"device_id"`
    Protocol         string    `json:"protocol"`
    Status           string    `json:"status"` // normal, warning, degraded
    FailureCount     int       `json:"failure_count"`
    LastSuccessTime  time.Time `json:"last_success_time"`
    LastFailureTime  time.Time `json:"last_failure_time"`
    ProbeInterval    int       `json:"probe_interval"`
    LastProbeTime    time.Time `json:"last_probe_time"`
    Degraded         bool      `json:"degraded"`
}

// 设备点位状态概览
type DevicePointStatusOverview struct {
    DeviceID          string    `json:"device_id"`
    TotalPoints       int       `json:"total_points"`
    NormalPoints      int       `json:"normal_points"`
    WarningPoints     int       `json:"warning_points"`
    DegradedPoints    int       `json:"degraded_points"`
    DegradationRate   float64   `json:"degradation_rate"`
    LastUpdated       time.Time `json:"last_updated"`
}

// 降级建议
type DegradationSuggestion struct {
    PointID          string `json:"point_id"`
    DeviceID         string `json:"device_id"`
    Protocol         string `json:"protocol"`
    FailureCount     int    `json:"failure_count"`
    Status           string `json:"status"`
    Suggestion       string `json:"suggestion"`
    Priority         string `json:"priority"` // high, medium, low
    EstimatedTime    string `json:"estimated_time"` // 预计恢复时间
}

// 监控实现
func (m *DeviceStateManager) GetPointStats() map[string]PointStats {
    stats := make(map[string]PointStats)
    
    for deviceID, node := range m.nodes {
        if node.PointFailureMap != nil {
            for pointID, failureCount := range node.PointFailureMap {
                status := "normal"
                degraded := false
                
                if node.PointDegradedMap != nil && node.PointDegradedMap[pointID] {
                    status = "degraded"
                    degraded = true
                } else if failureCount > 0 {
                    status = "warning"
                }
                
                lastSuccessTime := time.Time{}
                if node.PointLastSuccessTime != nil {
                    lastSuccessTime = node.PointLastSuccessTime[pointID]
                }
                
                probeInterval := 8
                lastProbeTime := time.Time{}
                if node.PointProbeInterval != nil {
                    probeInterval = node.PointProbeInterval[pointID]
                }
                if node.PointLastProbeTime != nil {
                    lastProbeTime = node.PointLastProbeTime[pointID]
                }
                
                stats[pointID+"_"+deviceID] = PointStats{
                    PointID:          pointID,
                    DeviceID:         deviceID,
                    Protocol:         node.ProtocolType, // 假设node中有协议类型字段
                    Status:           status,
                    FailureCount:     failureCount,
                    LastSuccessTime:  lastSuccessTime,
                    LastFailureTime:  time.Now(), // 可以从其他地方获取
                    ProbeInterval:    probeInterval,
                    LastProbeTime:    lastProbeTime,
                    Degraded:         degraded,
                }
            }
        }
    }
    
    return stats
}

// 获取降级点位列表
func (m *DeviceStateManager) GetDegradedPoints() []PointStats {
    var degradedPoints []PointStats
    stats := m.GetPointStats()
    
    for _, stat := range stats {
        if stat.Degraded {
            degradedPoints = append(degradedPoints, stat)
        }
    }
    
    return degradedPoints
}

// 获取设备点位状态概览
func (m *DeviceStateManager) GetDevicePointStatusOverview(deviceID string) DevicePointStatusOverview {
    node := m.GetNode(deviceID)
    if node == nil {
        return DevicePointStatusOverview{
            DeviceID: deviceID,
            TotalPoints: 0,
            NormalPoints: 0,
            WarningPoints: 0,
            DegradedPoints: 0,
            DegradationRate: 0,
            LastUpdated: time.Now(),
        }
    }
    
    totalPoints := 0
    normalPoints := 0
    warningPoints := 0
    degradedPoints := 0
    
    if node.PointFailureMap != nil {
        totalPoints = len(node.PointFailureMap)
        
        for pointID, failureCount := range node.PointFailureMap {
            if node.PointDegradedMap != nil && node.PointDegradedMap[pointID] {
                degradedPoints++
            } else if failureCount > 0 {
                warningPoints++
            } else {
                normalPoints++
            }
        }
    }
    
    degradationRate := 0.0
    if totalPoints > 0 {
        degradationRate = float64(degradedPoints) / float64(totalPoints)
    }
    
    return DevicePointStatusOverview{
        DeviceID: deviceID,
        TotalPoints: totalPoints,
        NormalPoints: normalPoints,
        WarningPoints: warningPoints,
        DegradedPoints: degradedPoints,
        DegradationRate: degradationRate,
        LastUpdated: time.Now(),
    }
}

2.13.7 配置参数

基于Modbus点位降级策略,我们优化了配置参数,使其更符合实际的点位状态管理需求,支持从DeviceNodeTemplate中直接管理点位状态。

// 点位降级配置
type PointDegradationConfig struct {
    EnablePointDegradation bool          // 启用电位降级
    FailureThreshold       int           // 失败次数阈值(默认3)
    MaxProbeInterval       int           // 最大探测间隔(秒,默认1024)
    InitialProbeInterval   int           // 初始探测间隔(秒,默认8)
    ProbeMultiplier        int           // 探测间隔乘数(默认2)
    StatusExpiry           time.Duration // 状态过期时间(默认24小时)
    // 协议特定配置
    ProtocolConfigs        map[string]ProtocolDegradationConfig
    // 监控配置
    EnableMonitoring       bool          // 启用监控
    MonitoringInterval     int           // 监控间隔(秒,默认60)
    EnableAlerting         bool          // 启用告警
    AlertThreshold         float64       // 告警阈值(默认0.3,即30%点位降级)
}

// 协议特定降级配置
type ProtocolDegradationConfig struct {
    FailureThreshold       int           // 失败次数阈值
    MaxProbeInterval       int           // 最大探测间隔(秒)
    InitialProbeInterval   int           // 初始探测间隔(秒)
    CustomParameters       map[string]interface{} // 协议特定参数
    // 协议特定的批量处理配置
    BatchSize              int           // 批量处理大小
    BatchTimeout           int           // 批量处理超时(毫秒)
}

// 设备节点配置扩展
type DeviceNodeConfig struct {
    // 现有配置...
    PointDegradation       PointDegradationConfig // 点位降级配置
    // 其他配置...
}

// 配置加载示例
func LoadPointDegradationConfig() PointDegradationConfig {
    return PointDegradationConfig{
        EnablePointDegradation: true,
        FailureThreshold:       3,
        MaxProbeInterval:       1024,
        InitialProbeInterval:   8,
        ProbeMultiplier:        2,
        StatusExpiry:           24 * time.Hour,
        EnableMonitoring:       true,
        MonitoringInterval:     60,
        EnableAlerting:         true,
        AlertThreshold:         0.3,
        ProtocolConfigs: map[string]ProtocolDegradationConfig{
            "modbus": {
                FailureThreshold:     3,
                MaxProbeInterval:     1024,
                InitialProbeInterval: 8,
                BatchSize:            100,
                BatchTimeout:         5000,
            },
            "bacnet": {
                FailureThreshold:     3,
                MaxProbeInterval:     1024,
                InitialProbeInterval: 8,
                BatchSize:            50,
                BatchTimeout:         3000,
            },
            "opcua": {
                FailureThreshold:     3,
                MaxProbeInterval:     1024,
                InitialProbeInterval: 8,
                BatchSize:            200,
                BatchTimeout:         10000,
            },
            "s7": {
                FailureThreshold:     3,
                MaxProbeInterval:     1024,
                InitialProbeInterval: 8,
                BatchSize:            80,
                BatchTimeout:         4000,
            },
        },
    }
}
// 设备适配器配置
type DeviceAdapterConfig struct {
	EnableAdaptiveCollection bool          // 启用自适应采集
	MinResponseTime         time.Duration // 最小响应时间
	MaxResponseTime         time.Duration // 最大响应时间
	StabilityThreshold      float64       // 稳定性阈值
	BatchOptimization       bool          // 启用批量优化
	ConcurrencyLimit        int           // 并发限制
	ProfileExpiry           time.Duration // 画像过期时间
	// RTT相关配置
	RTTSampleWindow         int           // RTT采样窗口大小
	RTTAlphaGain            float64       // EWMA增益系数
	RTTThresholdHigh        int64         // 高延迟阈值
	RTTThresholdLow         int64         // 低延迟阈值
	// MTU相关配置
	MTUMax                  int           // 最大MTU
	MTUMin                  int           // 最小MTU
	MTUStep                 int           // MTU探测步长
	MTUHysteresis           float64       // MTU抖动滞后因子
	// Gap相关配置
	GapMaxHole              int           // 最大允许地址空洞
	GapFillStrategy         int           // 空洞填充策略
	GapDynamicEnable        bool          // 启用基于RTT的动态Gap调整
	// 心跳相关配置
	HeartbeatInterval       int           // 心跳间隔
	HeartbeatSuppressEnable bool          // 启用心跳抑制
}

2.12.2 监控接口

// 监控接口
type DeviceAdapterMonitor interface {
	// 获取设备状态统计
	GetDeviceStats() map[string]DeviceStats
	
	// 获取协议性能统计
	GetProtocolStats() map[string]ProtocolStats
	
	// 获取异常设备列表
	GetUnstableDevices() []string
	
	// 导出设备画像数据
	ExportProfiles() ([]byte, error)
	
	// 获取采集优化建议
	GetOptimizationSuggestions() []OptimizationSuggestion
	
	// 获取RTT统计
	GetRTTStats() map[string]RTTStats
	
	// 获取MTU统计
	GetMTUStats() map[string]MTUStats
	
	// 获取Gap统计
	GetGapStats() map[string]GapStats
}

// 设备统计信息
type DeviceStats struct {
	DeviceID        string    `json:"device_id"`
	Name            string    `json:"name"`
	Protocol        string    `json:"protocol"`
	Status          string    `json:"status"`
	StabilityScore  float64   `json:"stability_score"`
	ResponseTime    float64   `json:"response_time"` // 毫秒
	ErrorRate       float64   `json:"error_rate"`
	采集成功率       float64   `json:"success_rate"`
	连续失败次数     int       `json:"consecutive_failures"`
	LastUpdated     time.Time `json:"last_updated"`
	// RTT相关
	EWMARTT         int64     `json:"ewma_rtt"` // 微秒
	// MTU相关
	CurrentMTU      int       `json:"current_mtu"`
	// Gap相关
	CurrentGap      int       `json:"current_gap"`
}

// 协议统计信息
type ProtocolStats struct {
	Protocol        string    `json:"protocol"`
	DeviceCount     int       `json:"device_count"`
	OnlineCount     int       `json:"online_count"`
	AvgResponseTime float64   `json:"avg_response_time"`
	AvgErrorRate    float64   `json:"avg_error_rate"`
	LastUpdated     time.Time `json:"last_updated"`
	// RTT相关
	AvgEWMARTT      int64     `json:"avg_ewma_rtt"`
	// MTU相关
	AvgMTU          int       `json:"avg_mtu"`
	// Gap相关
	AvgGap          int       `json:"avg_gap"`
}

// RTT统计信息
type RTTStats struct {
	DeviceID        string    `json:"device_id"`
	EWMARTT         int64     `json:"ewma_rtt"`
	MinRTT          int64     `json:"min_rtt"`
	MaxRTT          int64     `json:"max_rtt"`
	AvgRTT          int64     `json:"avg_rtt"`
	LastUpdated     time.Time `json:"last_updated"`
}

// MTU统计信息
type MTUStats struct {
	DeviceID        string    `json:"device_id"`
	CurrentMTU      int       `json:"current_mtu"`
	MaxMTU          int       `json:"max_mtu"`
	MinMTU          int       `json:"min_mtu"`
	LastNegotiation time.Time `json:"last_negotiation"`
}

// Gap统计信息
type GapStats struct {
	DeviceID        string    `json:"device_id"`
	CurrentGap      int       `json:"current_gap"`
	MaxGap          int       `json:"max_gap"`
	MergedRequests  uint64    `json:"merged_requests"`
	SavedRequests   uint64    `json:"saved_requests"`
	FillEfficiency  float64   `json:"fill_efficiency"`
	LastUpdated     time.Time `json:"last_updated"`
}

// 优化建议
type OptimizationSuggestion struct {
	DeviceID    string `json:"device_id"`
	Suggestion  string `json:"suggestion"`
	Priority    string `json:"priority"` // high, medium, low
	ExpectedImprovement float64 `json:"expected_improvement"`
}

3. 前端(嵌入式/设备侧)修改要点

为了配合网关的高效采集,下位机/传感器端需配合优化:

3.1 精准时序控制

  • 中断优先级: 将通信接口 (UART/ETH/CAN) 中断优先级提至最高,确保 RTT 时间戳精度 < 1 ms。
  • 硬件捕获: 使用 MCU 硬件定时器 Capture 通道捕获帧首/帧尾时间,消除软件抖动。

3.2 协议栈增强

  • 心跳响应: 实现对网关 HeartbeatInterval 探测包的快速响应 (Echo)。
  • 大包支持: 确保存储区支持连续读写,配合网关的 Gap 合并策略。

3.3 内存管理

  • 零拷贝: 采集数据队列使用 DMA + 环形缓冲,避免数据搬运。
  • 动态 Buffer: 根据协商后的 MTU 动态分配/复用接收缓冲区,避免内存浪费。

4. 后端(Go 网关侧)修改要点

4.1 协议栈扩展

  • 控制特征值: 增加 COLLECT_CTRL 指令处理,支持通过 API 下发 RTT 采样周期与 MTU 策略。
  • 参数持久化: 确保 HeartbeatInterval, BufferSize, Qos 等参数正确保存至 selfpara/*.json

4.2 流量优化

  • 心跳抑制: 在调度器中实现 ShouldSendHeartbeat(interval) 逻辑,若 time.Since(LastActivity) < interval 则跳过心跳。
  • ACK 压缩: 对高频 Qos=1 的采集包,实现 Bitmap ACK 或累积 ACK,减少反向确认包数量。
  • 动态 Gap: 在协议驱动中,根据当前 MTU 与 RTT 动态计算最佳 Gap 值,平衡总线占用与响应速度。

4.3 运维支持

  • 热更新: 提供 HTTP 接口 POST /api/v2/interface/communication/update_params 支持参数 A/B 灰度测试。
  • 增强日志: 日志中增加 seq_no, rtt_us, mtu_size, gap_val 字段,便于大数据分析。

5. 关键步骤与里程碑

阶段 任务 验收标准
Step-0 基准测试 输出当前延迟、丢包、功耗基线报告;确认现有协议配置加载正常。
Step-1 高精打戳 驱动层实现纳秒/微秒级打戳;验证精度 < 1 ms。
Step-2 RTT 算法 实现 RTT 采样窗口与 EWMA 算法;验证在丢包场景下的曲线平滑度。
Step-3 MTU 状态机 实现二分协商状态机;单元测试覆盖率 100%。
Step-4 Gap 联动 实现 MTU -> Gap Max 的联动,以及 RTT -> Gap Current 的动态调整。
Step-5 前后端联调 对接 API,前端 UI 实时展示 RTT 曲线、当前 MTU 及 Gap 合并效率。
Step-6 压力测试 100k 采样点/秒持续 2h;延迟 99th < 10ms;内存无泄漏。

6. 实施计划

6.1 阶段一:核心模块开发

  1. 实现设备适配器核心接口
    • 定义DeviceCommunicationProfile结构,包含RTT、MTU、Gap等字段
    • 实现DeviceAdapter接口,添加RTT、MTU、Gap相关方法
    • 实现ProtocolAdapter接口,添加协议特定的MTU和Gap计算
  2. 开发基础协议适配器
    • Modbus协议适配器,实现RTT自适应和MTU协商
    • BACnet协议适配器,实现COV订阅优化
    • OPC UA协议适配器,实现订阅优化和批处理
    • S7协议适配器,实现数据块优化和MTU协商
  3. 实现设备画像管理
    • DeviceAdapterManager实现,集成RTT、MTU、Gap管理器
    • 画像持久化存储
    • 画像过期机制
  4. 实现RTT、MTU、Gap管理器
    • RTTManager:实现RTT统计和EWMA计算
    • MTUManager:实现MTU动态协商
    • GapOptimizer:实现基于RTT和MTU的Gap优化
  5. 实现影子设备核心模块
    • Real Shadow Store:纯内存运行时快照
    • Virtual Shadow Engine:跨设备公式依赖计算
    • Edge Compute Engine:差值/步长/滑动平均/阈值计算
    • Atomic Dispatcher:版本推进 + QoS ACK 机制
    • Shadow Ingress:批量写入 + QoS 0/1/2 + 去重
  6. 实现通用点位降级框架
    • 扩展DeviceNodeTemplate结构,添加点位状态管理字段
    • 实现批次构建函数,集成点位降级逻辑
    • 开发指数级退避探测机制
    • 与ChannelManager的collectDevice方法集成
    • 与设备画像系统集成,同步点位降级状态
    • 实现监控与告警接口
  7. 实现边缘运维与Gossip同步
    • Gossip通讯模块:节点发现、成员心跳、增量反熵同步
    • bbolt持久化层:配置存储、离线队列、冲突日志
    • 冲突解决引擎:时间戳最新优先策略
    • UUID管理工具:生成、校验、手工绑定、替换演练

6.2 阶段二:集成与测试

  1. 与现有采集模块集成
    • 修改ChannelManager集成智能适配逻辑
    • 驱动层参数优化,支持RTT、MTU、Gap参数
    • 状态管理集成
    • 心跳机制集成,实现智能业务心跳
    • 集成点位降级框架,实现点位过滤和状态管理
  2. 进行多协议测试
    • Modbus设备测试,验证RTT自适应和MTU协商
    • BACnet设备测试,验证COV订阅优化
    • OPC UA设备测试,验证订阅优化和批处理
    • 混合协议测试,验证并发采集优化
    • 多协议点位降级测试,验证不同协议的点位降级效果
  3. 优化并发采集性能
    • 并发采集测试,验证采集顺序优化
    • 批量采集测试,验证Gap合并策略
    • 点位降级测试,验证降级和恢复机制
    • 性能瓶颈分析,优化系统资源利用

6.3 阶段三:功能扩展

  1. 添加批量采集优化
    • 同一总线设备批量采集
    • 协议特定批量优化
    • 批量采集效果评估
  2. 实现监控和统计功能
    • 设备画像监控面板,展示RTT、MTU、Gap等指标
    • 点位状态监控面板,展示点位降级状态和历史
    • 协议性能统计
    • 优化建议生成
    • 实时监控API
  3. 支持更多协议类型
    • 串口协议适配器
    • 以太网IP协议适配器
    • 三菱PLC协议适配器
    • 欧姆龙PLC协议适配器
    • 为新协议添加点位降级适配器
  4. 运维支持
    • 热更新:提供HTTP接口支持参数A/B灰度测试
    • 增强日志:日志中增加RTT、MTU、Gap、点位降级状态等字段
    • 故障诊断:基于设备画像和点位降级状态的故障分析
    • 点位降级配置管理:通过API调整点位降级参数

7. 预期效果

  1. 采集效率提升:通过智能适配、RTT优化、批量采集和点位降级,采集效率提升40%以上
  2. 系统稳定性提高:通过设备画像、状态预测和点位降级,系统稳定性提高30%
  3. 延迟优化:99th百分位延迟控制在10ms以内
  4. 吞吐量提升:通过MTU动态协商和Gap合并,吞吐量提升50%以上
  5. 资源利用率优化:合理的并发策略、批量采集和点位降级,减少系统资源占用
  6. 运维成本降低:自动化的设备管理、优化和点位降级,减少人工干预
  7. 可扩展性增强:模块化设计,易于添加新协议支持
  8. 点位故障隔离:通过点位降级,避免故障点位影响整体采集性能
  9. 故障恢复自动化:通过指数级退避探测,实现故障点位的自动恢复

8. 技术创新点

  1. 统一设备画像:为不同协议设备建立统一的画像结构,实现跨协议的设备管理
  2. 智能参数调整:基于设备历史数据自动调整采集参数,适应设备特性
  3. RTT自适应:基于往返时间动态调整采集参数,优化延迟和吞吐量
  4. MTU动态协商:智能调整最大传输单元,提高数据传输效率
  5. Gap合并优化:基于RTT和MTU动态调整Gap值,提高批量读取效率
  6. 并发优化策略:基于设备稳定性和响应时间的智能调度算法
  7. 智能业务心跳:根据业务通信自动调整心跳频率,减少网络流量
  8. 实时监控与分析:提供设备性能监控和优化建议
  9. 通用点位降级框架:为所有协议提供统一的点位降级机制,实现故障隔离
  10. 指数级退避探测:对降级点位采用智能探测机制,实现自动恢复
  11. 协议无关的点位管理:通过适配器模式,支持不同协议的点位状态管理

9. 总结

设备级别智能适配公共模块的设计,为多协议环境下的设备管理和采集优化提供了统一的解决方案。通过设备画像、智能调度、RTT自适应、MTU动态协商、批量优化和点位降级等技术,可以显著提高采集效率和系统稳定性,同时为未来的功能扩展和协议支持奠定基础。

该方案充分考虑了当前项目的架构特点,与现有系统无缝集成,同时引入了先进的智能适配机制,为边缘计算南向采集通道的性能优化提供了有力支持。

10. 测试验证方法与验收标准

10.1 测试验证方法

10.1.1 单元测试

  1. 点位状态管理测试
    • 测试点位状态的初始化和更新
    • 测试点位失败次数的统计
    • 测试点位降级状态的切换
    • 测试指数级退避探测机制
  2. 协议适配器测试
    • 测试Modbus点位适配器的实现
    • 测试BACnet点位适配器的实现
    • 测试OPC UA点位适配器的实现
    • 测试S7点位适配器的实现
  3. 点位降级管理器测试
    • 测试点位过滤逻辑
    • 测试批量点位处理
    • 测试与设备画像的集成
    • 测试并发访问安全性

10.1.2 集成测试

  1. 与ChannelManager集成测试
    • 测试点位降级框架与ChannelManager的集成
    • 测试采集流程中的点位过滤
    • 测试点位状态更新的准确性
  2. 多协议集成测试
    • 测试不同协议下的点位降级效果
    • 测试混合协议场景下的点位管理
    • 测试协议切换时的点位状态处理
  3. 性能测试
    • 测试点位降级对采集性能的影响
    • 测试大量点位场景下的性能表现
    • 测试故障点位对系统的影响
  4. 故障注入测试
    • 模拟点位故障场景
    • 验证点位降级机制的有效性
    • 验证故障恢复机制的可靠性

10.1.3 端到端测试

  1. 完整采集流程测试
    • 测试从设备配置到数据采集的完整流程
    • 验证点位降级在实际场景中的效果
    • 测试系统稳定性和可靠性
  2. 故障恢复测试
    • 模拟设备故障和恢复
    • 验证点位自动恢复机制
    • 测试系统的容错能力
  3. 边界场景测试
    • 测试网络异常场景
    • 测试设备离线场景
    • 测试大量点位同时失败场景

10.2 验收标准

10.2.1 功能验收标准

  1. 点位降级功能
    • 连续失败3次的点位应被降级
    • 降级点位应采用指数级间隔探测
    • 成功采集一次后应自动恢复
    • 不同协议的点位应正确降级
  2. 性能验收标准
    • 点位降级应减少30%以上的无效通信
    • 系统整体采集效率应提升20%以上
    • 99th百分位延迟应控制在10ms以内
    • 支持至少1000个点位的同时管理
  3. 稳定性验收标准
    • 系统应能稳定运行72小时以上
    • 故障点位不应影响其他点位的采集
    • 系统应能自动从故障中恢复
    • 内存使用应保持稳定,无泄漏
  4. 可扩展性验收标准
    • 应支持轻松添加新协议的点位适配器
    • 配置参数应可通过API动态调整
    • 应通过现有 diagnostics API 与结构化日志提供可观测性
    • 应提供详细的日志和监控信息

10.2.2 测试通过标准

  1. 单元测试覆盖率
    • 核心代码覆盖率应达到80%以上
    • 关键路径覆盖率应达到100%
  2. 集成测试通过标准
    • 所有集成测试用例应通过
    • 无严重或中等级别缺陷
  3. 性能测试通过标准
    • 性能指标应达到预期目标
    • 系统应能处理预期的负载
  4. 端到端测试通过标准
    • 完整流程测试应通过
    • 故障恢复测试应通过
    • 边界场景测试应通过

11. 交付物

  • 设计文档: docs/[TODO]边缘计算南向采集优化方案2026第二季度.md (本文档)
  • 架构总览: docs/edge/边缘网关架构设计总览.md
  • ScanEngine 测试报告: docs/TODO/ScanEngine重构测试报告.md
  • 接口代码(已实现):
    • internal/core/device_adapter.go
    • internal/core/rtt_manager.go, mtu_manager.go, gap_optimizer.go
    • internal/core/shadow_core.go, shadow_ingress.go, shadow_device_optimizer.go
    • internal/core/scan_engine.go, scan_engine_compat.go, execution_layer.go
    • internal/core/virtual_shadow_engine.go
    • internal/driver/modbus/modbus_executor.go
  • 接口代码(Q3 计划,原 §11 未交付):
    • internal/core/point_degradation_manager.go
    • internal/core/gossip_sync.go
    • internal/core/protocol_point_adapters.go
  • 单元测试(已实现):
    • internal/core/device_adapter_test.go
    • internal/core/shadow_core_test.go, shadow_ingress_test.go
    • internal/core/rtt_manager_test.go, mtu_manager_test.go, gap_optimizer_test.go
    • internal/core/scan_engine_test.go, scan_engine_large_scale_test.go
  • 配置示例(未单独拆分 JSON,运行时写入 config.db)

12. Q2 实现审查(截至 2026-06-25)

对照 §5 里程碑、§6 实施计划与代码库实际状态。状态:✅ 已完成 🟡 部分完成 ❌ 未实现 🗑️ 已替代

12.1 里程碑 Step-0 ~ Step-6 对照

阶段 计划内容 状态 实际交付 / 差距
Step-0 基准测试、协议配置加载 🟡 ScanEngine 压力测试报告(100 设备混合协议);无正式延迟/功耗基线报告
Step-1 驱动纳秒/微秒打戳 🟡 OPC UA stampCollectionTime 使用本地采集时间;无硬件级 Capture
Step-2 RTT 采样 + EWMA rtt_manager.go + 单元测试;ScanEngine 主路径未调用 UpdateRTT
Step-3 MTU 二分协商状态机 🟡 mtu_manager.go 有 NegotiateMTU;非完整 FSM,未接入驱动读路径
Step-4 MTU→Gap 联动 🟡 gap_optimizer.go + ShadowDeviceOptimizer 联动;ExecutionLayer 读点前未消费 Gap
Step-5 前后端联调 RTT/MTU/Gap UI 画像字段写入 Shadow CommunicationProfile;UI 无 RTT 曲线/MTU/Gap 面板
Step-6 100k 点/秒 · 2h · P99<10ms 大规模测试为 mock 驱动、短时;未达工业压测指标

12.2 §6.1 阶段一:核心模块 — 实现矩阵

模块 计划 状态 说明
DeviceCommunicationProfile 统一画像结构 model/types.go + Shadow 通信画像
DeviceAdapter / ProtocolAdapter 接口 + 实现 🟡 device_adapter.go 存在;ProtocolAdapterRegistry 未全面接入驱动
DeviceAdapterManager 画像管理 🟡 已创建;画像未持久化到 DB,无过期机制
RTTManager EWMA 实现 + 测试
MTUManager 动态协商 🟡 简化算法;未驱动 Modbus PDU 分片
GapOptimizer 地址合并 🟡 算法存在;ModbusExecutor 未读 GapOptimizer 输出
ShadowCore 纯内存 shadow_core.go;Subscribe → WebSocket
ShadowIngress QoS 批量写入 🟡 代码 + 测试;main.go 未挂载,ScanEngine 直写 Shadow
VirtualShadowEngine 公式依赖 🟡 virtual_shadow_engine.go;Subscribe 已接 Shadow
EdgeComputeManager 阈值/窗口/表达式 规则 CRUD + pipeline 触发
Atomic Dispatcher / QoS ACK CAS + ACK 🟡 CompareAndSwap 存在;QoS ACK 链未产品化
点位降级框架 point_degradation_manager 文件未创建
Gossip / bbolt 联机 gossip_sync.go 未实现;SyncManager 在 main 中禁用
CollectionScheduler 画像驱动调度 🗑️ 已由 ScanEngine 替代collection_scheduler.go 已删)
CommDriver HAL 统一物理层 未实现
零拷贝 Ring Buffer 缓存层 未实现
Traffic-Aware Heartbeat 智能心跳 未实现

12.3 §6.2 阶段二:集成 — 实现矩阵

集成项 状态 说明
ChannelManager + 智能适配 ScanEngineAdapter 注册任务;deviceLoop / collectDevice 已移除
驱动层 RTT/MTU/Gap 参数 🟡 ModbusExecutor + ConnectionController 新增;Gap 未闭环
状态管理 FinalizeCollect ScanEngine 经 finalizeScanCollectFinalizeCollect 回写设备状态
心跳机制 未实现
点位降级集成 未实现
多协议实测报告 🟡 OPC UA 点位时间戳问题已修;缺 Modbus/BACnet 实测文档

12.4 §6.3 阶段三:扩展 — 实现矩阵

扩展项 状态 说明
同总线批量采集 🟡 ModbusExecutor PointScheduler;未与 GapOptimizer 联动
监控面板 RTT/MTU/Gap 南向指标卡片有部分 metrics;无画像专项 UI
DLT645 / Omron / Mitsubishi 驱动已注册
EtherNet/IP 驱动 + Limited 执行模式
热更新 API /api/v2/... 配置热更新走标准 CRUD
增强日志 seq/rtt/mtu/gap 结构化字段未统一
优化建议 API GetOptimizationSuggestions 未暴露 HTTP

12.5 架构级变更(Q2 外溢,2026-06 已完成)

以下不在原 V2 正文但已落地,影响 Q3 规划基线:

变更 文件 意义
ScanEngine 统一调度 scan_engine.go + compat 替代 per-device goroutine loop
ExecutionLayer 三模式 serial / parallel / limited 对标 Kepware 通道隔离
防饿死 + 背压 + 资源上限 anti-starvation 300s, backpressure 512 工业调度骨架
DB 单源配置 config.db + runtime.db + ConfigStore 安装向导 + 配置 DB 强一致写入
ShadowCore 接入 ScanEngine main.go SetShadowCore 采集 → 影子 → WebSocket
UI 读影子 WS Subscribe + GetDevicePoints 优先影子 collected_at / updated_at 双时间戳
安装模式 Install.vue + install_handler 无 DB 首启

12.6 当前运行时数据流(实际)

ScanEngine → Driver.ReadPoints → ShadowCore.WriteShadowDevice → WebSocket
                                      ↓ (断层)
                              DataPipeline ✗
                                      ↓
                         EdgeCompute / Northbound / values 历史

核心断层:周期采集未进入 DataPipeline,边缘规则与北向遥测与 UI 影子 不同源

12.7 Q2 目标达成度评估

Q2 预期效果 达成度 说明
采集效率 +40% 🟡 未量化 调度统一有收益;块读/Gap 未闭环
稳定性 +30% 🟡 ScanEngine 防饿死有效;点位降级缺失
P99 延迟 <10ms 无生产压测证据
吞吐 +50% MTU/Gap 未驱动 I/O
点位故障隔离 降级框架未做
72h 稳定运行 未执行

Q2 结论调度层(ScanEngine)+ 影子层(ShadowCore)+ 画像算法模块(RTT/MTU/Gap) 主体代码已交付,但 集成闭环不足,距离方案 V2 描述的端到端智能采集仍有明显差距。Q3 重点应从「模块存在」转向「Kepware 式产品能力闭环」。


13. 2026 Q3 规划 — 对标 Kepware 路线

完整 Q3 方案已独立成文边缘计算南向采集优化方案2026第三季度.md
以下 §13–§14 为 Q2 文档内保留摘要;实施、验收、进度跟踪以 Q3 文档为准。

参考 KEPServerEX 核心概念:Tag Database · Scan Mode/Class · Block I/O · Publish Rate · Store & Forward · Diagnostics
详细生命周期见:docs/edge/边缘网关架构设计总览.md

13.1 Kepware 概念 → EdgeX 映射

Kepware 概念 含义 EdgeX Q3 目标模块
Tag Database 统一 Tag 命名、属性、缩放、EU TagRegistry(DB 扩展或 Devices 之上抽象层)
Scan Mode 同步读 / 订阅 / 只读一次 驱动 Capability + ScanEngine 任务类型
Scan Class 100ms / 1s / 10s 分组调度 ScanEngine 多 Interval 或点位分组任务
Block I/O 寄存器块读、减少报文 Modbus GapOptimizer → ExecutionLayer 分片
Publish Rate 采集速率 ≠ 上报速率 Shadow 全采 + Pipeline 限频推送北向
Store & Forward 断网缓存补发 统一 OfflineQueue(南向 values + 北向 cache)
Diagnostics 通道/设备/Tag 级延迟与错误 GET /diagnostics/scan-engine/api/diagnostics/*
Media Level Redundancy 主备通道 Phase Q3-B(设计预留,不强制实现)

13.2 Q3 总目标

  1. 统一数据面:Shadow 为 SoT,Pipeline 为扇出,北向/边缘/历史与 UI 一致。
  2. 可预测调度:Scan Class + 调度 SLA 可观测,工业模式可关闭失败退避。
  3. 高吞吐 I/O:Modbus/OPC UA 块读闭环,万 Tag 压测出基线报告。
  4. 点位级韧性:Tag 降级与探测恢复(补 Q2 缺失)。
  5. 可运维:Diagnostics 对标 Kepware Event Log / Statistics。

13.3 Q3 分期计划

Q3-A(7 月):统一数据面 + 调度可观测 — P0

编号 任务 交付 验收
A1 Shadow → Pipeline 桥接 ShadowCore.Subscribe 内 Push model.Value 周期采集触发 ECM + NBM + values
A2 ScanEngine 状态回写 executeTaskAsyncFinalizeCollect 设备在线率与采集成功率一致
A3 删除/归档 collectDevice 清理死代码 无重复采集路径
A4 ShadowIngress 评估 决定保留直连或统一 Ingress 文档化单一写入规范
A5 调度指标 scan_lag_ms / starvation_rescue_total / task_overdue GET /diagnostics/scan-enginesla_warnings[]
A6 集成测试 shadow_pipeline_integration_test.go 四路(WS/规则/北向/DB)时间戳一致

Kepware 对齐:Publish Rate 分离(A1 北向可独立限频);Diagnostics 基础(A5)。

Q3-B(8 月):Scan Class + 块读闭环 — P0

编号 任务 交付 验收
B1 Scan Class 模型 设备/点位 scan_class: fast\|normal\|slow 同设备多周期并存
B2 工业调度模式 degrade_on_failure: false 失败仅 Bad,不拉长 Interval
B3 Event-driven 调度 堆顶 timer + 10ms 兜底 CPU 占用下降(对比报告)
B4 Gap → Modbus 读路径 ExecutionLayer 读前分片 连续寄存器合并读,请求数下降 ≥30%
B5 MTU → 分片上限 ModbusExecutor 读 PDU 用 CurrentMTU 单帧不超协商值
B6 ScanEngine → RTT 每次 Execute 后 UpdateDeviceRTT 画像 EWMARTT 随采集更新
B7 OPC UA 分批 Read 超 N 点拆分 + MaxAge 500+ 点设备稳定

Kepware 对齐:Scan Class + Block I/O。

Q3-C(9 月):Tag 韧性 + 压测 + Diagnostics — P1

编号 任务 交付 验收
C1 点位降级框架 point_degradation_manager.go 连续 3 次失败降级,指数探测恢复
C2 Tag 级 Quality ShadowPoint 保留 Bad + 降级标记 UI 显示降级状态
C3 万 Tag 压测 报告:吞吐、P50/P99 调度延迟、内存 1w Tag @1s,2h 无泄漏
C4 Diagnostics API 通道/设备:RTT、MTU、Gap、成功率 对标 Kepware Statistics 页
C5 UI 画像面板 DeviceMetricsCard 扩展 RTT/Gap Step-5 补完
C6 Store & Forward 统一 北向断线 + 南向 history 策略文档化并实现 恢复后补发

Kepware 对齐:Tag 级 Diagnostics + Store & Forward。

13.4 Q3 明确不做 / 推迟(Q4+)

原因
Gossip 多节点联机 Sync 曾致路径污染,需独立 redesign
CommDriver HAL / Ring Buffer 收益低于 ScanEngine + 驱动直连
通道级冗余 Failover 依赖联机与驱动抽象成熟
完整 Atomic QoS2 先保证 QoS0 扇出一致
100k 点/秒 Q3 目标 1w Tag 基线,10w 为 Q4

13.5 Q3 架构目标态

┌──────────────── Tag Database (scan_class, EU, alias) ────────────────┐
└───────────────────────────────┬────────────────────────────────────┘
                                │
                    ┌───────────▼───────────┐
                    │      ScanEngine        │
                    │ ScanClass · Priority   │
                    │ Anti-starvation · Metrics│
                    └───────────┬───────────┘
                                │ Block I/O (Gap/MTU)
                    ┌───────────▼───────────┐
                    │   ShadowCore (SoT)     │
                    │ collected_at/updated_at│
                    └───────────┬───────────┘
                                │ Subscribe 扇出
         ┌──────────────────────┼──────────────────────┐
         │                      │                      │
   WebSocket              DataPipeline           VirtualShadow
                              │
                    ┌─────────┴─────────┐
                    │ ECM · NBM · Hist  │
                    │ (Publish Rate 限频) │
                    └───────────────────┘

13.6 Q3 验收标准(对标 Kepware 工业可用)

指标 Q3 目标
数据一致性 同一 Tag 在 UI / MQTT / 规则 / values 四路值与 Quality 一致
调度 SLA 1s Scan Class:95% 采集落在 [1s, 1s+100ms]
防饿死 任意设备 5min 内至少被 rescue 或成功采集一次
Modbus 块读 100 点离散地址 → 请求数减少 ≥30%
点位降级 故障 Tag 不拖死同设备其他 Tag
压测 1w Tag · 2h · 内存漂移 <5% · 无 panic
可观测 每设备可查 EWMARTT、CurrentMTU、CurrentGap、成功率

13.7 Q3 交付物清单

交付物 路径 / 说明
统一数据面桥接 shadow_core.goshadow_pipeline_bridge.go
Scan Class 配置 model.Device / model.Point 字段 + 迁移
点位降级 internal/core/point_degradation_manager.go
压测报告 docs/testing/Q3_10k_tag_benchmark_2026Q3.md
Diagnostics API internal/server/diagnostics_handler.go
架构总览更新 docs/edge/边缘网关架构设计总览.md §7 Roadmap
Q3 审查报告 本文 §14(Q3 末填写)

13.8 风险与依赖

风险 缓解
Shadow→Pipeline 双写性能 批量 Push;Pipeline 合并同设备消息
Scan Class 任务数膨胀 按 Class 聚合为每设备每 Class 一任务,非每 Tag 一任务
Modbus 双驱动注册 统一 ModbusExecutor,废弃 modbus.go 重复 init
边缘规则延迟 ECM 索引已 O(1);扇通后需测规则触发延迟 <100ms
Q2 文档交付物路径过时 §11 参考实现中 gossip/point_degradation 改为 Q3 计划项

14. Q3 末审查占位

计划填写时间:2026-09-30
填写内容:Q3-A/B/C 完成率、压测数据、与 Kepware 对标差距、Q4 规划入口。

计划 实际 备注
A1 Shadow→Pipeline      
B4 Gap 闭环      
C3 万 Tag 压测      
C1 点位降级      

文档版本:V2.1 | 2026-06-25 增补 §12 Q2 审查 + §13 2026Q3 Kepware 路线规划 - edgex\config\gossip_sync_config.json - edgex\config\point_degradation_config.json