EMS 知识库
首页 / knowledge/代码解析/midware/meter

meter.h

源文件路径:midware/meter/meter.h 所属模块:midware(设备中间件)→ 电表通信 代码行数:528

1. 文件职责

本文件是 EMS 电表(meter)子系统的对外契约头,定义了电网侧(GRID)、并网逆变器侧(PV)、中压/三相侧(MID)三处电表以及正泰(CHNT)无线电表模块的全部数据结构、地址常量、故障标志位和对外函数原型。它把 meter.c 内部的采集结果(METER_DATA_T)与配置持久化结构(METER_CONFIG_TAB_T)打包成全局表 METER_TABLE_T g_t_meter,供 logic 层功率调度、data 层故障上报、hmi 层显示、net 层云端上传共同读取。第三个环节的位置是「协议采集 → 本文件的数据模型 → 上层策略/显示/上报」,本身不产生任何通信行为。它被 user/main.c 创建的电表守护任务 task_meter() 驱动填充,也被 CT 采样任务、P1(SMILE-P1-WB01)无线采集通道反向写入。文件头注释的 @version V1.0.0 / @date 2019/03/09 已明显滞后于代码实际演进度(含 2024+ 新增的 MID 电表、BB23K、相序自适应等)。

2. 关键数据结构与宏

2.1 枚举

名称类型说明
METER_TYPE_E枚举电表型号。SM60A=1, ADL3000, BACKUPBOX_5K, BACKUPBOX_10K, ACR10R, CT, ALPHA_NCT, ALPHA_SINGLE_LOOP, ALPHA_DOUBLE_LOOP, AGF200, ADL400, ADL400M_CT2, SMILE_P1_WB01, BACKUPBOX_23K, MAX_METER_TYPE。序号即持久化到 Flash 的值,不可随意调整次序
METER_SITE_E枚举电表安装位置。GRID_METER=1, PV_METER, CHNT_WIRELESS, MID_METER, MAX_METER_SITE,是绝大多数对外接口的 site 入参取值域
METER_PHASE_E枚举相位索引。PHASE_A=0, PHASE_B, PHASE_C, PHASE_T(总),MAX_PHASE=4;meter_read_xxx() 系列按此索引取数组
METER_SAMPLING_TYPE_E枚举采样来源。CT_SAMPLING, METER_SAMPLING, P1_SAMPLING,由 meter_set_ct_sampling() 的位掩码换算而来

2.2 结构体 / 联合体

