DL/T 645-2007 多功能电能表通信协议驱动开发方案

1. 概述

1.1 协议简介

DL/T 645-2007 是多功能电能表通信协议标准,规定了电能表和上位机(如数据采集系统)之间的通信协议,包括数据格式、通信过程、错误检测等内容。该协议主要用于电力系统中的电能表数据采集,支持单相和三相电能表。

DL/T 645-2007 支持多种通信方式:

  • 有线通信: RS-485、RS-232
  • 无线通信: GSM/GPRS、LoRa
  • 以太网通信: TCP/IP

1.2 功能定位

功能类别 功能描述 支持范围
数据采集 电能量采集(有功/无功电能) 当前/历史结算日数据
数据采集 需量采集(最大需量) 当前/历史最大需量及发生时间
数据采集 变量采集(电压、电流、功率等) 三相电压、电流、功率、功率因数
数据采集 谐波数据采集 电压/电流谐波含量
数据采集 状态信息采集 电表运行状态字
数据采集 冻结数据采集 定时冻结、瞬时冻结数据
地址广播 读取电表通信地址 支持广播地址识别

1.3 设计原则

  • 一致性: 遵循项目统一的驱动接口规范,与 S7、EtherNet/IP 等驱动保持一致的设计风格
  • 可扩展性: 模块化设计,便于后续功能扩展
  • 可靠性: 完善的错误处理和重连机制
  • 可测试性: 支持依赖注入,便于单元测试
  • 多设备支持: 支持单串口多设备连接

2. 技术架构

2.1 整体架构

┌─────────────────────────────────────────────────────────────────┐
│                      EdgeX Gateway                              │
├─────────────────────────────────────────────────────────────────┤
│                    Device Service Layer                         │
│   ┌─────────────┐  ┌─────────────┐  ┌─────────────┐           │
│   │  DriverMgr  │  │  Schedule   │  │  ConfigMgr  │           │
│   └──────┬──────┘  └─────────────┘  └─────────────┘           │
│          │                                                      │
├──────────┼──────────────────────────────────────────────────────┤
│                    Protocol Driver Layer                        │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │                   DLT645Driver                          │   │
│   │  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌────────┐  │   │
│   │  │ transport│◄─│ scheduler│◄─│ decoder  │  │ config │  │   │
│   │  │ (串口/TCP) │  │ (读写调度)│  │ (编解码) │  │        │  │   │
│   │  └────┬─────┘  └──────────┘  └──────────┘  └────────┘  │   │
│   │       │                                                 │   │
│   └───────┼─────────────────────────────────────────────────┘   │
├───────────┼──────────────────────────────────────────────────────┤
│                    Network Layer                                │
│              RS-485 / TCP/IP                                   │
└───────────┴──────────────────────────────────────────────────────┘

2.2 模块划分

模块 文件 职责 对应参考
驱动主模块 dlt645.go 实现 Driver 接口,协调整体流程 s7.go, ethernetip.go
传输层 transport.go 串口/TCP连接管理、帧收发、超时处理 transport.go
调度器 scheduler.go 批量点位读写调度、多设备轮询 scheduler.go
解码器 decoder.go 地址解析、数据编解码、协议帧处理 decoder.go

2.3 核心类/结构体设计

2.3.1 DLT645Driver(驱动主类)

type DLT645Driver struct {
    config    model.DriverConfig
    transport *DLT645Transport
    decoder   *DLT645Decoder
    scheduler *DLT645Scheduler
}

2.3.2 DLT645Transport(传输层)

type DLT645Transport struct {
    cfg               map[string]any
    conn              interface{} // serial.Port 或 net.Conn
    connected         atomic.Bool
    connectTime       time.Time
    lastDisconnectTime time.Time
    reconnectCount    atomic.Int32
    localAddr         string
    remoteAddr        string
    
    // 通信类型
    commType          CommType // Serial 或 Ethernet
    
    // 串口配置
    serialPort        string
    baudRate          int
    dataBits          int
    stopBits          int
    parity            string
    
    // TCP配置
    ip                string
    port              int
    connectionMode    ConnectionMode // Client 或 Server
    
    // 超时配置
    connectTimeout    time.Duration
    responseTimeout   time.Duration
    sendInterval      time.Duration
    retryInterval     time.Duration
    maxRetries        int
}

