meter.h
t_data 的单位约定4.3 三块运行表 + 一块配置段的分工5. 对外接口6. 依赖关系7. 备注与风险点源文件路径:
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(代码里也以 `0xE0 | n` 拼出) |
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_meter | METER_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 层引用 |
本文件依赖的声明
#include "chip.h"— 提供u8/u16/u32/s16/s32等基础类型与STATIC_ASSERT/TRUE/FALSE#include "backupbox.h"— 提供BACKUP_BOX_CONFIG_T、BACKUP_BOX_RUN_T、BB_DEBUG_INFO#include "debug.h"— 调试输出
6. 依赖关系
- 依赖:
chip.h(类型/断言)、backupbox.h(BB 配置与运行表类型)、debug.h(调试宏)。 - 被依赖(
include "meter.h"或直接用g_t_meter的文件):midware/meter/meter.c、midware/meter/ct.c、midware/meter/backupbox.c、midware/meter/bb_update.c、midware/meter/meter_upgrade.c、midware/meter/diesel_generator.clogic/(功率调度按meter_read_*取值)、data/(故障表ems_set_fault_bit)、net/(云端上报电表信息)、hmi/(菜单显示)、user/main.c(创建task_meter)
7. 备注与风险点
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 校验时需要留意。
STATIC_ASSERT(ASSERT_FLASH_SECTOR_SIZE % sizeof(METER_CONFIG_TAB_T) == 0)是硬约束。任何新增字段导致sizeof(METER_CONFIG_TAB_T)变化,都可能触发编译期断言失败,或(若仍能整除)静默改变 Flash 中已存配置的解析偏移,造成老设备升级后配置错乱。u8_reserved[13]就是为此预留的。
- 宏
MIN_VERSION_CH_DTSU666/MIN_VERSION_CH_DTSU666iDW定义了但未被使用。meter.c中判断最小版本时直接写死字符串"V127"(如meter.c:5081、meter.c:5135、meter.c:5333)。若后续修改最低版本门槛,改宏不会生效,是典型的版本遗留隐患。
- GRID 与 PV 的从站地址存在重合(ADL3000/BB/ADL400 的 GRID 地址与 PV 的如 0x01/0x02 等),二者共用同一条 USART1 总线与
g_u8_recv_buf。地址不重合是探测轮询表cmdData[][6]设计的前提,新增型号时必须保证地址与长度校验(地址 + 功能码 + 字节数三重判定)足够唯一,否则会出现误识别。
METER_TYPE_E的枚举值被持久化(写入e_grid_meter_type_old),且ALPHA_NCT(=7)、ALPHA_SINGLE_LOOP(=8)、ALPHA_DOUBLE_LOOP(=9) 等取值被 ADL400N/HMI 逻辑隐式依赖。禁止在中间插入新枚举项。
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 分支),存在字符串越界读取风险。
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的风险点章节。
- 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 布局无关但会大面积影响调用点。