名称类型说明
METER_DATA_T结构体单块电表的实时电气量。含 u16_volt[4](0.1V)、s16_current[4](0.01A)、s32_actpower[4](0.001kW)、s32_reactpower[4](0.001kvar)、s16_factor[4](0.01)、u16_frequent(0.01Hz)、u32_total_phase_energy_input/output、u32_energy_input/output[4](0.01kWh)。三重包 #pragma pack(1)
METER_SINGLE_NEGATE_U联合体正泰电表单相接反标志位域,u8_all 整体比较 + bit.meter_a/b/c 逐相比较,用于判断是否需要回写下发
BB_FAULT_U联合体BackupBox(BB)故障位域,u32_all 与 32 个 bit;仅 u8_overload_fault、u8_n_relay_fault、u8_over_temperature_fault 三位被赋值,其余保留
METER_RUN_TABLE_T结构体单块电表的运行表。含采数状态(u8_detect_flag/u8_found/u8_comm_lost_err/u8_lost_cnt/u32_get_data_last_time/u32_find_tick)、清表状态(u8_clear_flag/u8_clear_run_data_flag/u8_clear_cnt)、P1 刷新状态(u8_refresh_flag/u32_p1_cycle/u8_p1_lose_flag)、BB 身份(u16_bb_ver/u32_bb_sn/u8_bb_sn[31])、版本与序列号字符串、型号 e_type_now、单相接反标志、电参量 t_data、无线电表标志 u8_wireless_meter_flag
CHNT_WIRELESS_STACK结构体正泰无线模块命令栈占位:u8_codes[MAX_CODES+1] + 栈顶 u8_top;MAX_CODES=3
CHNT_WIRELESS_INFO_TABLE_T结构体正泰无线模组信息表。3 个节点(AP/STA1/STA2)各自的 SN、发射功率 u8_module_eirp[3]、频段 u8_regional_freq_bands[3]、型号 u8_module_type[3][16]、软硬件版本 [3][10]、RSSI s16_module_rssi[3];外加 u8_module_connect_state、u8_module_child_nodes_num/state、读/写状态机游标 u8_wirerless_read_state/u8_wirerless_set_state、丢失计数 u8_lost_cnt[3]
BB_DEBUGE_T结构体BackupBox 诊断量集合。字段即 BB 寄存器 0x0025~0x0073 的镜像(继电器状态、三相 grid_load 差流、ACL/backup/load 三相电压、N 线继电器故障、过温、过载电流、重启次数、BB 状态等),末尾内嵌 BB_FAULT_U bb_fault_u
METER_CONTROL_TABLE_T结构体三个「立即查询正泰 DTS 特殊指令」的触发标志:u8_grid_meter_query_dts_special_cmd_flag、u8_pv_meter_query_dts_special_cmd_flag、u8_mid_meter_query_dts_special_cmd_flag
METER_CONFIG_TAB_T结构体需要掉电保存的电表配置段,落盘到 METER_CONFIG_PAGE。含表头 u16_head、GRID/PV 的「电表还是 CT」u8_*_meter_or_ct、直接 CT 倍率 u16_*_ct_rate、电表侧 CT 使能与倍率、方向取反、上次识别到的型号 e_*_meter_type_old、内嵌 BACKUP_BOX_CONFIG_T t_bb、u8_gridforming_en、电表型号字符串 u8_grid_meter_type[17]/u8_pv_meter_type[17]、EMS 自算总电能标志与四个累计总电能 u32_total_phase_*_energy_*、硬件版本 u8_*_merter_hw_ver[9]、单相接反配置位域、u8_genset_enable_flag、u8_show_meter_data_flag、u8_energy_data_enable、u8_meter_triphase_sequnece_flag、u8_chnt_wireless_moudle_regional_freq_bands、MID 侧 u8_mid_meter_negate/u8_mid_meter_function_enable、u8_reserved[13]、u16_crc_code。注意:该结构体不在 #pragma pack(1) 范围内(pack 在 424 行已 #pragma pack() 关闭),因此布局由编译器默认对齐决定,改字段时必须同步核对 Flash 兼容性
METER_ADJUST_STATE_U联合体相序自适应完成状态位域:u8_grid_adjust_ready / u8_pv_adjust_ready,可与 BOTH_ADJUST_READY_MASK 整体比较
METER_TABLE_T结构体电表子系统总表,即全局 g_t_meter 的类型:t_config_data(配置)+ t_grid_info/t_pv_info/t_mid_info(三块运行表)+ t_bb_info(BB 运行态,来自 backupbox.h)+ t_bb_debug + t_meter_control_info + t_chnt_wireless_info + g_code_stack

2.3 重要宏