type CommType int
const (
    CommTypeSerial CommType = iota
    CommTypeEthernet
)

type ConnectionMode int
const (
    ConnectionModeClient ConnectionMode = iota
    ConnectionModeServer
)

2.3.3 DLT645Scheduler(调度器)

type DLT645Scheduler struct {
    transport     *DLT645Transport
    decoder       *DLT645Decoder
    
    // 配置
    sendInterval  time.Duration // 指令发送间隔
    retryInterval time.Duration // 重试间隔
    maxRetries    int           // 最大重试次数
    
    // 统计
    totalRequests int64
    successCount  int64
    failureCount  int64
    mu            sync.Mutex
}

2.3.4 DLT645Decoder(解码器)

type DLT645Decoder struct {
    // 数据标识映射
    dataIDMap map[string]DataIDInfo
}

type DataIDInfo struct {
    Name       string     // 数据项名称
    DataType   model.DataType // 数据类型
    Decimal    float64    // 小数位
    Unit       string     // 单位
    Description string    // 描述
}

type Address struct {
    MeterAddress string // 电表通信地址
    DataID       []byte // 数据标识 (DI3-DI2-DI1-DI0)
    Extension    string // 扩展标识(如 #T, #01 等)
}

3. 接口定义

3.1 驱动接口(实现 driver.Driver

方法 功能 参数 返回值
Init(cfg model.DriverConfig) error 初始化驱动 cfg: 驱动配置 error: 错误信息
Connect(ctx context.Context) error 建立连接 ctx: 上下文 error: 错误信息
Disconnect() error 断开连接 error: 错误信息
ReadPoints(ctx, points) (map, error) 批量读取点位 points: 点位列表 map[string]model.Value: 读取结果
WritePoint(ctx, point, value) error 写入单个点位 point: 点位, value: 值 error: 错误信息
Health() driver.HealthStatus 获取健康状态 HealthStatus: 健康状态
SetSlaveID(slaveID uint8) error 设置从站ID slaveID: 从站地址 error: 错误信息
SetDeviceConfig(config map[string]any) error 设置设备配置 config: 配置参数 error: 错误信息
GetConnectionMetrics() (...) 获取连接指标 连接时长、重连次数、地址信息

3.2 内部接口

3.2.1 传输层接口

方法 功能
SendFrame(frame *DLT645Frame) error 发送协议帧
RecvFrame(ctx context.Context) (*DLT645Frame, error) 接收协议帧
ReadData(meterAddr string, dataID []byte) ([]byte, error) 读取指定数据
WriteData(meterAddr string, dataID []byte, data []byte) error 写入指定数据
BroadcastAddress() ([]string, error) 广播读取电表地址

3.2.2 调度器接口

方法 功能
ReadPoints(ctx, points) 批量读取点位
WritePoint(ctx, point, value) 写入点位
GetStats() 获取统计信息

3.2.3 解码器接口

方法 功能
ParseAddress(addr string) (*Address, error) 解析地址字符串
EncodeFrame(data []byte, meterAddr string) ([]byte, error) 编码协议帧
DecodeFrame(data []byte) (*DLT645Frame, error) 解码协议帧
DecodeValue(frame *DLT645Frame, dataType model.DataType) (any, error) 解码数据值
GetDataIDInfo(dataID []byte) (DataIDInfo, error) 获取数据标识信息

4. 配置参数

4.1 设备配置项

参数名 类型 默认值 说明
commType string “Serial” 物理链路:Serial 或 Ethernet
connectionTimeout int 3000 连接超时时间(毫秒)
responseTimeout int 3000 响应超时时间(毫秒)
retryInterval int 1000 指令重发间隔(毫秒)
sendInterval int 500 指令发送间隔(毫秒)
maxRetries int 3 最大重试次数

串口模式配置

参数名 类型 默认值 说明
serialPort string - 串口设备路径,如 /dev/ttyS0
baudRate int 9600 波特率
dataBits int 8 数据位
stopBits int 1 停止位
parity string “None” 校验位:None, Odd, Even

以太网模式配置

参数名 类型 默认值 说明
connectionMode string “Client” 连接模式:Client 或 Server
ip string - 设备IP地址(Client模式)或本机地址(Server模式)
port int 9600 端口号

4.2 点位配置

4.2.1 地址格式

mail_address#DI3-DI2-DI1-DI0[#extension]
  • mail_address: 电表通信地址(12位十进制或16位BCD码)
  • DI3-DI2-DI1-DI0: 数据标识,十六进制表示
  • extension: 可选扩展标识(如 #T 表示时间,#01 表示费率1等)

4.2.2 支持的数据类型

数据类型 说明 适用场景
UINT8 无符号8位整数 状态字、标志位
INT8 有符号8位整数 带符号的状态值
UINT16 无符号16位整数 电压、电流等变量
INT16 有符号16位整数 带符号的测量值
UINT32 无符号32位整数 功率、冻结数据
INT32 有符号32位整数 带符号的功率值
UINT64 无符号64位整数 电能量(主要使用)
INT64 有符号64位整数 带符号的电能量
STRING 字符串 时间信息、状态描述

4.2.3 数据标识说明

DI3 = 00 - 电能量

数据标识 说明 数据类型 Decimal
00-00-00-00 当前组合有功总电能 UINT64 0.01
00-00-00-01 上1结算日组合有功总电能 UINT64 0.01
00-80-00-00 当前关联总电能 UINT64 0.01
00-15-00-01 上1结算日A相正向有功电能 UINT64 0.01

DI3 = 01 - 最大需量

数据标识 说明 数据类型 Decimal
01-01-00-00 当前正向有功最大需量 UINT64 0.0001
01-01-00-00#T 当前正向有功最大需量发生时间 STRING 0

DI3 = 02 - 变量

数据标识 说明 数据类型 Decimal
02-01-01-00 A相电压 UINT16 0.1
02-02-01-00 A相电流 UINT32 0.001
02-03-01-00 A相有功功率 INT32 0.1
02-0A-01-01 A相电压1次谐波含量 UINT16 0.01
02-80-00-01 零线电流 UINT16 0.01
02-80-00-02 电网频率 UINT16 0.01

DI3 = 04 - 参变量

数据标识 说明 数据类型 Decimal
04-00-01-01 日期及时间 STRING 0
04-00-01-03 最大需量周期 UINT16 0
04-00-04-01 通信地址 STRING 0
04-00-05-01 电表运行状态字1 UINT32 0

DI3 = 05 - 冻结数据

数据标识 说明 数据类型 Decimal
05-00-01-01 上1次定时冻结正向有功总电能 UINT32 0.01
05-00-09-01 上1次定时冻结正向有功最大需量 UINT32 0.0001
05-00-09-01#T 上1次定时冻结正向有功最大需量发生时间 STRING 0

4.2.4 地址示例

地址 数据类型 说明
123456789012#02-01-01-00 UINT16 A相电压
123456789012#02-02-01-00 UINT32 A相电流
123456789012#00-00-00-00 UINT64 当前组合有功总电能
123456789012#01-01-00-00 UINT64 当前正向有功最大需量
123456789012#01-01-00-00#T STRING 当前正向有功最大需量发生时间
123456789012#04-00-01-01 STRING 日期及时间

5. 数据处理流程

5.1 连接建立流程

串口模式

EdgeX                          电能表
  |                               |
  |--- 打开串口 ------------------->|
  |                               |
  |<-- 串口打开成功 ----------------|
  |                               |
  |--- 发送唤醒帧 ----------------->|  FE FE FE
  |                               |
  |<-- 电表响应 -------------------|  地址帧

以太网模式(Client)

EdgeX                          电能表/网关
  |                               |
  |--- TCP Connect -------------->|
  |                               |
  |<-- TCP ACK -------------------|
  |                               |
  |--- 发送唤醒帧 ----------------->|  FE FE FE
  |                               |
  |<-- 电表响应 -------------------|  地址帧

5.2 协议帧结构

┌─────────────────────────────────────────────────────────┐
│ DL/T 645-2007 Frame                                    │
├──────┬──────┬────────┬────────┬────────┬──────┬───────┤
│ STX  │ ADDR │ CTRL   │ LEN    │ DATA   │ CS   │ ETX   │
│ (1)  │ (6)  │ (1)    │ (1)    │ (N)    │ (1)  │ (1)   │
└──────┴──────┴────────┴────────┴────────┴──────┴───────┘

STX: 起始符 0x68
ADDR: 电表地址(6字节BCD码)
CTRL: 控制码
LEN: 数据长度
DATA: 数据域
CS: 校验和
ETX: 结束符 0x16

控制码格式:
┌─────────────────────────────────┐
│ D7 D6 D5 D4 D3 D2 D1 D0        │
├─────────────────────────────────┤
│ D7: 主站/从站标志 (1=主站)     │
│ D6-D5: 帧类型                  │
│        00: 标准帧              │
│        01: 扩展帧              │
│ D4-D3: 命令码                  │
│        01: 读数据              │
│        02: 写数据              │
│ D2: 保留                       │
│ D1-D0: 帧序号                  │
└─────────────────────────────────┘

5.3 点位读取流程

┌─────────────────────────────────────────────────────────────┐
│                    采集组定时触发                           │
└──────────────────────────┬──────────────────────────────────┘
                           ▼
┌─────────────────────────────────────────────────────────────┐
│  Scheduler: 按电表地址分组点位                             │
└──────────────────────────┬──────────────────────────────────┘
                           ▼
┌─────────────────────────────────────────────────────────────┐
│  Transport: 发送读数据命令 (CTRL=0x41)                     │
└──────────────────────────┬──────────────────────────────────┘
                           ▼
┌─────────────────────────────────────────────────────────────┐
│  Transport: 接收响应帧,校验和验证                         │
└──────────────────────────┬──────────────────────────────────┘
                           ▼
┌─────────────────────────────────────────────────────────────┐
│  Decoder: 解码数据,转换为 model.Value                      │
└──────────────────────────┬──────────────────────────────────┘
                           ▼
┌─────────────────────────────────────────────────────────────┐
│  返回结果到 Device Service                                  │
└─────────────────────────────────────────────────────────────┘

5.4 多设备轮询流程

┌─────────────────────────────────────────────────────────────┐
│                    多设备轮询调度                           │
└──────────────────────────┬──────────────────────────────────┘
                           ▼
┌─────────────────────────────────────────────────────────────┐
│  获取设备列表(按地址排序)                                │
└──────────────────────────┬──────────────────────────────────┘
                           ▼
┌─────────────────────────────────────────────────────────────┐
│  遍历每个设备,依次发送读取命令                             │
│  (每设备之间间隔 sendInterval)                             │
└──────────────────────────┬──────────────────────────────────┘
                           ▼
┌─────────────────────────────────────────────────────────────┐
│  接收响应,超时则跳过该设备                                 │
└─────────────────────────────────────────────────────────────┘

6. 错误处理机制

6.1 错误分类

错误类型 触发条件 处理策略
连接错误 串口打开失败、TCP连接失败 自动重连(带指数退避)
帧错误 帧格式错误、校验和失败 记录日志,丢弃错误帧
协议错误 数据标识不支持、参数错误 返回错误,不发送请求
超时错误 响应超时 重试或标记失败
设备错误 电表返回否定确认 记录日志,标记点位质量为Bad

6.2 DL/T 645-2007 错误码

错误码 说明
0x00 正常
0x01 不识别的命令
0x02 无效的数据标识
0x03 无效的数据
0x04 不允许的操作
0x05 访问被拒绝
0x06 数据不存在

6.3 重连机制

// 重连策略
maxRetries    int           // 最大重试次数
retryInterval time.Duration // 基础重试间隔
maxBackoff    time.Duration // 最大退避时间

// 重试间隔计算(指数退避)
wait = retryInterval * (2^attempt)
if wait > maxBackoff:
    wait = maxBackoff

7. 与其他系统集成

7.1 EdgeX Device Service 集成

驱动通过 driver.RegisterDriver 注册,Device Service 通过统一接口调用:

func init() {
    driver.RegisterDriver("dlt645-2007", func() driver.Driver {
        return NewDLT645Driver()
    })
}

7.2 数据流向

┌──────────────┐      ┌──────────────┐      ┌──────────────┐
│  电能表       │─────>│  DLT645      │─────>│  EdgeX       │
│  (RS-485/TCP)│      │  Driver      │      │  Core Data   │
└──────────────┘      └──────────────┘      └──────────────┘
     ^                      │                      │
     │                      │                      │
     │<─────────────────────│<─────────────────────│
     │    广播地址命令        │    读取请求          │    北向指令

7.3 北向数据格式

驱动上报的数据点包含以下信息:

字段 类型 说明
PointID string 点位唯一标识
Value any 数据值
Quality string 品质(Good/Bad/Uncertain)
TS time.Time 时间戳
Unit string 单位(可选)

8. 代码结构

internal/driver/dlt645/
├── dlt645.go          # 驱动主模块
├── transport.go       # 传输层(串口/TCP连接、帧处理)
├── scheduler.go       # 调度器(批量读写、多设备轮询)
├── decoder.go         # 解码器(地址解析、数据编解码)
├── protocol.go        # 协议常量和数据结构
├── transport_test.go  # 传输层测试
├── decoder_test.go    # 解码器测试
└── scheduler_test.go  # 调度器测试

8.1 文件职责说明

文件 职责 关键功能
dlt645.go 驱动入口 实现 Driver 接口,初始化各模块
transport.go 传输层 串口/TCP连接管理、帧收发、超时处理
scheduler.go 调度层 点位分组、批量读写、多设备轮询
decoder.go 编解码层 地址解析、协议帧编解码
protocol.go 协议定义 帧结构、控制码、数据标识映射

9. 安全性考虑

9.1 注意事项

风险点 描述 关联模块
未授权访问 无认证机制,任何人可连接 transport
数据篡改 无消息完整性校验(仅简单校验和) decoder
拒绝服务 恶意设备可发送大量数据 transport
地址伪造 电表地址无验证机制 decoder

9.2 建议措施

  1. 网络层面: 部署防火墙,限制访问IP
  2. 传输加密: 考虑使用 TLS 加密传输(以太网模式)
  3. 输入验证: 严格校验电表地址和数据标识格式
  4. 流量控制: 实现请求频率限制,防止总线拥塞

10. 部署与集成

10.1 驱动注册

cmd/driver/registry.go 中添加导入:

import (
    _ "github.com/anviod/edgex/internal/driver/dlt645"
)

10.2 配置示例

串口模式配置

device:
  name: "ElectricMeter-01"
  protocol: "dlt645-2007"
  config:
    commType: "Serial"
    serialPort: "/dev/ttyS0"
    baudRate: 9600
    dataBits: 8
    stopBits: 1
    parity: "None"
    connectionTimeout: 3000
    responseTimeout: 3000
    retryInterval: 1000
    sendInterval: 500
    maxRetries: 3

以太网模式配置(Client)

device:
  name: "ElectricMeter-01"
  protocol: "dlt645-2007"
  config:
    commType: "Ethernet"
    connectionMode: "Client"
    ip: "192.168.1.100"
    port: 9600
    connectionTimeout: 3000
    responseTimeout: 3000
    retryInterval: 1000
    sendInterval: 500
    maxRetries: 3

10.3 点位配置示例

points:
  - name: "ActiveEnergy-Total"
    address: "123456789012#00-00-00-00"
    dataType: "UINT64"
    attribute: "Read"
    unit: "kWh"
  
  - name: "Voltage-PhaseA"
    address: "123456789012#02-01-01-00"
    dataType: "UINT16"
    attribute: "Read"
    unit: "V"
  
  - name: "Current-PhaseA"
    address: "123456789012#02-02-01-00"
    dataType: "UINT32"
    attribute: "Read"
    unit: "A"
  
  - name: "Power-Active-PhaseA"
    address: "123456789012#02-03-01-00"
    dataType: "INT32"
    attribute: "Read"
    unit: "W"
  
  - name: "MaxDemand-Time"
    address: "123456789012#01-01-00-00#T"
    dataType: "STRING"
    attribute: "Read"
  
  - name: "Meter-Time"
    address: "123456789012#04-00-01-01"
    dataType: "STRING"
    attribute: "Read"

11. 测试方案

11.1 电能表连接示例

本章节介绍如何使用 DL/T 645-2007 插件连接电能表,实现读取电能表中的点位值。

11.1.1 前置准备

  1. 已安装 RS-485 转串口适配器或以太网网关
  2. 电能表已正确接线并通电
  3. 已知电能表通信地址(可通过广播命令获取)

11.1.2 硬件连接

RS-485 连接

EdgeX Gateway          RS-485 Adapter          电能表
     │                      │                     │
   TXD ──────────────────> A (+) ─────────────> A
   RXD ──────────────────> B (-) ─────────────> B
    GND ─────────────────> GND ───────────────> GND

以太网连接

EdgeX Gateway          串口服务器            电能表/集中器
     │                      │                     │
   Ethernet ─────────────> Ethernet ───────────> RS-485

11.1.3 获取电表地址

使用广播命令获取总线上所有电表地址:

device:
  name: "Meter-Scanner"
  protocol: "dlt645-2007"
  config:
    commType: "Serial"
    serialPort: "/dev/ttyS0"
    baudRate: 9600

驱动启动后会自动发送广播命令,扫描总线上的电表地址。

11.1.4 测试验证

测试项 验证方法 预期结果
连接测试 启动驱动后查看健康状态 HealthStatusGood
地址扫描 发送广播命令 收到电表地址响应
电能量读取 读取 00-00-00-00 读取到有功总电能
电压读取 读取 02-01-01-00 读取到A相电压
电流读取 读取 02-02-01-00 读取到A相电流
时间读取 读取 04-00-01-01 读取到电表时间

11.1.5 常见问题排查

问题现象 可能原因 解决方案
连接失败 串口设备不存在 检查串口路径是否正确
连接失败 权限不足 确保用户有权限访问串口设备
无响应 RS-485接线错误 检查A/B线是否接反
无响应 波特率不匹配 确认电表波特率配置
读取失败 地址错误 使用广播命令重新获取地址
读取失败 校验和错误 检查线路干扰,增加发送间隔

11.1.6 支持的电能表类型

类型 说明 支持程度
单相电能表 普通居民用电表 完全支持
三相四线电能表 工业用电表 完全支持
三相三线电能表 工业用电表 完全支持
多功能电能表 支持多种计量功能 完全支持

附录:DL/T 645-2007 数据标识编码

A.1 数据标识格式

DI3 DI2 DI1 DI0
├──┴──┴──┴──┘
│   │   │   └── 结算日/特殊标识
│   │   └────── 数据项分类
│   └────────── 数据类型

A.2 数据类型编码

DI3 数据类型
00 电能量
01 最大需量
02 变量
03 冻结数据
04 参变量
05 定时冻结数据
06 负荷记录

A.3 结算日编码(DI0)

DI0 说明
00 当前
01 上1结算日
02 上2结算日
0C 上12结算日