名称类型说明
METER_DEBUG_INFO / MID_METER_DEBUG_INFO宏调试开关,均置 0。注意代码中多处写成 #if (METER_DEBUG_INFO) 而非 == 1,效果等同于「关闭」
GRID_*_METER_ADDR / PV_*_METER_ADDR宏各型号电表在 485 总线上的从站地址。GRID:SM60A 0x03、ADL3000 0x01、BB 0x01、ACR10R 0x05、ALPHA 0x01、AGF200 0x07、ADL400 0x01、ADL400N 0x01;PV:0x04/0x02/0x02/0x06/0x02/0x0A/0x02,另加 PV_GRIDFORMING_EN_METER_ADDR=0x0B(gridforming 使能时 PV 侧 ALPHA/ADL3000/ADL400 统一改用 0x0B)
MID_3PH_METER_ADDR宏MID 三相电表地址 0x01
MAX_CODES宏正泰无线命令栈深度 3
METER_READ_FUN_CODE / METER_WRITE_FUN_CODE宏Modbus 功能码 0x03(读保持寄存器)/ 0x06(写单个寄存器)
CHNT_WIRELESS_AP_ADDR / _STA1_ / _STA2_宏正泰无线模组地址 0xE1/0xE2/0xE3(代码里也以 `0xE0n` 拼出)
CHNT_WIRELESS_INFO_ADDR / _NUM宏无线参数块起始 0xE300,长度 0x1B(27) 个寄存器
CHNT_SET_WIRELESS_FREQ_BANDS_ADDR宏设置频段寄存器 0xE301
CHNT_MOUDLE_INFO_ADDR / _NUM宏模组信息块起始 0xEA00,长度 0x13(19) 个寄存器
CHNT_WIRELESS_BOUND_ADDR宏无线模组绑定寄存器 0xEB00
BACKUPBOX_ID宏BB 应答中的识别字 0x00AB,用于把 BB 与 ADL3000/ADL400 的响应区分开
BACKUPBOX_*(0x0025~0x0073 系列)宏BB 各诊断寄存器的绝对寄存器地址(继电器状态 0x0025、N 线继电器故障 0x0049、过温 0x004A、最高继电器温度 0x004C、N/A/B/C 相过载电流 0x004D~0x0050、过载故障 0x005A、重启次数 0x005B、BB 状态 0x005C、backup/load/ACL 三相电压 0x006B~0x0073、grid_load 差流 0x0043~0x0045)。meter.c 用 (ADDR - 0x20) * 2 换算成响应报文偏移
METER_RECV_BUFFER_SIZE宏收发缓冲区大小 256 字节
METER_LOST_MAX_CNT宏电表丢失判决阈值 15 次
METER_DETECT_INTERVAL_TIME宏未找到电表时的重新探测间隔 1000ms
WIRELESS_METER_LOST_TIME宏无线电表判丢失时间 10000ms(10s)
CHNT_WIRELESS_DETECT_INTERVAL_TIME宏无线模组探测间隔 1000ms
TYPE_SM60A … TYPE_NONE宏型号字符串常量,供 meter_get_meter_modle() 返回给上层/HMI
VERSION_BB_HARD宏BB 固定硬件版本字符串 "V1.02"
METER_TYPE_CH_* / METER_TYPE_AC_ADL400宏电表型号字符串子串(DTSU666-100/40mA、DTSU666iDW433/446、DTSU666iDM433/446、ADL400),用作 strstr() 匹配依据来识别正泰/安科瑞电表
Chint / Acrel宏厂商编号 1 / 2,为 meter_get_meter_type() 返回值
GRID_ADJUST_READY_MASK/PV_ADJUST_READY_MASK/BOTH_ADJUST_READY_MASK宏相序自适应就绪位掩码 0x01/0x02/0x03
MIN_VERSION_CH_DTSU666 / MIN_VERSION_CH_DTSU666iDW宏正泰老/新电表最低支持版本 "V127" / "V102";实际代码里只有 "V127" 被字面量硬编码使用,这两个宏在 .c 中未被引用
GRID_SUPPORT_METER_NUMBER / PV_SUPPORT_METER_NUMBER / MID_SUPPORT_METER_NUMBER宏探测轮询表长度 7 / 8 / 1
STATIC_ASSERT(...)宏编译期断言 ASSERT_FLASH_SECTOR_SIZE % sizeof(METER_CONFIG_TAB_T) == 0,保证配置结构体在 Flash 扇区内可整数倍存放

2.4 导出的全局对象

名称类型说明
g_u8_recv_buf[METER_RECV_BUFFER_SIZE]u8[]全模块共享的 485 接收缓冲区(meter.c 定义,extern 到此处)。同一时刻 GRID/PV/MID/无线共用一块缓冲
g_t_meterMETER_TABLE_T电表子系统总表(在 meter.c:38 定义)

3. 函数清单

本文件为头文件,只含声明。以下为全部原型(含声明但未在本文件实现的函数)。

函数名作用域说明
task_meter全局电表守护任务入口,uCOS-II 静态任务
meter_write_data全局向电表 485(USART1)发送一帧
meter_recv_data全局从 USART1 收帧并做 Modbus CRC 校验
midvalue全局取三值中位数(N 中取 2 表决用)
meter_set_meter_or_ct / meter_get_meter_or_ct全局设置/读取某位置是「CT 还是电表」
meter_set_meter_ct_enable / meter_get_meter_ct_enable全局设置/读取「电表侧是否带 CT」
meter_set_meter_ct_rate / meter_get_meter_ct_rate全局设置/读取「电表侧 CT 倍率」
meter_read_voltage全局读某位置某相电压
meter_read_current全局读某位置某相电流
meter_read_actpower全局读某位置某相有功功率
meter_read_reactpower全局读某位置某相无功功率
meter_read_apparent全局读视在功率(软件由 U×I 计算,非电表原始值)
meter_read_factor全局读某位置某相功率因数
meter_read_frequent全局读频率
meter_read_einput全局读正向(买电)电能
meter_read_eoutput全局读反向(卖电)电能
meter_read_connect_status / meter_set_connect_status全局读/写电表连接状态
meter_get_meter_type_now / meter_get_meter_type_old全局读当前识别型号 / 读上次保存型号
meter_get_pv_meter_addr_change_flag全局gridforming 使能时 PV 侧地址是否改用 0x0B
meter_read_bb_sn全局读 BackupBox 序列号字符串
meter_read_bb_software_version全局读 BackupBox 软件版本字符串
meter_get_meter_modle全局取型号字符串(供 HMI/云端)
meter_read_meter_sn全局读电表序列号字符串
meter_read_software_version全局读电表软件版本字符串
meter_read_hard_version全局读电表硬件版本字符串
meter_set_clear_flag全局置位「向电表下发清表指令」请求
meter_clear_run_data_flag全局置位「清空本地电表缓存数据」请求
set_meter_phase_sequnece_adjust_mark全局下发正泰老电表相序自适应命令
meter_set_meter_negate全局设置方向取反
IsUpgradeMeter全局是否正在被云端要求升级电表
GetPercentUpgradeMeter全局电表升级进度百分比
get_energy_data_enable全局电能数据上报使能
grid_ct_handle全局GRID 侧 CT 采样处理(实现在 ct.c;meter.c 中已被注释停用)
pv_ct_handle全局PV 侧 CT 采样处理(同上)
meter_set_run_data全局由 P1 采集通道注入整块电网数据
ct_handle全局用 CT 采样值填充某位置的运行表
meter_set_grid_hw_ver全局外部写入 GRID 电表硬件版本
meter_set_grid_com_sta全局外部设置 GRID 电表通信状态
meter_set_clear_grid_data全局外部设置 GRID 电表清数据标志
meter_set_grid_wifi_P1_fault全局设置 WIFI 与 P1 配对错误故障位
meter_set_refresh_flag / meter_read_refresh_flag全局设置/读取 P1 数据已刷新标志
meter_read_P1_refresh_time全局读 P1 刷新周期(1S 或 10S)
meter_set_P1_lose_flag / meter_get_P1_lose_flag全局设置/读取 P1 通信丢失标志(升级期间不报警)
wireless_meter_handle_lost全局无线电表丢失统一处理
wireless_meter_is_all_lost全局判断 GRID 与 PV 侧无线电表是否全部丢失
meter_set_saveConfigFlag全局置位配置保存标志
meter_run_data_clear全局清空某位置的运行数据
meter_get_chnt_wireless_module_enable / meter_set_chnt_wireless_module_enable全局正泰无线模组使能读写
meter_set_chnt_wireless_module_regional_freq_bands / meter_get_...全局正泰无线模组频段读写
meter_set_chnt_wireless_bound全局下发正泰无线模组绑定码
be_to_float全局Modbus 大端 4 字节 → float
mid_meter_clear全局MID 三相电表电能清零
meter_set_ct_sampling全局由云端位掩码设置 GRID/PV 采样来源(CT/电表/P1)

4. 核心逻辑解析

头文件不含执行逻辑,但其数据契约决定了 meter.c 的三个隐含约束,阅读实现时必须带着看:

4.1 相位索引的语义统一

METER_PHASE_E 把「总」当作第 4 个相位(PHASE_T = 3),所有 [MAX_PHASE] 数组都多留一格。于是 meter_read_xxx(site, PHASE_T) 能直接取值,无需分支。代价是 MAX_PHASE=4 在 meter_read_data_negate()、meter_set_run_data() 等循环里会把总值也一并取反——这在「总 = 分相之和」的前提下数学上自洽,但要求电表上报的总值确实是分相之和。

4.2 t_data 的单位约定

METER_DATA_T 的注释给出了统一量纲:电压 0.1V、电流 0.01A、有功 0.001kW、无功 0.001kvar、功率因数 0.01、频率 0.01Hz、电能 0.01kWh。不同厂商电表的原始寄存器量纲各不相同(如 ADL 系列频率原始为 0.01Hz、ALPHA 为 0.1V 需 ×10、ADL400N 为 IEEE754 浮点需 ×100),换算全部在 meter.c 的 meter_read_data_for_* 系列里完成。头文件不参与换算,但也因此没有任何编译期保护——上层若直接读寄存器原始值会得到错误的物理量。

4.3 三块运行表 + 一块配置段的分工

METER_CONFIG_TAB_T 是唯一掉电保存的部分(save_job_post(METER_CONFIG_PAGE)),其余表在重启后由探测流程重建。因此 e_grid_meter_type_old / e_pv_meter_type_old 是「上一次识别结果」,而 e_type_now 是「本次识别结果」,两者比较产生配置变更事件。METER_RUN_TABLE_T 内的 u8_*_meter_type[17] 字符串(正泰/安科瑞型号名)之所以放在配置段而不是运行表,是为了让相序自适应判定在重启后仍能立即生效。

5. 对外接口

本文件导出(供外部 include)

类别内容
全局变量g_u8_recv_buf、g_t_meter
函数上表第 3 节全部原型,共 62 个(含 grid_ct_handle/pv_ct_handle 两个实际由 ct.c 实现)
类型/宏第 2 节全部内容,供 ct.c、backupbox.h、logic/data/net/hmi 层引用

本文件依赖的声明

6. 依赖关系

7. 备注与风险点

  1. METER_CONFIG_TAB_T 不在 #pragma pack(1) 区间内。#pragma pack(1) 在第 188 行开启、第 424 行 #pragma pack() 关闭,而 METER_CONFIG_TAB_T(379~421 行)处于 pack(1) 区间内——它确实被 pack(1) 覆盖。但 METER_TABLE_T(437 行)在 pack 关闭之后,其成员对齐回到编译器默认,结构体总大小与 Flash 布局与 pack(1) 假设不一致,做 Flash 双备份/CRC 校验时需要留意。
  1. STATIC_ASSERT(ASSERT_FLASH_SECTOR_SIZE % sizeof(METER_CONFIG_TAB_T) == 0) 是硬约束。任何新增字段导致 sizeof(METER_CONFIG_TAB_T) 变化,都可能触发编译期断言失败,或(若仍能整除)静默改变 Flash 中已存配置的解析偏移,造成老设备升级后配置错乱。u8_reserved[13] 就是为此预留的。
  1. 宏 MIN_VERSION_CH_DTSU666 / MIN_VERSION_CH_DTSU666iDW 定义了但未被使用。meter.c 中判断最小版本时直接写死字符串 "V127"(如 meter.c:5081、meter.c:5135、meter.c:5333)。若后续修改最低版本门槛,改宏不会生效,是典型的版本遗留隐患。
  1. GRID 与 PV 的从站地址存在重合(ADL3000/BB/ADL400 的 GRID 地址与 PV 的如 0x01/0x02 等),二者共用同一条 USART1 总线与 g_u8_recv_buf。地址不重合是探测轮询表 cmdData[][6] 设计的前提,新增型号时必须保证地址与长度校验(地址 + 功能码 + 字节数三重判定)足够唯一,否则会出现误识别。
  1. METER_TYPE_E 的枚举值被持久化(写入 e_grid_meter_type_old),且 ALPHA_NCT(=7)、ALPHA_SINGLE_LOOP(=8)、ALPHA_DOUBLE_LOOP(=9) 等取值被 ADL400N/HMI 逻辑隐式依赖。禁止在中间插入新枚举项。
  1. u8_bb_sn[31] 与 u8_meter_sn[31] 长度注释为「30 字符 + '\0'」,而 meter_read_meter_sn() 中对 BB23K 分支使用 snprintf(..., 31, ...) 写入 u8_meter_sn(meter.c:6347),恰好占满无冗余;若上游 u8_bb_sn 未正确 NUL 结尾(meter.c:3380 显式补了 '\0',但仅在 23K 分支),存在字符串越界读取风险。
  1. CHNT_WIRELESS_INFO_TABLE_T.u8_software_version[3][10] 空间紧张但不溢出。get_chnt_wireless_module_info() 用 sprintf(..., "V%d.%02d.%02d", ver/10000, (ver/100)%100, ver%100) 写入,因 ver 为 u16,最坏输出为 "V6.55.35"(8 字符 + '\0' = 9 字节),恰好放得下 10 字节。但只剩 1 字节余量,任何增加字段宽度的改动(如补上四位主版本)都会立刻越界。相比之下 meter_read_bb_software_version() 的 s_u8_bb_sw_ver[7] 是真的不够(最坏 8 字节),详见 meter.c.md 的风险点章节。
  1. GRID 与 PV 的硬件版本字段在 meter.c 中被张冠李戴(meter.c:383 用 u8_pv_merter_hw_ver 处理 GRID 分支,meter.c:745 反之)。头文件本身没问题,但这两个字段命名高度相似(u8_grid_merter_hw_ver / u8_pv_merter_hw_ver,注意 merter 是源码中的拼写错误,不是 "meter"),是该 bug 的直接诱因。改名时须保持谨慎,因为字段名与 Flash 布局无关但会大面积影响调用点。