Initial commit: CCU621_M firmware project with BLE debug link support.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,340 @@
|
||||
#include "meter_calculate/calculate_task.h"
|
||||
|
||||
#include "app_init/app_init.h"
|
||||
#include "app_init/task_lock.h"
|
||||
#include "mylog/mylog.h"
|
||||
#include "wdt_task/wdt_task.h"
|
||||
#include "publicdata/publicdata.h"
|
||||
#include "flash_file_mgr/meter_calculate_flash_impl.h"
|
||||
#include "flash_file_mgr/unsettled_order_mng.h"
|
||||
|
||||
#include "app_rtc/app_rtc.h"
|
||||
#include <string.h>
|
||||
|
||||
#define METERFEE_TASK_ID (TASK_ID_Meterfee)
|
||||
#define METERFEE_SLEEP_TIME_MS() (MY_GET_SLEEP_TIME(METERFEE_TASK_ID))
|
||||
#define METERFEE_LOCK_INIT() (u8_task_lock_ctrl(METERFEE_TASK_ID, TASK_LOCK_OP_INIT))
|
||||
#define METERFEE_LOCK_TAKE() (u8_task_lock_ctrl(METERFEE_TASK_ID, TASK_LOCK_OP_TAKE))
|
||||
#define METERFEE_LOCK_GIVE() (u8_task_lock_ctrl(METERFEE_TASK_ID, TASK_LOCK_OP_GIVE))
|
||||
#define METERFEE_FEED_DOG() (v_wdt_setFlag(METERFEE_TASK_ID))
|
||||
#define METERFEE_LOG(fmt, ...) MYLOG_MSG(METERFEE_TASK_ID, (fmt), ##__VA_ARGS__)
|
||||
|
||||
S_METERING_BILLING_CTRL s_metering_ctrl;
|
||||
|
||||
|
||||
#if 1 //计量费率控制
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 订单数据周期更新 -更新结束电量、结束soc、充电时间等信息
|
||||
*
|
||||
* @param[in] u8_gunNo - 枪号 0-N
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-12
|
||||
* ************************************************************************
|
||||
*/
|
||||
static void v_meterlog_log_updata(U8_T u8_gunNo)
|
||||
{
|
||||
S_LOG_DATA *s_gun_log = &s_metering_ctrl.s_gun_log[u8_gunNo];
|
||||
Comm_Time time;
|
||||
|
||||
U8_T u8_soc = u32_meterlog_get_sys_data(u8_gunNo,E_METERLOG_GET_DATA_GUN_SOC,0);
|
||||
GetCurrentTime(&time);
|
||||
U32_T u32_time = xDate2Seconds(&time); //获取时间戳
|
||||
if(s_gun_log->StartChargeTime.iYear == 0)
|
||||
memcpy(&s_gun_log->StartChargeTime, &time,sizeof(Comm_Time));
|
||||
U32_T u32_start_time = xDate2Seconds(&s_gun_log->StartChargeTime); //获取起始时间时间戳
|
||||
|
||||
// 修复后的充电时间计算
|
||||
s_gun_log->u16_ChargedTime = (u32_time >= u32_start_time)
|
||||
? (u32_time - u32_start_time) / 60 // 正常情况
|
||||
: (u32_start_time - u32_time) / 60; // 处理时间反转情况
|
||||
|
||||
//获取结束时间值
|
||||
memcpy(&s_gun_log->EndChargeTime, &time,sizeof(Comm_Time)); //充电开始时间
|
||||
s_gun_log->u64_endKWh = u64_meterlog_get_meter_data(u8_gunNo,0); //当前电表数据
|
||||
if(u8_soc != 0)
|
||||
s_gun_log->u8_endSoc = u8_soc; //SOC
|
||||
|
||||
if(s_gun_log->u8_startSoc == 0)
|
||||
s_gun_log->u8_startSoc = s_gun_log->u8_endSoc;
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 实时获取当前费率信息 - 计算当前时段 获取费率信息
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-12
|
||||
* ************************************************************************
|
||||
*/
|
||||
static void v_meterlog_real_fee_updata()
|
||||
{
|
||||
Comm_Time time;
|
||||
U8_T u8_period_time;
|
||||
GetCurrentTime(&time); //获取当前时间
|
||||
|
||||
u8_period_time = (time.ucHour*60 + time.ucMin) / 15; //计算当前所处时段 最大96
|
||||
//METERFEE_LOG("u8_period_time=%d\n",u8_period_time);
|
||||
if(u8_period_time >= 96 ) //异常数值
|
||||
{
|
||||
if(u8_cheak_fault_flag(E_FAULT_17009,E_WHOLE_HOST)==0)
|
||||
{
|
||||
METERFEE_LOG("Time invalid, now %d-%d-%d %d:%d; period=%d", time.iYear, time.ucMonth, time.ucDay, time.ucHour, time.ucMin, u8_period_time);
|
||||
v_meterlog_set_fault(0,2,1,time.ucHour,time.ucMin); //时间不合法
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(u8_cheak_fault_flag(E_FAULT_17009,E_WHOLE_HOST) == 1)
|
||||
{
|
||||
|
||||
v_meterlog_set_fault(0,2,0,time.ucHour,time.ucMin);
|
||||
}
|
||||
}
|
||||
|
||||
//赋值当前费率信息
|
||||
s_metering_ctrl.s_real_fee_data[0].u8_period_time = u8_period_time;
|
||||
s_metering_ctrl.s_real_fee_data[0].u32_money_rate = u32_meterlog_get_sys_data(u8_period_time,E_METERLOG_GET_DATA_FEE_RATE_DATA,0); //费率电费信息 抢号代表时段位置 0-95;参数0代表充电费率 1代表放电费率 精度 0.0001元
|
||||
s_metering_ctrl.s_real_fee_data[0].u32_money_service = u32_meterlog_get_sys_data(u8_period_time,E_METERLOG_GET_DATA_FEE_SERVER_DATA,0); //费率服务费信息 抢号代表时段位置 0-95;参数0代表充电费率 1代表放电费率 精度 0.0001元
|
||||
|
||||
s_metering_ctrl.s_real_fee_data[1].u8_period_time = u8_period_time; //放电
|
||||
s_metering_ctrl.s_real_fee_data[1].u32_money_rate = u32_meterlog_get_sys_data(u8_period_time,E_METERLOG_GET_DATA_FEE_RATE_DATA,1); //费率电费信息 抢号代表时段位置 0-95;参数0代表充电费率 1代表放电费率 精度 0.0001元
|
||||
s_metering_ctrl.s_real_fee_data[1].u32_money_service = u32_meterlog_get_sys_data(u8_period_time,E_METERLOG_GET_DATA_FEE_SERVER_DATA,1); //费率服务费信息 抢号代表时段位置 0-95;参数0代表充电费率 1代表放电费率 精度 0.0001元
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 枪周期充电电量、计量计算
|
||||
*
|
||||
* @param[in] u8_gunNo - 枪号
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-12
|
||||
* ************************************************************************
|
||||
*/
|
||||
static void v_meterlog_charge_calculate(U8_T u8_gunNo)
|
||||
{
|
||||
S_CHAR_DATA *s_char_data = &s_metering_ctrl.s_char_data[u8_gunNo]; //获取枪控制信息
|
||||
S_LOG_DATA *s_gun_log = &s_metering_ctrl.s_gun_log[u8_gunNo]; //获取枪订单信息
|
||||
S_REAL_FEE_DATA *s_real_fee_data = &s_metering_ctrl.s_real_fee_data[s_char_data->u8_discharge_flag]; //获取当前费率信息 默认已经更新正确
|
||||
|
||||
U32_T u64_meter_data = u64_meterlog_get_meter_data(u8_gunNo,0); //获取枪电表值 精度 0.0001kwh
|
||||
U32_T u32_cyc_pow = 0; //本次循环电量 精度 0.0001kwh
|
||||
U32_T u32_cyc_money = 0; //本次循环费用 精度 0.0001kwh
|
||||
|
||||
//计算周期电表差值
|
||||
if(s_char_data->u64_whm_start_energy == 0) //本次充电 首次计量
|
||||
{
|
||||
s_char_data->u64_whm_start_energy = u64_meter_data;
|
||||
return;
|
||||
}
|
||||
else if(s_char_data->u64_whm_start_energy > u64_meter_data && u64_meter_data !=0 ) //上次电表电量 大于本次 则说明电表电量 超出上限进行了归0
|
||||
{
|
||||
u32_cyc_pow = METER_MAX_ELE - s_char_data->u64_whm_start_energy + u64_meter_data;
|
||||
}
|
||||
else //正常计算差值
|
||||
{
|
||||
u32_cyc_pow = u64_meter_data - s_char_data->u64_whm_start_energy;
|
||||
}
|
||||
|
||||
if(u32_cyc_pow > 10000 ) // 跳变大于10度判定电表异常
|
||||
{
|
||||
if(u8_cheak_fault_flag(E_FAULT_17007, (E_FAULT_SITE)((U16_T)E_GUN + (U16_T)u8_gunNo)) == 0U)//无告警
|
||||
{
|
||||
METERFEE_LOG("[%d]Gun meter jump fault, delta=%d, thr=%d\n", u8_gunNo, u32_cyc_pow, 10000);
|
||||
v_meterlog_set_fault(u8_gunNo,0,1,s_char_data->u64_whm_start_energy,u64_meter_data); //设置系统故障
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//更新存储的电量,本次存储电量作为下一循环的起始电量
|
||||
s_char_data->u64_whm_start_energy = u64_meter_data;
|
||||
|
||||
s_gun_log->u32_used_energy += u32_cyc_pow;
|
||||
s_gun_log->u32_part_energy[s_real_fee_data->u8_period_time] += u32_cyc_pow; //将本次循环电量累加到本时段电量中
|
||||
|
||||
u32_cyc_money = (u32_cyc_pow*(s_real_fee_data->u32_money_rate)) / 10000; //周期电费 精度 0.0001kwh
|
||||
s_gun_log->u32_used_money += u32_cyc_money; //电费累加
|
||||
|
||||
u32_cyc_money = (u32_cyc_pow*(s_real_fee_data->u32_money_service)) / 10000; //周期电费 精度 0.0001kwh
|
||||
s_gun_log->u32_used_service += u32_cyc_money;
|
||||
//METERFEE_LOG("[%d]Gun period=%d, rate=%d, svc=%d\n", u8_gunNo, s_real_fee_data->u8_period_time, s_real_fee_data->u32_money_rate, s_real_fee_data->u32_money_service);
|
||||
METERFEE_LOG("[%d]Gun addE(0.0001kWh)=%d, totalE=%d, ChgTime=%d,\n", u8_gunNo, u32_cyc_pow, s_gun_log->u32_used_energy,s_gun_log->u16_ChargedTime);
|
||||
//METERFEE_LOG(" fee=%d, svcFee=%d\n", s_gun_log->u32_used_money, s_gun_log->u32_used_service);
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief -计量计费流程
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-24
|
||||
* ************************************************************************
|
||||
*/
|
||||
static void v_meterlog_ctrl()
|
||||
{
|
||||
U8_T u8_gunNo;
|
||||
S_CHAR_DATA *s_char_ctrl = NULL;
|
||||
|
||||
if(s_metering_ctrl.u16_timer % NUM_5000 != 0) //5秒 计算一次
|
||||
return;
|
||||
|
||||
v_meterlog_real_fee_updata(); //更新当前费率信息
|
||||
|
||||
//遍历所有的枪 依次进行计量判断
|
||||
for (u8_gunNo = 0; u8_gunNo < s_metering_ctrl.u8_gunCnt; u8_gunNo++)
|
||||
{
|
||||
s_char_ctrl = &s_metering_ctrl.s_char_data[u8_gunNo]; //获取枪控制数据
|
||||
|
||||
if(s_char_ctrl->u8_billing_flag == 1) //进行充电计量计费
|
||||
{
|
||||
v_meterlog_charge_calculate(u8_gunNo); //周期计量计费
|
||||
|
||||
v_meterlog_log_updata(u8_gunNo); //更新充电结束信息
|
||||
|
||||
/* 充电中:每 60 秒将订单临时记录写入 EEPROM,支持掉电续传 */
|
||||
{
|
||||
U32_T now = get_current_seconds();
|
||||
if (s_char_ctrl->u32_last_temp_save_sec == 0 ||
|
||||
u32_safe_seconds_since(s_char_ctrl->u32_last_temp_save_sec) >= 60)
|
||||
{
|
||||
v_meterlog_flash_save_log(0, u8_gunNo);
|
||||
s_char_ctrl->u32_last_temp_save_sec = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
else //不进行计量计费
|
||||
{
|
||||
s_char_ctrl->u64_whm_start_energy = 0; //周期电表电量值清零
|
||||
s_char_ctrl->u32_last_temp_save_sec = 0;
|
||||
if(u8_cheak_fault_flag(E_FAULT_17007, (E_FAULT_SITE)((U16_T)E_GUN + (U16_T)u8_gunNo)) == 1U) //若电表跳变异常,不计量计费时恢复
|
||||
{
|
||||
v_meterlog_set_fault(u8_gunNo,0,0,0,0); //设置系统故障恢复
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief -计量计费判断
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-24
|
||||
* ************************************************************************
|
||||
*/
|
||||
static void v_meterlog_action_cheak()
|
||||
{
|
||||
U8_T u8_gunNo;
|
||||
S_CHAR_DATA *s_char_ctrl = NULL;
|
||||
S_LOG_DATA *s_gun_log = NULL;
|
||||
//终端充电状态 0空闲 1充电启动 2绝缘启动 3绝缘计算完成 4绝缘检测模块停止 5开始预充 6预充完成 7充电中 8BMS通讯超时 9充电停止 10充电停止完成
|
||||
U8_T u8_flow_state;
|
||||
|
||||
//遍历所有的枪 依次进行计量判断
|
||||
for (u8_gunNo = 0; u8_gunNo < s_metering_ctrl.u8_gunCnt; u8_gunNo++)
|
||||
{
|
||||
s_char_ctrl = &s_metering_ctrl.s_char_data[u8_gunNo]; //获取枪控制数据
|
||||
s_gun_log = &s_metering_ctrl.s_gun_log[u8_gunNo]; //获取枪订单信息
|
||||
u8_flow_state = u32_meterlog_get_sys_data(u8_gunNo,E_METERLOG_GET_DATA_GUN_FLOW_STATE,0);
|
||||
|
||||
if(u8_flow_state > 1 && u8_flow_state != 10) //后续考虑是否需要增加 充电后5秒钟再进行一次计量的判断--计量计费检测
|
||||
{
|
||||
if(s_char_ctrl->u8_billing_flag == 0)
|
||||
{
|
||||
s_char_ctrl->u8_billing_flag = 1; //开始计量计费
|
||||
METERFEE_LOG("[%d]Gun(0-N) metering start\n", u8_gunNo);
|
||||
}
|
||||
|
||||
if(u8_flow_state == 7)
|
||||
s_gun_log->u8_charge_flag = 1; //进入充电中标志
|
||||
|
||||
s_char_ctrl->u32_billing_time = get_current_seconds(); //更新超时起始时间
|
||||
}
|
||||
else
|
||||
{
|
||||
if(u8_flow_state == 0 || u8_flow_state == 10)
|
||||
{
|
||||
if(u32_safe_seconds_since(s_char_ctrl->u32_billing_time) > 7) //处于充电完成状态7S后停止计量,确保最后一次计量完成
|
||||
{
|
||||
if(s_char_ctrl->u8_billing_flag != 0)
|
||||
{
|
||||
s_char_ctrl->u8_billing_flag = 0;
|
||||
v_set_plat_log_flag(u8_gunNo); //设置平台订单上送标志
|
||||
v_meterlog_flash_save_log(1 , u8_gunNo); //设置订单信息存储
|
||||
METERFEE_LOG("[%d]Gun(0-N) metering stop\n", u8_gunNo);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s_char_ctrl->u32_billing_time = get_current_seconds(); //更新超时起始时间
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void v_metering_billing_data_init(void)
|
||||
{
|
||||
memset(&s_metering_ctrl , 0 ,sizeof(S_METERING_BILLING_CTRL));
|
||||
|
||||
s_metering_ctrl.u8_gunCnt = u32_meterlog_get_sys_data(0, E_METERLOG_GET_DATA_GUN_CNT , 0); //获取枪数量
|
||||
|
||||
// 初始化充电订单Flash存储模块
|
||||
v_chg_order_flash_init();
|
||||
//v_chg_order_clear_all();
|
||||
v_unsettled_order_init();
|
||||
v_meterlog_restore_temp_from_eeprom();
|
||||
//Calculate_LOG("充电订单Flash存储模块初始化完成\r\n");
|
||||
}
|
||||
|
||||
void v_meterfee_task(void *arg)
|
||||
{
|
||||
uint16_t sleep_time_ms;
|
||||
uint32_t last_send_time;
|
||||
uint32_t current_time;
|
||||
|
||||
(void)arg;
|
||||
(void)METERFEE_LOCK_INIT();
|
||||
sleep_time_ms = METERFEE_SLEEP_TIME_MS();
|
||||
last_send_time = get_current_seconds();
|
||||
v_metering_billing_data_init();
|
||||
|
||||
for (;;) {
|
||||
METERFEE_FEED_DOG();
|
||||
current_time = get_current_seconds();
|
||||
if ((TASK_RUN_PRINT_TIME != 0U) && (u32_safe_seconds_since(last_send_time) >= TASK_RUN_PRINT_TIME)) {
|
||||
if (METERFEE_LOCK_TAKE() == 1U) {
|
||||
METERFEE_LOG("meter fee task is runing, sleep time is %ums", sleep_time_ms);
|
||||
(void)METERFEE_LOCK_GIVE();
|
||||
}
|
||||
last_send_time = current_time;
|
||||
}
|
||||
|
||||
v_meterlog_action_cheak();
|
||||
v_meterlog_ctrl();
|
||||
|
||||
if (s_metering_ctrl.u16_timer < 0xFF00U) {
|
||||
s_metering_ctrl.u16_timer += sleep_time_ms;
|
||||
} else {
|
||||
s_metering_ctrl.u16_timer = 0U;
|
||||
}
|
||||
|
||||
mSleep(sleep_time_ms);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef CALCULATE_TASK_H
|
||||
#define CALCULATE_TASK_H
|
||||
|
||||
#ifdef __cplusplus /*C++编译环境下兼容C语言*/
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*------ Exported includes(头文件) ----------------------*/
|
||||
#include "main.h"
|
||||
#include "meter_calculate/meter_calculate_impl.h"
|
||||
|
||||
/*------ Exported macro(宏定义) -------------------------*/
|
||||
|
||||
/*------ Exported struct(结构体定义) --------------------*/
|
||||
typedef struct
|
||||
{
|
||||
U8_T u8_billing_flag; //计费标志 0不进行计量计费 1进行计量计费 -- 注意,充电结束后应延时足够的时间,保证电量更新完全
|
||||
U8_T u8_discharge_flag; //充放电标志 0常规充电 1放电
|
||||
U32_T u32_billing_time; //充电结束后计量延时起始时间
|
||||
|
||||
U64_T u64_whm_start_energy; //循环起始电量值 精度 0.0001kW 用于存储上次周期计算电表低值
|
||||
|
||||
/* 充电中临时订单写入节流(秒):上次写入时间戳 */
|
||||
U32_T u32_last_temp_save_sec;
|
||||
}S_CHAR_DATA;
|
||||
typedef struct
|
||||
{
|
||||
U8_T u8_period_time; //当前费率时段 0-95 代表一天15分钟的时段位置
|
||||
U32_T u32_money_rate; //当前电费 单位0.0001元
|
||||
U32_T u32_money_service; //当前服务费 单位0.0001元
|
||||
}S_REAL_FEE_DATA;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
U8_T u8_gunCnt; //枪数量
|
||||
|
||||
U16_T u16_timer; //周期计数
|
||||
|
||||
S_REAL_FEE_DATA s_real_fee_data[NUM_2]; //实时电费信息 - 0代表充电电费 1代表放电电费
|
||||
S_CHAR_DATA s_char_data[GUN_MAX_CNT]; //充电计费 控制信息
|
||||
S_LOG_DATA s_gun_log[GUN_MAX_CNT]; //枪订单信息
|
||||
}S_METERING_BILLING_CTRL;
|
||||
/*------ Exported variables(变量定义) -------------------*/
|
||||
|
||||
/*------ Exported function prototypes (函数声明) -------*/
|
||||
void v_meterfee_task(void *arg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CALCULATE_TASK_H */
|
||||
|
||||
@@ -0,0 +1,583 @@
|
||||
#include "meter_calculate/meter_calculate_impl.h"
|
||||
|
||||
#include "publicdata/publicdata.h"
|
||||
#include "flowctrl/flow_interface.h"
|
||||
#include "bms/bms_interface.h"
|
||||
#include "meter_calculate/meter_coll_task.h"
|
||||
#include "flash_file_mgr/meter_calculate_flash_impl.h"
|
||||
#include "flash_file_mgr/unsettled_order_mng.h"
|
||||
#include "mylog/mylog.h"
|
||||
#include "plat_comm/impl/bs_public_impl.h"
|
||||
|
||||
#include "app_rtc/app_rtc.h"
|
||||
#include "meter_calculate/calculate_task.h"
|
||||
#include <string.h>
|
||||
|
||||
#define METERFEE_LOG(fmt, ...) MYLOG_MSG(TASK_ID_Meterfee, (fmt), ##__VA_ARGS__)
|
||||
|
||||
/*
|
||||
* 说明:
|
||||
* - CCU601E_D 的实现包含订单Flash/EEPROM持久化与平台结算逻辑;
|
||||
* - CCU621_M 当前工程目录未包含对应 flash_file_mgr/unsettled_order_mng 模块,
|
||||
* 因此此处先提供“计量计费核心数据获取 + 故障上报”功能,持久化接口留空实现,
|
||||
* 后续如引入存储模块再补齐。
|
||||
*/
|
||||
|
||||
extern S_METERING_BILLING_CTRL s_metering_ctrl;
|
||||
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 电量计量任务 获取外部数据
|
||||
*
|
||||
* @param[in] u8_gunNo - 枪号 0-N
|
||||
* @param[in] e_dataType - 数据类型 详看枚举定义
|
||||
* @param[in] u16_dataPara - 数据参数
|
||||
*
|
||||
* @return U32_T - 获取数据返回值
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-15
|
||||
* ************************************************************************
|
||||
*/
|
||||
U32_T u32_meterlog_get_sys_data(U8_T u8_gunNo , E_METERLOG_GET_DATA_TYPE e_dataType , U16_T u16_dataPara)
|
||||
{
|
||||
U32_T u32_ret = 0;
|
||||
|
||||
(void)u16_dataPara;
|
||||
|
||||
switch (e_dataType)
|
||||
{
|
||||
case E_METERLOG_GET_DATA_GUN_CNT:
|
||||
u32_ret = g_t_share_data.t_sys_fix_cfg.u8_gunCnt;
|
||||
break;
|
||||
|
||||
case E_METERLOG_GET_DATA_FEE_RATE_DATA: //费率电费信息 抢号代表时段位置 0-95;参数0代表充电费率 1代表放电费率 精度 0.0001元
|
||||
/* 费率数据结构若未初始化,返回 0 */
|
||||
u32_ret = 0;
|
||||
if (u8_gunNo < FEE_MODEL_CNT) {
|
||||
U8_T u8_type = (U8_T)(g_t_share_data.t_sys_var_cfg.s_fee_model.u8_fee_type[u8_gunNo]);
|
||||
if (u8_type > 0) {
|
||||
u8_type = (U8_T)(u8_type - 1U);
|
||||
}
|
||||
if (u8_type >= FEE_MODEL_TYPE_CNT) {
|
||||
u8_type = 0;
|
||||
}
|
||||
u32_ret = (U32_T)(g_t_share_data.t_sys_var_cfg.s_fee_model.s_fee_data[u8_type].u32_rate/10U);
|
||||
}
|
||||
break;
|
||||
|
||||
case E_METERLOG_GET_DATA_FEE_SERVER_DATA: //费率服务费信息 抢号代表时段位置 0-95;参数0代表充电费率 1代表放电费率 精度 0.0001元
|
||||
u32_ret = 0;
|
||||
if (u8_gunNo < FEE_MODEL_CNT) {
|
||||
U8_T u8_type = (U8_T)(g_t_share_data.t_sys_var_cfg.s_fee_model.u8_fee_type[u8_gunNo]);
|
||||
if (u8_type > 0) {
|
||||
u8_type = (U8_T)(u8_type - 1U);
|
||||
}
|
||||
if (u8_type >= FEE_MODEL_TYPE_CNT) {
|
||||
u8_type = 0;
|
||||
}
|
||||
u32_ret = (U32_T)(g_t_share_data.t_sys_var_cfg.s_fee_model.s_fee_data[u8_type].u32_sevice/10U);
|
||||
}
|
||||
break;
|
||||
|
||||
case E_METERLOG_GET_DATA_GUN_SOC: //当前soc值
|
||||
u32_ret = (U32_T)u16_bms_put_data(u8_gunNo, E_BMS_PUT_DATA_GUN_SOC, 0);
|
||||
break;
|
||||
|
||||
case E_METERLOG_GET_DATA_GUN_PUT_VOL: //当前充电电压 精度 0.1V
|
||||
u32_ret = (U32_T)(u64_get_meter_data(u8_gunNo, 0, E_METER_DATA_VOL) / 1000U);
|
||||
break;
|
||||
|
||||
case E_METERLOG_GET_DATA_GUN_PUT_CUR: //当前充电电流 精度 0.1A
|
||||
u32_ret = (U32_T)(u64_get_meter_data(u8_gunNo, 0, E_METER_DATA_CUR) / 1000U);
|
||||
break;
|
||||
|
||||
case E_METERLOG_GET_DATA_GUN_FLOW_STATE: //终端充电状态 0空闲 1充电启动 2绝缘启动 3绝缘计算完成 4绝缘检测模块停止 5开始预充 6预充完成 7充电中 8BMS通讯超时 9充电停止 10充电停止完成
|
||||
u32_ret = (U32_T)u16_flow_put_data(u8_gunNo, E_FLOW_PUT_DATA_GUN_REAL_STATE, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return u32_ret;
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 获取枪电表电量值
|
||||
*
|
||||
* @param[in] u8_gunNo - 枪号 0-n
|
||||
* @param[in] u8_type - 数据类型 0组合有功电量 1正向有功电量 2反向有功电量
|
||||
*
|
||||
* @return U64_T -
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-15
|
||||
* ************************************************************************
|
||||
*/
|
||||
U64_T u64_meterlog_get_meter_data(U8_T u8_gunNo, U8_T u8_type)
|
||||
{
|
||||
U64_T u64_ret = 0;
|
||||
|
||||
/* 当前工程仅提供组合/正向有功电量:统一取 E_METER_DATA_POWER */
|
||||
(void)u8_type;
|
||||
u64_ret = u64_get_meter_data(u8_gunNo, 0, E_METER_DATA_POWER);
|
||||
return u64_ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 设置电量计量异常故障 用于外部触发故障告警
|
||||
*
|
||||
* @param[in] u8_flag - 故障类型 0电表跳变故障 1电量长时间未更新故障 2对时时间不合法故障
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-08-07
|
||||
* ************************************************************************
|
||||
*/
|
||||
void v_meterlog_set_fault(U8_T u8_gunNo, U8_T u8_flag,U8_T u8_fault_flag,U16_T u16_data1,U16_T u16_data2)
|
||||
{
|
||||
E_FAULT_TYPE e_fault_status = (u8_fault_flag != 0U) ? E_FAULT : E_RES;
|
||||
|
||||
if (u8_flag == 0U) {
|
||||
v_set_fault_data((E_FAULT_SITE)((U16_T)E_GUN + (U16_T)u8_gunNo), e_fault_status, E_FAULT_17007, 0U, u16_data1, u16_data2);
|
||||
} else if (u8_flag == 1U) {
|
||||
v_set_fault_data((E_FAULT_SITE)((U16_T)E_GUN + (U16_T)u8_gunNo), e_fault_status, E_FAULT_17008, 0U, u16_data1, u16_data2);
|
||||
} else if (u8_flag == 2U) {
|
||||
v_set_fault_data(E_WHOLE_HOST, e_fault_status, E_FAULT_17009, 0U, u16_data1, u16_data2);
|
||||
} else {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if 1 //对外提供接口
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 启动充电时 赋值订单信息
|
||||
*
|
||||
* @param[in] u8_gunNo - 枪号 0-N
|
||||
* @param[in] s_log_start_data - 启动充电订单数据信息
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-12
|
||||
* ************************************************************************
|
||||
*/
|
||||
void v_meterlog_set_start_char_log_info(U8_T u8_gunNo,S_CHAR_LOG_START_DATA *s_log_start_data)
|
||||
{
|
||||
if(u8_gunNo >= GUN_MAX_CNT ) return;
|
||||
|
||||
S_LOG_DATA *s_gun_log = &s_metering_ctrl.s_gun_log[u8_gunNo];
|
||||
Comm_Time time;
|
||||
GetCurrentTime(&time);
|
||||
|
||||
memset(s_gun_log , 0 ,sizeof(S_LOG_DATA));
|
||||
|
||||
//赋值订单信息
|
||||
s_gun_log->e_start_reason = s_log_start_data->e_start_reason ; //启动充电原因
|
||||
s_gun_log->e_sdk_startType = s_log_start_data->e_sdk_startType; //启动方式 sdk订单上传使用
|
||||
s_gun_log->e_user_type = s_log_start_data->e_user_type ; //用户类型 --积成平台使用
|
||||
s_gun_log->u32_balance_begin = s_log_start_data->u32_balance_begin; ////启动充电前金额 0.0001元
|
||||
|
||||
memcpy(s_gun_log->u8_preTradeNo ,s_log_start_data->u8_preTradeNo, EVS_MAX_TRADE_LEN); //平台交易流水号:8位桩地址+2位枪号+12位时间+补0(ascii码后面补0,相当于结束符)
|
||||
memcpy(s_gun_log->u8_tradeNo ,s_log_start_data->u8_tradeNo, EVS_MAX_TRADE_LEN); //设备交易流水号
|
||||
memcpy(s_gun_log->u8_stop_cmd ,s_log_start_data->u8_stop_cmd, 4); //停机码 4位
|
||||
memcpy(s_gun_log->u8_user_id ,s_log_start_data->u8_user_id, 32); //用户标识
|
||||
memcpy(s_gun_log->u8_vinCode ,s_log_start_data->u8_vinCode, 18); //车辆vin
|
||||
|
||||
//获取初始值
|
||||
memcpy(&s_gun_log->StartChargeTime, &time,sizeof(Comm_Time)); //充电开始时间
|
||||
s_gun_log->u64_startKWh = u64_meterlog_get_meter_data(u8_gunNo,0); //当前电表数据
|
||||
|
||||
s_gun_log->u8_gunNo = u8_gunNo; //抢号信息
|
||||
|
||||
METERFEE_LOG("[%d]Gun(0-N) start order info set\n", u8_gunNo);
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 停止充电时 赋值订单信息
|
||||
*
|
||||
* @param[in] u8_gunNo - 枪号 0-N
|
||||
* @param[in] e_stop_reason - 故障原因
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-12
|
||||
* ************************************************************************
|
||||
*/
|
||||
void v_meterlog_set_stop_char_log_info(U8_T u8_gunNo,E_FAULT_INFO e_stop_reason)
|
||||
{
|
||||
if(u8_gunNo >= GUN_MAX_CNT ) return;
|
||||
|
||||
S_LOG_DATA *s_gun_log = &s_metering_ctrl.s_gun_log[u8_gunNo];
|
||||
Comm_Time time;
|
||||
U8_T i,u8_soc;
|
||||
GetCurrentTime(&time);
|
||||
|
||||
for (i = 0; i < NUM_5; i++)
|
||||
{
|
||||
if(s_gun_log->e_stop_reason[i] == 0)
|
||||
{
|
||||
s_gun_log->e_stop_reason[i] = e_stop_reason;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//获取车辆VIN码
|
||||
v_bms_put_vin_str(u8_gunNo,s_gun_log->u8_vinCode,18);
|
||||
|
||||
//获取结束时间值
|
||||
memcpy(&s_gun_log->EndChargeTime, &time,sizeof(Comm_Time)); //充电开始时间
|
||||
s_gun_log->u64_endKWh = u64_meterlog_get_meter_data(u8_gunNo,0); //当前电表数据
|
||||
u8_soc = u32_meterlog_get_sys_data(u8_gunNo,E_METERLOG_GET_DATA_GUN_SOC,0);
|
||||
if(u8_soc != 0)
|
||||
s_gun_log->u8_endSoc = u8_soc; //SOC
|
||||
|
||||
METERFEE_LOG("[%d]Gun(0-N) stop order info set, stopRsn=%d\n", u8_gunNo, e_stop_reason);
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 对外获取充电订单信息
|
||||
*
|
||||
* @param[in] u8_gunNo - 抢号 0-N
|
||||
* @param[in] s_log - 订单结构体指针
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-22
|
||||
* ************************************************************************
|
||||
*/
|
||||
void v_meterlog_put_char_log_data(U8_T u8_gunNo,S_LOG_DATA *s_log)
|
||||
{
|
||||
memset(s_log , 0, sizeof(S_LOG_DATA));
|
||||
|
||||
if(u8_gunNo > GUN_MAX_CNT) return;
|
||||
|
||||
memcpy(s_log , &s_metering_ctrl.s_gun_log[u8_gunNo],sizeof(S_LOG_DATA));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 设置充电订单信息
|
||||
*
|
||||
* @param[in] u8_gunNo - 抢号 0-N
|
||||
* @param[in] s_log_start_data - 设置订单信息 其中数组数据设置NULL代表无需改动
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-22
|
||||
* ************************************************************************
|
||||
*/
|
||||
void v_meterlog_set_char_log_info(U8_T u8_gunNo, E_METERLOG_SET_LOG_INFO_TYPE e_type , U32_T *u32_data, U8_T *u8_str)
|
||||
{
|
||||
S_LOG_DATA *s_gun_log;
|
||||
|
||||
if (u8_gunNo >= GUN_MAX_CNT) {
|
||||
return;
|
||||
}
|
||||
s_gun_log = &s_metering_ctrl.s_gun_log[u8_gunNo];
|
||||
|
||||
switch (e_type)
|
||||
{
|
||||
case E_METERLOG_SET_LOG_INFO_u32_start_reason : //启动充电原因
|
||||
if (u32_data != NULL) s_gun_log->e_start_reason = (START_REASON)(*u32_data);
|
||||
break;
|
||||
case E_METERLOG_SET_LOG_INFO_u32_sdk_startType: //启动方式 sdk订单上传使用
|
||||
if (u32_data != NULL) s_gun_log->e_sdk_startType = (E_SDK_START_TYPE)(*u32_data);
|
||||
break;
|
||||
case E_METERLOG_SET_LOG_INFO_u32_user_type: //用户类型 --积成平台使用
|
||||
if (u32_data != NULL) s_gun_log->e_user_type = (USER_TYPE_E)(*u32_data);
|
||||
break;
|
||||
case E_METERLOG_SET_LOG_INFO_u32_balance_begin: //启动充电前金额 0.0001元
|
||||
if (u32_data != NULL) s_gun_log->u32_balance_begin = *u32_data;
|
||||
break;
|
||||
case E_METERLOG_SET_LOG_INFO_char_vinCode: //车辆vin
|
||||
if (u8_str != NULL) strncpy((char *)s_gun_log->u8_vinCode, (char *)u8_str, sizeof(s_gun_log->u8_vinCode));
|
||||
break;
|
||||
case E_METERLOG_SET_LOG_INFO_char_user_id: //用户标识
|
||||
if (u8_str != NULL) strncpy((char *)s_gun_log->u8_user_id, (char *)u8_str, sizeof(s_gun_log->u8_user_id));
|
||||
break;
|
||||
case E_METERLOG_SET_LOG_INFO_char_stop_cmd: //停机码 4位
|
||||
if (u8_str != NULL) strncpy((char *)s_gun_log->u8_stop_cmd, (char *)u8_str, sizeof(s_gun_log->u8_stop_cmd));
|
||||
break;
|
||||
case E_METERLOG_SET_LOG_INFO_char_tradeNo: //设备交易流水号
|
||||
if (u8_str != NULL) strncpy((char *)s_gun_log->u8_tradeNo, (char *)u8_str, sizeof(s_gun_log->u8_tradeNo));
|
||||
break;
|
||||
case E_METERLOG_SET_LOG_INFO_char_preTradeNo: //平台交易流水号:
|
||||
if (u8_str != NULL) strncpy((char *)s_gun_log->u8_preTradeNo, (char *)u8_str, sizeof(s_gun_log->u8_preTradeNo));
|
||||
/* StartTransaction 取得 txId 后立即落 EEPROM,避免掉电前 60s 周期未到而丢失流水号 */
|
||||
if (s_metering_ctrl.s_char_data[u8_gunNo].u8_billing_flag != 0U) {
|
||||
(void)v_save_temp_chg_record_to_eeprom(u8_gunNo, s_gun_log, TEMP_CHG_FLAG_UNSETTLED);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 设置充电订单信息上送
|
||||
*
|
||||
* @param[in] u8_gunNo - 抢号 0-N
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-25
|
||||
* ************************************************************************
|
||||
*/
|
||||
void v_set_plat_log_flag(U8_T u8_gunNo)
|
||||
{
|
||||
v_bs_log_txFlag_tradUpload(u8_gunNo);
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief -输出订单中指定数据
|
||||
*
|
||||
* @param[in] u8_gunNo -枪号
|
||||
* @param[in] e_typeData -数据枚举
|
||||
*
|
||||
* @return U32_T -读取的数据
|
||||
*
|
||||
* @author YZG
|
||||
* @date 2025-09-29
|
||||
* ************************************************************************
|
||||
*/
|
||||
U32_T u32_meterlog_put_data(U8_T u8_gunNo,E_METERLOG_PUT_LOG_INFO_TYPE e_typeData ,U8_T *u8_str , U16_T u16_len)
|
||||
{
|
||||
U32_T u32_ret = 0;
|
||||
|
||||
if(u8_str != NULL)
|
||||
memset(u8_str , 0 , u16_len);
|
||||
|
||||
switch (e_typeData)
|
||||
{
|
||||
case E_METERLOG_PUT_LOG_INFO_USED_MONEY: //以充电费金额,精度0.0001元
|
||||
{
|
||||
u32_ret = s_metering_ctrl.s_gun_log[u8_gunNo].u32_used_money;
|
||||
break;
|
||||
}
|
||||
case E_METERLOG_PUT_LOG_INFO_USED_SERVICE: //以充服务费金额,精度0.0001元
|
||||
{
|
||||
u32_ret = s_metering_ctrl.s_gun_log[u8_gunNo].u32_used_service;
|
||||
break;
|
||||
}
|
||||
case E_METERLOG_PUT_LOG_INFO_USED_ENERGY: //以充电量,精度0.0001kWh
|
||||
{
|
||||
u32_ret = s_metering_ctrl.s_gun_log[u8_gunNo].u32_used_energy;
|
||||
break;
|
||||
}
|
||||
case E_METERLOG_PUT_LOG_INFO_START_TYPE: //启动充电方式 START_REASON
|
||||
{
|
||||
u32_ret = s_metering_ctrl.s_gun_log[u8_gunNo].e_start_reason;
|
||||
break;
|
||||
}
|
||||
case E_METERLOG_PUT_LOG_INFO_USER_ID: //用户ID
|
||||
{
|
||||
if(u8_str == NULL) break;
|
||||
|
||||
strncpy((char *)u8_str,(char *)s_metering_ctrl.s_gun_log[u8_gunNo].u8_user_id,u16_len);
|
||||
}
|
||||
case E_METERLOG_PUT_LOG_INFO_CHAR_TIME: //充电开始时间
|
||||
{
|
||||
u32_ret = s_metering_ctrl.s_gun_log[u8_gunNo].u16_ChargedTime;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return u32_ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief -计算下一循环的金额
|
||||
*
|
||||
* @param[in] u8_gunNo -枪号
|
||||
*
|
||||
* @return U32_T -下一循环金额
|
||||
*
|
||||
* @author YZG
|
||||
* @date 2025-10-09
|
||||
* ************************************************************************
|
||||
*/
|
||||
U32_T u32_meterlog_next_cycle_money(U8_T u8_gunNo)
|
||||
{
|
||||
Comm_Time time;
|
||||
U32_T u32_next_cycle_money = 0,u32_out_vol = 0, u32_out_curr = 0; //下个周期金额 精度0.0001元,输出电压 精度0.1V,输出电流 精度0.1A
|
||||
U32_T u32_power = 0,u32_energy = 0; //充电功率,下周期充电电量
|
||||
S_REAL_FEE_DATA *s_real_fee_data = &s_metering_ctrl.s_real_fee_data[s_metering_ctrl.s_char_data[u8_gunNo].u8_discharge_flag]; //获取当前费率信息 默认已经更新正确
|
||||
GetCurrentTime(&time);
|
||||
|
||||
u32_out_vol = u64_get_meter_data(u8_gunNo,0,E_METER_DATA_VOL)/1000; //输出电压,精度0.1
|
||||
u32_out_curr = u64_get_meter_data(u8_gunNo,0,E_METER_DATA_CUR)/1000; //输出电流,精度0.1
|
||||
|
||||
u32_power = u32_out_vol * u32_out_curr /10; //充电功率 精度 0.0001kw(0.01W /10 = 0.0001kw)
|
||||
u32_energy = u32_power/ 3600.0 * 10;//10S充电电能,单位0.0001kwh
|
||||
|
||||
//下一周期金额 精度0.0001元
|
||||
u32_next_cycle_money = (U32_T)(u32_energy *(s_real_fee_data->u32_money_rate + s_real_fee_data->u32_money_service) / 10000);
|
||||
|
||||
//METERFEE_LOG("充电功率:%d,充电电量:%d,预判金额:%d\n",u32_power,u32_energy,u32_next_cycle_money);
|
||||
return u32_next_cycle_money;
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 存储订单操作函数
|
||||
*
|
||||
* @param[in] u8_flag - 0 为数据更新 1为订单结束存储
|
||||
* @param[in] u8_gunNo - 枪号 0-1
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2026-01-19
|
||||
* ************************************************************************
|
||||
*/
|
||||
|
||||
static U8_T u8_meterlog_pre_trade_no_valid(const U8_T *pre)
|
||||
{
|
||||
if (pre == NULL || pre[0] == '\0' || pre[0] == (U8_T)0xFF) {
|
||||
return 0U;
|
||||
}
|
||||
if (atoi((const char *)pre) <= 0) {
|
||||
return 0U;
|
||||
}
|
||||
return 1U;
|
||||
}
|
||||
|
||||
/** 订单结束写 Flash 前:RAM 被复位时从 EEPROM 临时记录补全 txId 等字段 */
|
||||
static void v_meterlog_merge_temp_into_log(U8_T u8_gunNo, S_LOG_DATA *s_gun_log)
|
||||
{
|
||||
S_LOG_DATA temp;
|
||||
|
||||
if (s_gun_log == NULL || u8_meterlog_pre_trade_no_valid(s_gun_log->u8_preTradeNo) != 0U) {
|
||||
return;
|
||||
}
|
||||
if (v_check_temp_chg_record_valid(u8_gunNo) != 1U) {
|
||||
return;
|
||||
}
|
||||
if (v_read_temp_chg_record_from_eeprom(u8_gunNo, &temp, NULL) != 0U) {
|
||||
return;
|
||||
}
|
||||
if (u8_meterlog_pre_trade_no_valid(temp.u8_preTradeNo) == 0U) {
|
||||
return;
|
||||
}
|
||||
memcpy(s_gun_log->u8_preTradeNo, temp.u8_preTradeNo, EVS_MAX_TRADE_LEN);
|
||||
if (s_gun_log->u8_user_id[0] == '\0' && temp.u8_user_id[0] != '\0') {
|
||||
memcpy(s_gun_log->u8_user_id, temp.u8_user_id, sizeof(s_gun_log->u8_user_id));
|
||||
}
|
||||
if (temp.StartChargeTime.iYear != 0 && s_gun_log->StartChargeTime.iYear == 0) {
|
||||
memcpy(&s_gun_log->StartChargeTime, &temp.StartChargeTime, sizeof(Comm_Time));
|
||||
}
|
||||
if (s_gun_log->u64_startKWh == 0U && temp.u64_startKWh != 0U) {
|
||||
s_gun_log->u64_startKWh = temp.u64_startKWh;
|
||||
}
|
||||
METERFEE_LOG("[%d]Gun merge EEPROM temp txId=%s\r\n", u8_gunNo, (char *)s_gun_log->u8_preTradeNo);
|
||||
}
|
||||
|
||||
void v_meterlog_restore_temp_from_eeprom(void)
|
||||
{
|
||||
U8_T gun;
|
||||
|
||||
for (gun = 0U; gun < GUN_MAX_CNT; gun++) {
|
||||
if (v_check_temp_chg_record_valid(gun) != 1U) {
|
||||
continue;
|
||||
}
|
||||
if (v_read_temp_chg_record_from_eeprom(gun, &s_metering_ctrl.s_gun_log[gun], NULL) != 0U) {
|
||||
continue;
|
||||
}
|
||||
METERFEE_LOG("[%d]Gun restore order from EEPROM temp txId=%s\r\n",
|
||||
gun, (char *)s_metering_ctrl.s_gun_log[gun].u8_preTradeNo);
|
||||
}
|
||||
}
|
||||
|
||||
void v_meterlog_flash_save_log(U8_T u8_flag , U8_T u8_gunNo)
|
||||
{
|
||||
if(u8_flag == 1) //订单结束存储
|
||||
{
|
||||
v_meterlog_merge_temp_into_log(u8_gunNo, &s_metering_ctrl.s_gun_log[u8_gunNo]);
|
||||
// 同时保存到Flash存储(双备份)
|
||||
U32_T orderIndex = u32_chg_order_save_record(&s_metering_ctrl.s_gun_log[u8_gunNo]);
|
||||
|
||||
s_metering_ctrl.s_gun_log[u8_gunNo].u32_index = orderIndex;
|
||||
|
||||
// 将订单索引添加到未结算订单列表(默认未收到平台结算)
|
||||
if (orderIndex != 0)
|
||||
{
|
||||
u8_add_unsettled_order(orderIndex);
|
||||
METERFEE_LOG("[%d]Gun(0-N) order add to unsettled, idx=%u\r\n", u8_gunNo, orderIndex);
|
||||
}
|
||||
|
||||
// 清除EEPROM中的临时充电记录(设置为已结算状态)
|
||||
v_clear_temp_chg_record_in_eeprom(u8_gunNo);
|
||||
|
||||
#if FATFS_ENABLE_ORDER
|
||||
// 可选:同时保存到文件系统缓存
|
||||
cache_add_order(&s_metering_ctrl.s_gun_log[u8_gunNo]);
|
||||
METERFEE_LOG("[%d]Gun(0-N) order saved Flash/EEPROM + FS cache, add unsettled\r\n", u8_gunNo);
|
||||
#else
|
||||
METERFEE_LOG("[%d]Gun(0-N) order saved Flash/EEPROM, add unsettled\r\n", u8_gunNo);
|
||||
#endif
|
||||
}
|
||||
else //订单数据更新(充电中周期更新)
|
||||
{
|
||||
// 更新EEPROM中的临时充电记录(设置为未结算状态)
|
||||
v_save_temp_chg_record_to_eeprom(u8_gunNo, &s_metering_ctrl.s_gun_log[u8_gunNo], TEMP_CHG_FLAG_UNSETTLED);
|
||||
|
||||
//METERFEE_LOG("[%d]枪(0-N)充电订单数据已更新到EEPROM临时记录(未结算状态)\r\n", u8_gunNo);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 平台结算确认接口
|
||||
*
|
||||
* 当平台确认订单结算后调用此函数,从未结算订单列表中移除对应的订单索引
|
||||
*
|
||||
* @param[in] orderIndex - 订单索引(由u32_chg_order_save_record返回)
|
||||
* @return U8_T - 0-成功,1-失败(未找到该索引)
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2026-01-19
|
||||
* ************************************************************************
|
||||
*/
|
||||
U8_T u8_platform_settlement_confirm(U32_T orderIndex)
|
||||
{
|
||||
U8_T ret = u8_remove_unsettled_order(orderIndex);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
METERFEE_LOG("Plat settle confirm OK, rm unsettled idx=%u\r\n", orderIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
METERFEE_LOG("Plat settle confirm fail, idx=%u NF\r\n", orderIndex);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 查询订单结算状态接口
|
||||
*
|
||||
* 查询指定订单是否已结算
|
||||
*
|
||||
* @param[in] orderIndex - 订单索引(由u32_chg_order_save_record返回)
|
||||
* @return U8_T - 0-已结算,1-未结算
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2026-01-19
|
||||
* ************************************************************************
|
||||
*/
|
||||
U8_T u8_check_order_settlement_status(U32_T orderIndex)
|
||||
{
|
||||
return u8_check_order_settled(orderIndex);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,181 @@
|
||||
|
||||
#ifndef METER_CALCULATE_IMPL_H
|
||||
#define METER_CALCULATE_IMPL_H
|
||||
|
||||
#ifdef __cplusplus /*C++编译环境下兼容C语言*/
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*------ Exported includes(头文件) ----------------------*/
|
||||
#include "main.h"
|
||||
#include "fault_cheak/fault_interface.h"
|
||||
/*------ Exported macro(宏定义) -------------------------*/
|
||||
#define METER_MAX_ELE (9999999999) //电表最大支持电量 xxxxxx.xxxx kWh
|
||||
#define METER_PERIOD_MAX_ADDED_VAL (10000 * 10) //周期计算电量 最大增量 超过这个值 则认为是数据错误
|
||||
|
||||
#define EVS_MAX_TRADE_LEN (40 + 1) //订单流水号最大长度
|
||||
|
||||
#define Calculate_LOG(fmt, ...) \
|
||||
MYLOG_MSG(TASK_ID_Meterfee,fmt, ##__VA_ARGS__) //任务打印
|
||||
/*------ Exported struct(结构体定义) --------------------*/
|
||||
typedef enum
|
||||
{
|
||||
E_METERLOG_GET_DATA_GUN_CNT = 0, //枪数量
|
||||
E_METERLOG_GET_DATA_FEE_RATE_DATA, //费率电费信息 抢号代表时段位置 0-95;参数0代表充电费率 1代表放电费率 精度 0.0001元
|
||||
E_METERLOG_GET_DATA_FEE_SERVER_DATA, //费率服务费信息 抢号代表时段位置 0-95;参数0代表充电费率 1代表放电费率 精度 0.0001元
|
||||
|
||||
E_METERLOG_GET_DATA_GUN_SOC, //当前soc值
|
||||
E_METERLOG_GET_DATA_GUN_PUT_VOL, //当前充电电压 精度 0.1V
|
||||
E_METERLOG_GET_DATA_GUN_PUT_CUR, //当前充电电流 精度 0.1A
|
||||
|
||||
E_METERLOG_GET_DATA_GUN_FLOW_STATE, //终端充电状态 0空闲 1充电启动 2绝缘启动 3绝缘计算完成 4绝缘检测模块停止 5开始预充 6预充完成 7充电中 8BMS通讯超时 9充电停止 10充电停止完成
|
||||
}E_METERLOG_GET_DATA_TYPE;
|
||||
|
||||
//启动原因
|
||||
typedef enum
|
||||
{
|
||||
CTR_CMD_NONE = 0, //无动作
|
||||
CTR_CMD_CARD_START, //离线卡
|
||||
CTR_CMD_ONCARD_START, //在线卡
|
||||
CTR_CMD_BS_START, //后台启动(tcu/积成)\扫码启动
|
||||
CtR_CMD_VIN_START, //vin启动
|
||||
CTR_CMD_DEBUG, //调试启动
|
||||
}START_REASON;
|
||||
typedef enum //启动方式
|
||||
{
|
||||
E_SDK_STTY_APP = 10, //10:app一键启动
|
||||
E_SDK_STTY_VIN, //11:即插即充
|
||||
E_SDK_STTY_BLUETOOTH, //12:蓝牙离线启动
|
||||
E_SDK_STTY_QR, //13:二维码启动
|
||||
E_SDK_STTY_PILE, //14:桩侧扫码启动(桩自带识别器)
|
||||
E_SDK_STTY_PLAT, //15:平台启动
|
||||
E_SDK_STTY_BLE, //16: 蓝牙启动//
|
||||
E_SDK_STTY_VIN2, //17: 离线 VIN 即插即充
|
||||
}E_SDK_START_TYPE;
|
||||
typedef enum /* 用户类型 */
|
||||
{
|
||||
USER_TYPE_ONLINE_CARD = 0, //在线卡
|
||||
USER_TYPE_APP, //APP用户
|
||||
USER_TYPE_OFFLINE_CARD, //离线卡
|
||||
USER_TYPE_VIN, //VIN
|
||||
USER_TYPE_WECHAT, //微信用户
|
||||
USER_TYPE_ALIPAY, //支付宝
|
||||
USER_TYPE_BAIDU, //百度用户
|
||||
USER_TYPE_CAR_BRAND, //车牌,类似VIN
|
||||
USER_TYPE_LCD, //触摸屏按键启动
|
||||
USER_TYPE_UNICOM, //互联互通用户
|
||||
}USER_TYPE_E;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
START_REASON e_start_reason; //启动充电原因
|
||||
E_SDK_START_TYPE e_sdk_startType; //启动方式 sdk订单上传使用
|
||||
USER_TYPE_E e_user_type; //用户类型 --积成平台使用
|
||||
|
||||
U8_T u8_preTradeNo[EVS_MAX_TRADE_LEN]; //平台交易流水号:8位桩地址+2位枪号+12位时间+补0(ascii码后面补0,相当于结束符)
|
||||
U8_T u8_tradeNo[EVS_MAX_TRADE_LEN]; //设备交易流水号
|
||||
|
||||
U8_T u8_stop_cmd[4]; //停机码 4位
|
||||
U8_T u8_user_id[32]; //用户标识
|
||||
U8_T u8_vinCode[18]; //车辆vin
|
||||
|
||||
U32_T u32_balance_begin; //启动充电前金额 0.0001元
|
||||
}S_CHAR_LOG_START_DATA; //启动充电 赋值订单信息
|
||||
|
||||
typedef struct
|
||||
{
|
||||
U32_T u32_index; //订单索引,用于唯一标识和快速查找
|
||||
|
||||
START_REASON e_start_reason; //启动充电原因
|
||||
E_FAULT_INFO e_stop_reason[NUM_5]; //停止原因
|
||||
|
||||
E_SDK_START_TYPE e_sdk_startType; //启动方式 sdk订单上传使用
|
||||
USER_TYPE_E e_user_type; //用户类型 --积成平台使用
|
||||
|
||||
Comm_Time StartChargeTime; //开始充电时间
|
||||
Comm_Time EndChargeTime; //停止充电时间
|
||||
|
||||
U8_T u8_gunNo; //充电枪编号
|
||||
U8_T u8_charge_flag; //充电标志 是否进入到充电
|
||||
U8_T u8_preTradeNo[EVS_MAX_TRADE_LEN]; //平台交易流水号:8位桩地址+2位枪号+12位时间+补0(ascii码后面补0,相当于结束符)
|
||||
U8_T u8_tradeNo[EVS_MAX_TRADE_LEN]; //设备交易流水号
|
||||
|
||||
U8_T u8_startSoc; //启动时SOC
|
||||
U8_T u8_endSoc; //结束时SOC
|
||||
|
||||
U8_T u8_stop_cmd[4]; //停机码 4位
|
||||
U8_T u8_user_id[32]; //用户标识
|
||||
U8_T u8_vinCode[18]; //车辆vin
|
||||
|
||||
U16_T u16_ChargedTime; //充电时间 min
|
||||
U32_T u32_balance_begin; //启动充电前金额 0.0001元
|
||||
|
||||
U32_T u32_used_money; //己充金额,单位是0.0001元
|
||||
U32_T u32_used_service; //己充服务费,单位是0.0001元
|
||||
|
||||
U64_T u64_startKWh; //电表总起示值 单位是0.0001KWh
|
||||
U64_T u64_endKWh; //电表总止示值 单位是0.0001KWh
|
||||
|
||||
U32_T u32_used_energy; //己用电量,单位是0.0001KWh
|
||||
U32_T u32_part_energy[FEE_MODEL_CNT]; //各时段使用电量,单位是0.0001KWh - 96个
|
||||
}S_LOG_DATA;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
E_METERLOG_SET_LOG_INFO_u32_start_reason = 0, //启动充电原因
|
||||
E_METERLOG_SET_LOG_INFO_u32_sdk_startType, //启动方式 sdk订单上传使用
|
||||
E_METERLOG_SET_LOG_INFO_u32_user_type, //用户类型 --积成平台使用
|
||||
E_METERLOG_SET_LOG_INFO_u32_balance_begin, //启动充电前金额 0.0001元
|
||||
E_METERLOG_SET_LOG_INFO_char_vinCode, //车辆vin
|
||||
E_METERLOG_SET_LOG_INFO_char_user_id, //用户标识
|
||||
E_METERLOG_SET_LOG_INFO_char_stop_cmd, //停机码 4位
|
||||
E_METERLOG_SET_LOG_INFO_char_tradeNo, //设备交易流水号
|
||||
E_METERLOG_SET_LOG_INFO_char_preTradeNo, //平台交易流水号:
|
||||
}E_METERLOG_SET_LOG_INFO_TYPE;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
E_METERLOG_PUT_LOG_INFO_USED_MONEY, //以充电费金额,精度0.0001元
|
||||
E_METERLOG_PUT_LOG_INFO_USED_SERVICE, //以充服务费金额,精度0.0001元
|
||||
E_METERLOG_PUT_LOG_INFO_USED_ENERGY, //以充电量,精度0.0001kWh
|
||||
E_METERLOG_PUT_LOG_INFO_START_TYPE, //启动充电方式 START_REASON
|
||||
E_METERLOG_PUT_LOG_INFO_USER_ID, //用户ID
|
||||
E_METERLOG_PUT_LOG_INFO_CHAR_TIME, //充电时长 单位分钟
|
||||
}E_METERLOG_PUT_LOG_INFO_TYPE;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
E_METER_DATA_POWER = 0, //电表采集电量值 精度 0.0001
|
||||
E_METER_DATA_VOL, //电表采集电压值 精度 0.0001
|
||||
E_METER_DATA_CUR, //电表采集电流值 精度 0.0001
|
||||
}E_METER_DATA_TYPE;
|
||||
|
||||
/*------ Exported variables(变量定义) -------------------*/
|
||||
|
||||
/*------ Exported function prototypes (函数声明) -------*/
|
||||
|
||||
|
||||
U64_T u64_meterlog_get_meter_data(U8_T u8_gunNo ,U8_T u8_type);
|
||||
U32_T u32_meterlog_get_sys_data(U8_T u8_gunNo , E_METERLOG_GET_DATA_TYPE e_dataType , U16_T u16_dataPara);
|
||||
U64_T u64_get_meter_data(U8_T u8_gunNo ,U8_T u8_id , E_METER_DATA_TYPE e_type);
|
||||
|
||||
void v_meterlog_put_char_log_data(U8_T u8_gunNo,S_LOG_DATA *s_log);
|
||||
void v_meterlog_set_char_log_info(U8_T u8_gunNo, E_METERLOG_SET_LOG_INFO_TYPE e_type , U32_T *u32_data, U8_T *u8_str);
|
||||
void v_meterlog_set_start_char_log_info(U8_T u8_gunNo,S_CHAR_LOG_START_DATA *s_log_start_data);
|
||||
void v_meterlog_set_stop_char_log_info(U8_T u8_gunNo,E_FAULT_INFO e_stop_reason);
|
||||
|
||||
void v_set_plat_log_flag(U8_T u8_gunNo);
|
||||
void v_meterlog_set_fault(U8_T u8_gunNo, U8_T u8_flag,U8_T u8_fault_flag,U16_T u16_data1,U16_T u16_data2);
|
||||
U32_T u32_meterlog_put_data(U8_T u8_gunNo,E_METERLOG_PUT_LOG_INFO_TYPE e_typeData,U8_T *u8_str , U16_T u16_len);
|
||||
U32_T u32_meterlog_next_cycle_money(U8_T u8_gunNo);
|
||||
|
||||
void v_meterlog_flash_save_log(U8_T u8_flag , U8_T u8_gunNo);
|
||||
/** 上电后从 EEPROM 临时记录恢复未完成订单到 RAM(掉电续传) */
|
||||
void v_meterlog_restore_temp_from_eeprom(void);
|
||||
U8_T u8_platform_settlement_confirm(U32_T orderIndex);
|
||||
U8_T u8_check_order_settlement_status(U32_T orderIndex);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* METER_CALCULATE_IMPL_H */
|
||||
@@ -0,0 +1,80 @@
|
||||
# meter_calculate 模块说明
|
||||
|
||||
> 路径:`app/meter_calculate/`
|
||||
> 返回主说明:[README](../../README.md)
|
||||
|
||||
## 1. 模块职责
|
||||
|
||||
- 提供电表采集与计量计费两类任务能力:`meter_coll_task` 负责电表数据采集,`meterfee_task` 负责订单计量与费用累计;
|
||||
- 维护充电订单生命周期数据(开始、充电中、结束)并对外提供订单读写接口;
|
||||
- 提供费率查询、周期计量、下周期金额预估能力;
|
||||
- 对接故障系统,处理计量跳变、费率时间异常等故障置位/恢复;
|
||||
- 对接 Flash/EEPROM 订单存储与未结算订单管理,支持掉电续传和平台结算确认。
|
||||
|
||||
## 2. 关键文件
|
||||
|
||||
- `meter_coll_task.c/.h`:电表通信采集任务与底层数据更新;
|
||||
- `calculate_task.c/.h`:计量计费任务(`v_meterfee_task`)及计费控制结构;
|
||||
- `meter_calculate_impl.c/.h`:计量对外接口、订单数据接口、故障与结算接口;
|
||||
- `BSP/flash_file_mgr/meter_calculate_flash_impl.c/.h`:订单正式记录与临时订单 EEPROM/Flash 持久化;
|
||||
- `BSP/flash_file_mgr/unsettled_order_mng.c/.h`:未结算订单索引管理。
|
||||
|
||||
## 3. 功能说明
|
||||
|
||||
### 3.1 任务与调度模型
|
||||
|
||||
- `v_meterfee_task()` 在系统任务表中按周期运行,流程为:
|
||||
- 初始化计量控制对象 `s_metering_ctrl`;
|
||||
- 初始化订单存储模块 `v_chg_order_flash_init()` 与未结算模块 `v_unsettled_order_init()`;
|
||||
- 循环执行 `v_meterlog_action_cheak()`(计费启停状态机)和 `v_meterlog_ctrl()`(周期计费);
|
||||
- 周期喂狗、日志输出与睡眠调度。
|
||||
- 计量主循环按 5 秒节拍进行电量与费用更新,充电中每 60 秒刷新一次 EEPROM 临时订单。
|
||||
|
||||
### 3.2 计量与计费流程
|
||||
|
||||
- 通过 `u64_meterlog_get_meter_data()` 获取电表电量(0.0001kWh 精度);
|
||||
- 首次计量记录起始电量,后续计算循环差分电量并累计到:
|
||||
- 总电量 `u32_used_energy`
|
||||
- 分时段电量 `u32_part_energy[period]`
|
||||
- 电费 `u32_used_money`
|
||||
- 服务费 `u32_used_service`
|
||||
- `v_meterlog_real_fee_updata()` 根据当前时间计算 15 分钟时段索引(0~95),并更新充电/放电费率缓存;
|
||||
- `u32_meterlog_next_cycle_money()` 提供下一周期金额预测(基于当前电压电流和费率)。
|
||||
|
||||
### 3.3 订单生命周期管理
|
||||
|
||||
- 启动充电:`v_meterlog_set_start_char_log_info()` 清零并写入启动订单关键信息(交易号、用户、VIN、起始电量、起始时间);
|
||||
- 充电中:`v_meterlog_log_updata()` 持续更新结束时间、结束电量、SOC、已充时长;
|
||||
- 停止充电:`v_meterlog_set_stop_char_log_info()` 写入停止原因并更新末态数据;
|
||||
- 对外接口:
|
||||
- `v_meterlog_put_char_log_data()` 读取订单完整结构;
|
||||
- `v_meterlog_set_char_log_info()` 按字段更新订单;
|
||||
- `u32_meterlog_put_data()` 按枚举提取关键订单字段。
|
||||
|
||||
### 3.4 存储与结算接口
|
||||
|
||||
- `v_meterlog_flash_save_log(u8_flag, u8_gunNo)`:
|
||||
- `u8_flag=0`:充电中更新 EEPROM 临时订单(未结算标志);
|
||||
- `u8_flag=1`:结束后保存正式订单到 Flash,记录 `u32_index`,加入未结算列表,并清除临时订单。
|
||||
- `u8_platform_settlement_confirm(orderIndex)`:平台确认结算后,从未结算列表移除订单;
|
||||
- `u8_check_order_settlement_status(orderIndex)`:查询订单是否已结算(0 已结算,1 未结算)。
|
||||
|
||||
### 3.5 故障处理
|
||||
|
||||
- 计量相关故障由 `v_meterlog_set_fault()` 统一上报:
|
||||
- `E_FAULT_17007`:电表跳变异常;
|
||||
- `E_FAULT_17008`:电量长时间未更新;
|
||||
- `E_FAULT_17009`:费率/时间异常。
|
||||
- 计费停止后会执行对应恢复逻辑,避免故障状态长期残留。
|
||||
|
||||
## 4. 维护建议
|
||||
|
||||
- 调整计量周期、临时订单保存周期(当前 60 秒)时,需同步评估 EEPROM 擦写寿命与掉电恢复精度;
|
||||
- 修改费率来源时,优先保持 `u32_meterlog_get_sys_data()` 作为统一入口,避免业务层直连全局配置;
|
||||
- 若后续接入平台上送流程,可在 `v_set_plat_log_flag()` 增加上送触发逻辑并和未结算列表闭环验证;
|
||||
- 建议联动验证三类场景:正常停机、异常掉电恢复、平台延迟结算。
|
||||
|
||||
## 5. 关联文档
|
||||
|
||||
- `BSP/flash_file_mgr/flash_file_mgr模块说明.md`
|
||||
- 主索引:[README](../../README.md)
|
||||
@@ -0,0 +1,853 @@
|
||||
#include "meter_calculate/meter_coll_task.h"
|
||||
|
||||
#include "app_init/app_init.h"
|
||||
#include "app_init/task_lock.h"
|
||||
#include "mylog/mylog.h"
|
||||
#include "publicdata/publicdata.h"
|
||||
#include "wdt_task/wdt_task.h"
|
||||
#include "usart.h"
|
||||
#include "fault_cheak/fault_interface.h"
|
||||
|
||||
#define METERCOLL_TASK_ID (TASK_ID_MeterColl)
|
||||
#define METERCOLL_SLEEP_TIME_MS() (MY_GET_SLEEP_TIME(METERCOLL_TASK_ID))
|
||||
#define METERCOLL_LOCK_INIT() (u8_task_lock_ctrl(METERCOLL_TASK_ID, TASK_LOCK_OP_INIT))
|
||||
#define METERCOLL_LOCK_TAKE() (u8_task_lock_ctrl(METERCOLL_TASK_ID, TASK_LOCK_OP_TAKE))
|
||||
#define METERCOLL_LOCK_GIVE() (u8_task_lock_ctrl(METERCOLL_TASK_ID, TASK_LOCK_OP_GIVE))
|
||||
#define METERCOLL_FEED_DOG() (v_wdt_setFlag(METERCOLL_TASK_ID))
|
||||
#define METERCOLL_LOG(fmt, ...) MYLOG_MSG(METERCOLL_TASK_ID, (fmt), ##__VA_ARGS__)
|
||||
|
||||
#define D_Print_flag (0) //printf 打印标志
|
||||
#define D_YaDa_Flag (1) //雅达电表 使用高精度指令不一样 其组合有功电量获取为 00 0E 00 00 XXXXX.XXX
|
||||
|
||||
#define D_FrameStart 0x68 /* 帧头 */
|
||||
#define D_FrameEnd 0x16 /* 帧尾 */
|
||||
//645协议
|
||||
#define D_645_FCReadData 0x11 /* 读实时数据 */
|
||||
#define D_645_FCReadDataResp 0x91 /* 读实时数据正常应答 */
|
||||
|
||||
//645协议标准精度与高精度度数据标识
|
||||
#define DATA_645_ENERGY 0x00000000 //电量(标准)
|
||||
#define DATA_645_VOL 0x00010102 //电压(标准)
|
||||
#define DATA_645_CUR 0x00010202 //电流(标准)
|
||||
|
||||
#define DATA_645_HIGH_CUR 0x00011102 //电流(扩展)
|
||||
#define DATA_645_HIGH_VOL 0x00011002 //电压(扩展帧)
|
||||
|
||||
#if (D_YaDa_Flag == 0)
|
||||
#define DATA_645_HIGH_ENERGY 0x00006200 //电量(高精度)
|
||||
#else
|
||||
#define DATA_645_HIGH_ENERGY 0x00006000 //电量(高精度) 使用正向有功电量获取
|
||||
#endif
|
||||
|
||||
#define DATE_645_DATA_NUM (6) //645协议标准内容数量
|
||||
|
||||
static const Meter_Data_Item st_MeterDataSheet[DATE_645_DATA_NUM] =
|
||||
{
|
||||
//数据标识 字节数 系数
|
||||
{DATA_645_ENERGY, 4, 100, }, //组合电量 (标准) XXXXXX.XX kWh
|
||||
{DATA_645_VOL, 2, 10, }, //电压 (标准) XXX.X V
|
||||
{DATA_645_CUR, 3, 1000, }, //电流(标准)XXX.XXX A
|
||||
|
||||
{DATA_645_HIGH_ENERGY, 5, 10000, }, //组合电量(扩展帧)xxxxxx.xxxx kWh
|
||||
{DATA_645_HIGH_VOL, 3, 100, }, //电压(扩展帧) xxxx.xx V
|
||||
{DATA_645_HIGH_CUR, 4, 10000, }, //电流(扩展帧) xxxx.xxxx A
|
||||
};
|
||||
|
||||
S_METER_CTRL_DATA_DATA s_memter_ctrl; //电表控制任务初始化
|
||||
|
||||
#if SYSTEM_TYPE == SYSTEM_TYPE_CCU601E_D
|
||||
const U8_T u8_commPort[GUN_MAX_CNT] = {E_USART_6,E_USART_5}; //双枪对应通信口
|
||||
#elif SYSTEM_TYPE == SYSTEM_TYPE_CCU621_M
|
||||
const U8_T u8_commPort[GUN_MAX_CNT] = {E_USART_3,E_USART_4}; //双枪对应通信口
|
||||
#else
|
||||
const U8_T u8_commPort[GUN_MAX_CNT] = {E_USART_3,E_USART_4}; //双枪对应通信口
|
||||
#endif
|
||||
|
||||
|
||||
/****************************************************************************系统集成改动部分-开始******************************************************/
|
||||
|
||||
|
||||
#if 1 //接口函数
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 电表数据发送接口函数 发送485数据
|
||||
*
|
||||
* @param[in] u8_gunNo - 枪号 0-1 函数内部自行匹配串口号
|
||||
* @param[in] u8_sendBuf - 发送数据地址
|
||||
* @param[in] u16_len - 发送数据长度
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-08-07
|
||||
* ************************************************************************
|
||||
*/
|
||||
static void v_com_meterSend(U8_T u8_gunNo , U8_T *u8_sendBuf , U16_T u16_len)
|
||||
{
|
||||
if(u8_gunNo >= METER_COMM_MAX_CNT) return;
|
||||
|
||||
u16_usart_send(u8_commPort[u8_gunNo],u8_sendBuf, u16_len);
|
||||
#if D_Print_flag //调试打印
|
||||
if(u8_gunNo == D_Print_flag - 1) {
|
||||
printf("[%d] GUN meter send,len is [%d]\r\n",u8_gunNo , u16_len);
|
||||
printf_hex("--",u8_sendBuf,u16_len);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 电表数据接收接口函数 接收485数据
|
||||
*
|
||||
* @param[in] u8_gunNo - 枪号 0-1 函数内部自行匹配串口号
|
||||
* @param[in] u8_recvBuf - 接收数据地址
|
||||
* @param[in] u16_len - 预期接收数据长度
|
||||
*
|
||||
* @return U16_T - 已接收数据长度
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-08-07
|
||||
* ************************************************************************
|
||||
*/
|
||||
static U16_T u16_com_meterRecv(U8_T u8_gunNo , U8_T *u8_recvBuf ,U16_T u16_len)
|
||||
{
|
||||
U16_T recv_len = 0;
|
||||
if(u8_gunNo >= METER_COMM_MAX_CNT) return 0;
|
||||
|
||||
recv_len = u16_usart_recv(u8_commPort[u8_gunNo],u8_recvBuf,u16_len);
|
||||
|
||||
#if D_Print_flag //调试打印
|
||||
if(u8_gunNo == D_Print_flag - 1) {
|
||||
if(recv_len == 0) return 0;
|
||||
|
||||
printf("[%d] GUN meter recv,len is [%d]\r\n",u8_gunNo , recv_len);
|
||||
printf_hex("--",u8_recvBuf,recv_len);
|
||||
}
|
||||
#endif
|
||||
|
||||
return recv_len;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 1 //对外数据提供 --
|
||||
/**2
|
||||
* ************************************************************************
|
||||
* @brief - 外部获取电表数据
|
||||
*
|
||||
* @param[in] u8_gunNo - 抢号 0-1
|
||||
* @param[in] u8_id - 该枪的电表号 默认0为直流表 1为交流表
|
||||
* @param[in] u8_type - 数据类型 0电量 1电压 2电流
|
||||
*
|
||||
* @return U64_T - 返回值 电压、电流、电量 -精度都为0.0001 4位小数
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-08-07
|
||||
* ************************************************************************
|
||||
*/
|
||||
U64_T u64_get_meter_data(U8_T u8_gunNo ,U8_T u8_id , E_METER_DATA_TYPE e_type)
|
||||
{
|
||||
U64_T u64_ret = 0;
|
||||
S_METER_DATA *s_meter_data =NULL;
|
||||
|
||||
//数据合法性判断
|
||||
if(u8_gunNo >= GUN_MAX_CNT || u8_id >= METER_COMM_MAX_CNT || e_type >= METER_COMM_TYPE_CNT)
|
||||
return u64_ret;
|
||||
|
||||
METERCOLL_LOCK_TAKE(); //数据上锁
|
||||
|
||||
s_meter_data = &s_memter_ctrl.s_meters[u8_gunNo][u8_id]; //获取电表数据地址
|
||||
|
||||
switch (e_type)
|
||||
{
|
||||
case E_METER_DATA_POWER:
|
||||
u64_ret = s_meter_data->u64_energy;
|
||||
break;
|
||||
case E_METER_DATA_VOL:
|
||||
u64_ret = s_meter_data->u32_voltage;
|
||||
break;
|
||||
case E_METER_DATA_CUR:
|
||||
u64_ret = s_meter_data->u32_current;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
METERCOLL_LOCK_GIVE(); //数据解锁
|
||||
|
||||
return u64_ret;
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 获取电表号
|
||||
*
|
||||
* @param[in] u8_gunNo - 枪号 0-N
|
||||
* @param[in] u8_id - 该枪的电表号 默认0为直流表 1为交流表
|
||||
*
|
||||
* @return U8_T - 0未获取 1获取
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-11-27
|
||||
* ************************************************************************
|
||||
*/
|
||||
U8_T u8_get_meter_id_str(U8_T u8_gunNo ,U8_T u8_id ,U8_T *u8_getData , U8_T u8_len)
|
||||
{
|
||||
if(u8_gunNo >= GUN_MAX_CNT || u8_id >= METER_COMM_MAX_CNT)
|
||||
return 0;
|
||||
|
||||
memset(u8_getData, 0, u8_len);
|
||||
strncpy((char *)u8_getData , (char *)&s_memter_ctrl.s_meters[u8_gunNo][u8_id].u8_meterNumber[0] , u8_len);
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 1 //外部数据获取
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 获取电表地址
|
||||
*
|
||||
* @param[in] s_memter_data 电表数据
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-08-06
|
||||
* ************************************************************************
|
||||
*/
|
||||
static void v_meter_get_para(S_METER_CTRL_DATA_DATA *s_memter_data)
|
||||
{
|
||||
U8_T i, k, cnt;
|
||||
S_MEMET_PARA *s_para = &g_t_share_data.t_sys_fix_cfg.s_memter;
|
||||
U8_T gun_cnt = g_t_share_data.t_sys_fix_cfg.u8_gunCnt;
|
||||
|
||||
if (s_memter_data == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((gun_cnt == 0U) || (gun_cnt > GUN_MAX_CNT)) {
|
||||
gun_cnt = GUN_MAX_CNT;
|
||||
}
|
||||
s_memter_data->u8_gunCnt = gun_cnt; // 枪数量
|
||||
|
||||
for (i = 0; i < s_memter_data->u8_gunCnt; i++) {
|
||||
cnt = 0;
|
||||
for (k = 0; k < s_para->u8_meter_num; k++) { //遍历所有电表
|
||||
if (s_para->s_data[k].u8_inGun == (U8_T)(i + 1U)) { //参数设置电表 为本枪 则保存数据
|
||||
if (cnt >= METER_COMM_MAX_CNT) {
|
||||
break; // 单枪电表位已满
|
||||
}
|
||||
|
||||
s_memter_data->s_meters[i][cnt].u8_enFlag = 1; //使能标志 0不使能 1使能 用于区分该位置是否存在电表
|
||||
s_memter_data->s_meters[i][cnt].u8_isAC = s_para->s_data[k].u8_isAC; // 是否为交流电表标识 (1:交流 0:直流)
|
||||
s_memter_data->s_meters[i][cnt].u8_highPrec = s_para->s_data[k].u8_highPrec; //是否支持高精度 0 不支持 1支持
|
||||
|
||||
memcpy((char *)&s_memter_data->s_meters[i][cnt].u8_meterNumber[0],
|
||||
(char *)&s_para->s_data[k].u8_meterAddr[0],
|
||||
6);
|
||||
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 设置电表通信超时状态 用于外部触发故障告警
|
||||
*
|
||||
* @param[in] u8_gunNo - 枪号 0-1
|
||||
* @param[in] u8_id - 该枪电表序号 默认0直流表 1交流表
|
||||
* @param[in] u8_flag - 故障动作 0通信恢复 1 通信超时故障
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-08-07
|
||||
* ************************************************************************
|
||||
*/
|
||||
static void v_meter_set_comm_timeout(U8_T u8_gunNo , U8_T u8_id , U8_T u8_flag)
|
||||
{
|
||||
if(u8_flag == 1)
|
||||
{
|
||||
v_set_fault_data((E_FAULT_SITE)(u8_gunNo + E_GUN), E_FAULT, E_FAULT_13071, 0, u8_id, 0); //电表通讯故障
|
||||
}
|
||||
else
|
||||
v_set_fault_data((E_FAULT_SITE)(u8_gunNo + E_GUN), E_RES, E_FAULT_13071, 0, u8_id, 0); //电表通讯故障
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
/****************************************************************************系统集成改动部分-结束******************************************************/
|
||||
|
||||
/****************************************************************************内部逻辑-开始******************************************************/
|
||||
|
||||
#if 1 //发送控制
|
||||
/*************************************************************
|
||||
函数名称: u8_645_SendFrame(U8_T u8_frm_type, MeterAddress meteraddr, U32_T datesign, U8_T* u8_sendBuf)
|
||||
函数功能: 打包组帧发送645协议命令帧
|
||||
输入参数: u8_frm_type 控制码 meteraddr 电表地址 datesign 数据标识 u8_sendBuf 需要发送的数据
|
||||
输出参数:
|
||||
**************************************************************/
|
||||
static void v_645_SendFrame(S_METER_FRAME *s_meterFrame) //组帧发送
|
||||
{
|
||||
U8_T u8_cnt = 0;
|
||||
U8_T CheckSum;
|
||||
U8_T u8_sendBuf[NUM_64] = {0};
|
||||
|
||||
u8_sendBuf[u8_cnt++] = D_FrameStart; //帧起始域
|
||||
memcpy(&u8_sendBuf[u8_cnt], &s_meterFrame->address[0], 6);
|
||||
u8_cnt += 6;
|
||||
u8_sendBuf[u8_cnt++] = D_FrameStart; //帧起始域
|
||||
u8_sendBuf[u8_cnt++] = s_meterFrame->control_code; //控制码 0x11 读数据
|
||||
u8_sendBuf[u8_cnt++] = 0x04; //数据长度
|
||||
// 数据标识(拆分为高字节和低字节)
|
||||
u8_sendBuf[u8_cnt++] = ((s_meterFrame->u32_datesign >> 24) & 0xFF) + 0x33;
|
||||
u8_sendBuf[u8_cnt++] = ((s_meterFrame->u32_datesign >> 16) & 0xFF) + 0x33;
|
||||
u8_sendBuf[u8_cnt++] = ((s_meterFrame->u32_datesign >> 8) & 0xFF) + 0x33;
|
||||
u8_sendBuf[u8_cnt++] = ((s_meterFrame->u32_datesign) & 0xFF) + 0x33;
|
||||
/* 打包校验和与帧尾 */
|
||||
CheckSum = u16_crc_checksum(u8_sendBuf, u8_cnt);
|
||||
u8_sendBuf[u8_cnt++] = CheckSum;
|
||||
u8_sendBuf[u8_cnt++] = D_FrameEnd;
|
||||
|
||||
v_com_meterSend(s_meterFrame->u8_gunNo,u8_sendBuf, u8_cnt);
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 电表数据采集发送组帧函数
|
||||
*
|
||||
* @param[in] u8_gunNo - 枪地址索引号 0-1
|
||||
* @param[in] u8_type - 0 电量 1电压 2电流
|
||||
* @param[in] s_meter_data - 电表数据
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-08-06
|
||||
* ************************************************************************
|
||||
*/
|
||||
static void v_memter_SendFrame(U8_T u8_gunNo, U8_T u8_type , S_METER_DATA *s_meter_data)
|
||||
{
|
||||
S_METER_FRAME s_meterFrame;
|
||||
|
||||
memset(&s_meterFrame , 0, sizeof(S_METER_FRAME));
|
||||
|
||||
s_meterFrame.u8_gunNo = u8_gunNo;
|
||||
s_meterFrame.control_code = D_645_FCReadData; //控制码 0x11 读数据
|
||||
memcpy(&s_meterFrame.address[0], s_meter_data->u8_meterNumber, 6);
|
||||
|
||||
if(u8_type % 3 == 0){ //电量
|
||||
if(s_meter_data->u8_highPrec == 1) //高精度
|
||||
s_meterFrame.u32_datesign = DATA_645_HIGH_ENERGY;
|
||||
else
|
||||
s_meterFrame.u32_datesign = DATA_645_ENERGY;
|
||||
}
|
||||
else if(u8_type % 3 == 1){ //电压
|
||||
if(s_meter_data->u8_highPrec == 1) //高精度
|
||||
s_meterFrame.u32_datesign = DATA_645_HIGH_VOL;
|
||||
else
|
||||
s_meterFrame.u32_datesign = DATA_645_VOL;
|
||||
}
|
||||
else if(u8_type % 3 == 2){ //电流
|
||||
if(s_meter_data->u8_highPrec == 1) //高精度
|
||||
s_meterFrame.u32_datesign = DATA_645_HIGH_CUR;
|
||||
else
|
||||
s_meterFrame.u32_datesign = DATA_645_CUR;
|
||||
}
|
||||
|
||||
//printf("send ctrl , gun(%d) id(0x%08x) \n",s_meterFrame.u8_gunNo ,s_meterFrame.u32_datesign );
|
||||
v_645_SendFrame(&s_meterFrame);
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 电表数据采集发送控制函数 用于对两路电表分别进行电压、电流、电量的数据采集发送--
|
||||
*
|
||||
* @param[in] u16_sleepTime - 任务睡眠时间
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-08-06
|
||||
* ************************************************************************
|
||||
*/
|
||||
static void v_meter_send_ctrl(U16_T u16_sleepTime)
|
||||
{
|
||||
U8_T i;
|
||||
S_METER_DATA *s_meter_data =NULL;
|
||||
S_METER_COMM_CTRL *s_comm_ctrl = NULL;
|
||||
|
||||
for (i = 0; i < s_memter_ctrl.u8_gunCnt; i++) //遍历所有枪数量
|
||||
{
|
||||
s_comm_ctrl = &s_memter_ctrl.s_commCtrl[i]; //获取通信控制内容
|
||||
s_meter_data = &s_memter_ctrl.s_meters[i][s_comm_ctrl->u8_send_type/METER_COMM_TYPE_CNT]; //获取该枪电表首地址 当send type 大于3时,则为2枪数据
|
||||
|
||||
if(s_meter_data->u8_enFlag == 0) //不存在电表 则不执行; 当电表2不存在时,send type从0开始执行
|
||||
{
|
||||
s_comm_ctrl->u8_send_type = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(s_comm_ctrl->u8_recv_flag == 0) //未存在 等待接收数据
|
||||
{
|
||||
v_memter_SendFrame(i,s_comm_ctrl->u8_send_type, s_meter_data); //控制数据发送
|
||||
|
||||
if(++s_comm_ctrl->u8_send_type >= METER_COMM_TYPE_CNT*2) //发送数据类型 递增
|
||||
{
|
||||
s_comm_ctrl->u8_send_type = 0;
|
||||
}
|
||||
|
||||
s_comm_ctrl->u8_recv_flag = 1; //发送后等待数据接收
|
||||
s_comm_ctrl->u16_recv_timeout_cnt = 0; //超时计数清空
|
||||
}
|
||||
else //等待接收状态 判断超时
|
||||
{
|
||||
s_comm_ctrl->u16_recv_timeout_cnt += u16_sleepTime;
|
||||
if(s_comm_ctrl->u16_recv_timeout_cnt >= METER_RECV_TIMEOUT_MS) //数据接收超时时间 600ms ,则清空标识 重新进行发送
|
||||
{
|
||||
s_comm_ctrl->u8_recv_flag = 0;
|
||||
s_comm_ctrl->u16_recv_timeout_cnt = 0; //超时计数清空
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 1 //接收解析
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 电表数据内容解析 并存储; 查找接收数据对应的枪号、表号、数据内容等
|
||||
*
|
||||
* @param[in] s_meterFrame - 接收报文解析后的数据包-默认可用数据为正确数据
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-08-07
|
||||
* ************************************************************************
|
||||
*/
|
||||
static void v_meter_unpack(S_METER_FRAME *s_meterFrame)
|
||||
{
|
||||
U8_T i,id;
|
||||
U64_T u64_unpackData = 0,u64_recvData=0;
|
||||
S_METER_DATA *s_meter = NULL;
|
||||
|
||||
//printf_hex("解析成功////data",s_meterFrame->data,s_meterFrame->data_len);
|
||||
|
||||
|
||||
//前4个字节 转换成 数据标识
|
||||
s_meterFrame->u32_datesign = 0;
|
||||
for (i = 0; i < NUM_4; i++)
|
||||
{
|
||||
s_meterFrame->u32_datesign += ((s_meterFrame->data[3-i]- 0x33) << i*8);
|
||||
}
|
||||
|
||||
//遍历所有可解析数据标识 查询对应标识
|
||||
for (id = 0; id < DATE_645_DATA_NUM; id++)
|
||||
{
|
||||
if(s_meterFrame->u32_datesign == st_MeterDataSheet[id].iFlagCode)
|
||||
break;
|
||||
}
|
||||
|
||||
//查找失败,则退出
|
||||
if(id >= DATE_645_DATA_NUM){
|
||||
// printf("find fail , 0x%08x\n",s_meterFrame->u32_datesign);
|
||||
return;
|
||||
}
|
||||
|
||||
//长度一致性检查
|
||||
if(st_MeterDataSheet[id].iLenth != s_meterFrame->data_len-4){
|
||||
// printf("len fail , 0x%08x\n",s_meterFrame->u32_datesign);
|
||||
return;
|
||||
}
|
||||
|
||||
//将报文数据 -0x33 BCD转int 组成整形数据
|
||||
for (i = 0; i < st_MeterDataSheet[id].iLenth; i++)
|
||||
{
|
||||
U8_T u8_data = (s_meterFrame->data[i+4]- 0x33);
|
||||
u64_recvData = (u64_recvData << 8);
|
||||
u64_recvData += u8_data;
|
||||
}
|
||||
|
||||
//将BCD码 转换成 十进制 -- u64 8字节
|
||||
for (i = 0; i < st_MeterDataSheet[id].iLenth; i++)
|
||||
{
|
||||
// 提取当前BCD数字(4位)
|
||||
U8_T u8_data = (u64_recvData >> (8 * i))&0xFF;
|
||||
U8_T digit = bcd_to_dec(u8_data);
|
||||
|
||||
// 累积结果(每次乘以10并加上新数字)
|
||||
u64_unpackData = u64_unpackData * 100 + digit;
|
||||
}
|
||||
|
||||
//电表号赋值 -- 一路单电表时,可以不设置电表号 --当一路多电表使用时,电表号为已经设置好-因此此处仅对0地址电表进行赋值
|
||||
s_meter = &s_memter_ctrl.s_meters[s_meterFrame->u8_gunNo][0];
|
||||
if(s_meter->u8_meterNumber[0] == 0xAA && s_meter->u8_meterNumber[1] == 0xAA){
|
||||
memcpy(&s_meter->u8_meterNumber[0] , &s_meterFrame->address[0] , 6);
|
||||
|
||||
//凯弦欣能电表特殊处理 //打印
|
||||
if(s_meterFrame->u8_gunNo == 1)
|
||||
{
|
||||
if(s_meter->u8_meterNumber[0] == 0x01 &&
|
||||
s_meter->u8_meterNumber[1] == 0x00 &&
|
||||
s_meter->u8_meterNumber[2] == 0x00 &&
|
||||
s_meter->u8_meterNumber[3] == 0x00 &&
|
||||
s_meter->u8_meterNumber[4] == 0x00 &&
|
||||
s_meter->u8_meterNumber[5] == 0x00
|
||||
)
|
||||
{
|
||||
s_meter->u8_meterNumber[0] = 2;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//printf("u64_unpackData is %lld\n", u64_unpackData);
|
||||
METERCOLL_LOCK_TAKE(); // 数据上锁
|
||||
|
||||
//查找电表号一致的电表 - 赋值对应内容
|
||||
for (i = 0; i < METER_COMM_MAX_CNT; i++)
|
||||
{
|
||||
s_meter = &s_memter_ctrl.s_meters[s_meterFrame->u8_gunNo][i];
|
||||
//if(strncmp((char *)s_meter->u8_meterNumber , (char *)s_meterFrame->address , 6) == 0) //电表号相等
|
||||
if (memcmp(s_meter->u8_meterNumber, s_meterFrame->address, 6) == 0)
|
||||
{
|
||||
// printf("find ok , gun(%d) id(0x%08x) data(%lld)\n",s_meterFrame->u8_gunNo ,s_meterFrame->u32_datesign , u64_unpackData);
|
||||
//存储电压、电流、电量值的精度 都是0.0001 四位小数,因此根据数据系数做统一转换
|
||||
switch (st_MeterDataSheet[id].iFlagCode)
|
||||
{
|
||||
case DATA_645_ENERGY : //0x00000000 //电量(标准) XXXXXX.XX kWh
|
||||
case DATA_645_HIGH_ENERGY : //0x00006200 //电量(高精度) xxxxxx.xxxx kWh
|
||||
{
|
||||
if(st_MeterDataSheet[id].iCoefficient != 0)
|
||||
s_meter->u64_energy = u64_unpackData *(10000 / st_MeterDataSheet[id].iCoefficient) ; //电量值(单位:0.0001 kWh)
|
||||
s_meter->u16_timeout_cnt[0] = 0;
|
||||
break;
|
||||
}
|
||||
case DATA_645_VOL : //0x00010102 //电压(标准) XXX.X V
|
||||
case DATA_645_HIGH_VOL : //0x00011002 //电压(扩展帧 xxxx.xx V
|
||||
{
|
||||
if(st_MeterDataSheet[id].iCoefficient != 0)
|
||||
s_meter->u32_voltage = u64_unpackData *(10000 / st_MeterDataSheet[id].iCoefficient) ; //电压值(单位:0.0001V)
|
||||
s_meter->u16_timeout_cnt[1] = 0;
|
||||
break;
|
||||
}
|
||||
case DATA_645_CUR : //0x00010202 //电流(标准) XXX.XXX A
|
||||
case DATA_645_HIGH_CUR : //0x00011102 //电流(扩展) xxxx.xxxx A
|
||||
{
|
||||
if(st_MeterDataSheet[id].iCoefficient != 0)
|
||||
s_meter->u32_current = u64_unpackData *(10000 / st_MeterDataSheet[id].iCoefficient) ; //电流值(单位:0.0001 A)
|
||||
s_meter->u16_timeout_cnt[2] = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
METERCOLL_LOCK_GIVE(); //数据解锁
|
||||
|
||||
//接收标识清空
|
||||
s_memter_ctrl.s_commCtrl[s_meterFrame->u8_gunNo].u8_recv_flag = 0; //数据接收标识 已清空
|
||||
|
||||
|
||||
// if(s_meterFrame->u8_gunNo == 1)
|
||||
// printf("unpack is ok , gun(%d) id(0x%08x) data(%lld)\n",s_meterFrame->u8_gunNo ,s_meterFrame->u32_datesign , u64_unpackData);
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 接收电表数据解帧函数 - 内部包括多包、残包等异常数据内容的处理方法
|
||||
*
|
||||
* @param[in] new_data - 新接收数据内容
|
||||
* @param[in] new_len - 新接收数据内容长度
|
||||
* @param[in] s_frame - 电表数据帧结构体指针 用于存储解析后的电表数据
|
||||
*
|
||||
* @return U8_T - 0解析失败 1解析成功
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-08-07
|
||||
* ************************************************************************
|
||||
*/
|
||||
static U8_T u8_meter_recvFrame(const U8_T *new_data, U8_T new_len,S_METER_FRAME *s_frame)
|
||||
{
|
||||
S_METER_COMM_CTRL *s_ctrl = &s_memter_ctrl.s_commCtrl[s_frame->u8_gunNo];
|
||||
|
||||
// 1. 检查输入有效性
|
||||
if (!new_data || new_len <= 0 || !s_frame) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 2. 添加新数据到缓冲区
|
||||
int free_space = MAX_BUFFER_SIZE - s_ctrl->u8_buffer_len;
|
||||
int copy_len = (new_len > free_space) ? free_space : new_len;
|
||||
|
||||
if (copy_len > 0) {
|
||||
memcpy(s_ctrl->u8_buffer + s_ctrl->u8_buffer_len, new_data, copy_len);
|
||||
s_ctrl->u8_buffer_len += copy_len;
|
||||
}
|
||||
|
||||
// 3. 查找有效帧(最小帧长为12字节)
|
||||
if (s_ctrl->u8_buffer_len < 12) {
|
||||
return 0; // 数据不足
|
||||
}
|
||||
|
||||
// 4. 尝试查找帧起始位置
|
||||
int frame_start = -1;
|
||||
int frame_end = -1;
|
||||
|
||||
for (int i = 0; i <= s_ctrl->u8_buffer_len - 12; i++) {
|
||||
|
||||
// 检查帧起始标识(跳过前导符)
|
||||
int start_idx = i;
|
||||
if (s_ctrl->u8_buffer[start_idx] != D_FrameStart) continue;
|
||||
|
||||
// 检查地址后的帧起始符(位于start_idx+7)
|
||||
if (start_idx + 7 >= s_ctrl->u8_buffer_len) continue;
|
||||
if (s_ctrl->u8_buffer[start_idx + 7] != D_FrameStart) continue;
|
||||
|
||||
// 检查数据长度域(位于start_idx+9)
|
||||
if (start_idx + 9 >= s_ctrl->u8_buffer_len) continue;
|
||||
|
||||
// 检查帧完整长度
|
||||
frame_end = start_idx + 11 + s_ctrl->u8_buffer[start_idx + 9]; // 包含结束符的位置
|
||||
if (frame_end >= s_ctrl->u8_buffer_len) continue;
|
||||
|
||||
// 检查帧结束符(0x16)
|
||||
if (s_ctrl->u8_buffer[frame_end] != D_FrameEnd) continue;
|
||||
|
||||
frame_start = start_idx;
|
||||
break;
|
||||
}
|
||||
|
||||
// 5. 没有找到完整帧
|
||||
if (frame_start < 0) {
|
||||
// 缓冲区已满但无有效帧,清除部分数据
|
||||
if (s_ctrl->u8_buffer_len >= MAX_BUFFER_SIZE / 2) {
|
||||
// 保留最后1/4的数据
|
||||
int keep_len = MAX_BUFFER_SIZE / 4;
|
||||
if (s_ctrl->u8_buffer_len > keep_len) {
|
||||
memmove(s_ctrl->u8_buffer, s_ctrl->u8_buffer + (s_ctrl->u8_buffer_len - keep_len), keep_len);
|
||||
s_ctrl->u8_buffer_len = keep_len;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 6. 计算校验和
|
||||
|
||||
uint8_t calc_checksum = u16_crc_checksum(&s_ctrl->u8_buffer[frame_start] , frame_end - frame_start - 1);
|
||||
if (calc_checksum != s_ctrl->u8_buffer[frame_end - 1]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 8. 解包帧数据
|
||||
// 地址域(小端格式)
|
||||
memcpy(s_frame->address, s_ctrl->u8_buffer + frame_start + 1, 6);
|
||||
|
||||
// 控制码
|
||||
s_frame->control_code = s_ctrl->u8_buffer[frame_start + 8];
|
||||
|
||||
// 数据域长度和内容
|
||||
s_frame->data_len = s_ctrl->u8_buffer[frame_start + 9];
|
||||
s_frame->data_len = (s_frame->data_len > 0xFF) ? 0xFF : s_frame->data_len; // 安全限制
|
||||
if (s_frame->data_len > 0) {
|
||||
memcpy(s_frame->data, s_ctrl->u8_buffer + frame_start + 10, s_frame->data_len);
|
||||
}
|
||||
|
||||
// // 计算完整帧长度(包括前导符)
|
||||
// s_frame->data_len = (frame_end - frame_start + 1);
|
||||
|
||||
// 9. 移除已处理数据
|
||||
int process_len = (frame_end + 1);
|
||||
s_ctrl->u8_buffer_len -= process_len;
|
||||
|
||||
if (s_ctrl->u8_buffer_len > 0) {
|
||||
memmove(s_ctrl->u8_buffer, s_ctrl->u8_buffer + process_len, s_ctrl->u8_buffer_len);
|
||||
}
|
||||
else
|
||||
memset(s_ctrl->u8_buffer, 0 , MAX_BUFFER_SIZE);
|
||||
|
||||
return s_frame->data_len; // 返回处理的字节数
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 电表数据接收函数 遍历接收两个枪号的电表数据
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-08-07
|
||||
* ************************************************************************
|
||||
*/
|
||||
static void v_memter_recv_ctrl()
|
||||
{
|
||||
U8_T i,u8_readLen,u8_recvBuf[NUM_128];
|
||||
S_METER_FRAME s_meterFrame;
|
||||
|
||||
for (i = 0; i < METER_COMM_MAX_CNT; i++)
|
||||
{
|
||||
memset(u8_recvBuf , 0 , NUM_128);
|
||||
u8_readLen = u16_com_meterRecv(i,u8_recvBuf , NUM_128);
|
||||
if(u8_readLen > 0)
|
||||
{
|
||||
memset(&s_meterFrame , 0, sizeof(S_METER_FRAME));
|
||||
|
||||
s_meterFrame.u8_gunNo = i;
|
||||
if(u8_meter_recvFrame(u8_recvBuf , u8_readLen, &s_meterFrame ) != 0)
|
||||
{
|
||||
//printf("gun[%d] data_len is %d\n", i, s_meterFrame.data_len);
|
||||
if(s_meterFrame.data_len >= 6) //数据有效 否则可能为错误数据 最小长度为7
|
||||
{
|
||||
v_meter_unpack(&s_meterFrame);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 1 //通信超时
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 通信超时检查函数 超时计数累加,接收数据时会清空计数,由此判断接受超时
|
||||
*
|
||||
* @param[in] u16_sleepTime - 任务执行周期 ms
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-08-07
|
||||
* ************************************************************************
|
||||
*/
|
||||
static void v_meter_timeout_cheak(U16_T u16_sleepTime)
|
||||
{
|
||||
U8_T i,k;
|
||||
U16_T u16_max_timeout_cnt;
|
||||
S_METER_DATA *s_meter_data =NULL;
|
||||
|
||||
for (i = 0; i < s_memter_ctrl.u8_gunCnt; i++) //遍历所有枪数量
|
||||
{
|
||||
for (k = 0; k < METER_COMM_MAX_CNT; k++) //遍历所有电表位置
|
||||
{
|
||||
s_meter_data = &s_memter_ctrl.s_meters[i][k]; //获取该枪电表首地址
|
||||
if(s_meter_data->u8_enFlag == 0) //电表不存在 则不判断超时
|
||||
continue;
|
||||
|
||||
if (s_meter_data->u16_timeout_cnt[0] >= s_meter_data->u16_timeout_cnt[1] &&
|
||||
s_meter_data->u16_timeout_cnt[0] >= s_meter_data->u16_timeout_cnt[2]) {
|
||||
u16_max_timeout_cnt = s_meter_data->u16_timeout_cnt[0];
|
||||
} else if (s_meter_data->u16_timeout_cnt[1] >= s_meter_data->u16_timeout_cnt[0] &&
|
||||
s_meter_data->u16_timeout_cnt[1] >= s_meter_data->u16_timeout_cnt[2]) {
|
||||
u16_max_timeout_cnt = s_meter_data->u16_timeout_cnt[1];
|
||||
} else {
|
||||
u16_max_timeout_cnt = s_meter_data->u16_timeout_cnt[2];
|
||||
}
|
||||
|
||||
if(s_meter_data->u8_comm_flag == 0) //未通信 , 判通信恢复
|
||||
{
|
||||
if(u16_max_timeout_cnt < METER_TIMEOUT_CNT)
|
||||
{
|
||||
s_meter_data->u8_comm_flag = 1;
|
||||
v_meter_set_comm_timeout(i,k,0); //触发电表通信恢复
|
||||
METERCOLL_LOG("gun[%d] id[%d], comm is ok\n", i,k);
|
||||
// printf("gun[%d] id[%d], comm is ok\n", i,k);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(u16_max_timeout_cnt < METER_TIMEOUT_CNT)
|
||||
{
|
||||
s_meter_data->u16_timeout_cnt[0] += u16_sleepTime;
|
||||
s_meter_data->u16_timeout_cnt[1] += u16_sleepTime;
|
||||
s_meter_data->u16_timeout_cnt[2] += u16_sleepTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
s_meter_data->u8_comm_flag = 0;
|
||||
//memset(s_meter_data, 0 ,sizeof(S_METER_DATA)); //不能全部清空 仅情况采集值即可
|
||||
s_meter_data->u32_voltage =0;
|
||||
s_meter_data->u32_current =0;
|
||||
s_meter_data->u64_energy =0;
|
||||
v_meter_set_comm_timeout(i,k,1); //触发电表通信超时故障
|
||||
METERCOLL_LOG("gun[%d] id[%d], comm is fail\n", i,k);
|
||||
// printf("gun[%d] id[%d], comm is fail\n", i,k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 电表数据初始化 初始化数据,并获取外部数据
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-08-07
|
||||
* ************************************************************************
|
||||
*/
|
||||
static void v_memter_data_init()
|
||||
{
|
||||
U8_T i,k;
|
||||
S_METER_DATA *s_meter_data =NULL;
|
||||
|
||||
memset(&s_memter_ctrl , 0 ,sizeof(S_METER_CTRL_DATA_DATA));
|
||||
v_meter_get_para(&s_memter_ctrl);
|
||||
|
||||
for (i = 0; i < s_memter_ctrl.u8_gunCnt; i++) //遍历所有枪数量
|
||||
{
|
||||
for (k = 0; k < METER_COMM_MAX_CNT; k++) //遍历所有电表位置
|
||||
{
|
||||
s_meter_data = &s_memter_ctrl.s_meters[i][k]; //获取该枪电表首地址
|
||||
if(s_meter_data->u8_enFlag == 0) //电表不存在 则不判断超时
|
||||
continue;
|
||||
|
||||
s_meter_data->u8_comm_flag = 1;
|
||||
s_meter_data->u16_timeout_cnt[0] = 0;//0xFFFF; //电表默认通信超时
|
||||
s_meter_data->u16_timeout_cnt[1] = 0;
|
||||
s_meter_data->u16_timeout_cnt[2] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 电表数据采集函数 - 循环执行数据发生、数据接收、超时判断函数
|
||||
*
|
||||
* @param[in] p -
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-08-07
|
||||
* ************************************************************************
|
||||
*/
|
||||
void v_Meter_Coll_task(void *argument)
|
||||
{
|
||||
uint16_t sleep_time_ms;
|
||||
uint32_t last_send_time;
|
||||
uint32_t current_time;
|
||||
|
||||
(void)argument;
|
||||
(void)METERCOLL_LOCK_INIT();
|
||||
sleep_time_ms = METERCOLL_SLEEP_TIME_MS();
|
||||
last_send_time = get_current_seconds();
|
||||
v_memter_data_init();
|
||||
|
||||
for (;;) {
|
||||
METERCOLL_FEED_DOG();
|
||||
current_time = get_current_seconds();
|
||||
if ((TASK_RUN_PRINT_TIME != 0U) && (u32_safe_seconds_since(last_send_time) >= TASK_RUN_PRINT_TIME)) {
|
||||
if (METERCOLL_LOCK_TAKE() == 1U) {
|
||||
METERCOLL_LOG("meter coll task is runing, sleep time is %ums", sleep_time_ms);
|
||||
(void)METERCOLL_LOCK_GIVE();
|
||||
}
|
||||
last_send_time = current_time;
|
||||
}
|
||||
|
||||
v_memter_recv_ctrl(); //电表数据接收
|
||||
|
||||
|
||||
v_meter_send_ctrl(sleep_time_ms); //电表数据发生控制
|
||||
|
||||
v_meter_timeout_cheak(sleep_time_ms); //电表通信超时检查
|
||||
|
||||
mSleep(sleep_time_ms);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
#ifndef METER_COLL_TASK_H
|
||||
#define METER_COLL_TASK_H
|
||||
|
||||
#ifdef __cplusplus /*C++编译环境下兼容C语言*/
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*------ Exported includes(头文件) ----------------------*/
|
||||
#include "main.h"
|
||||
#include "publicdata/publicdata.h"
|
||||
#include "meter_calculate/meter_calculate_impl.h"
|
||||
/*------ Exported macro(宏定义) -------------------------*/
|
||||
#ifndef METER_MAX_CNT
|
||||
#define METER_MAX_CNT (4) //电表最大数量
|
||||
#endif
|
||||
|
||||
#ifndef METER_COMM_TYPE_CNT
|
||||
#define METER_COMM_TYPE_CNT (3) //电表通信数据类型数量 3分别为电量、电压、电流
|
||||
#endif
|
||||
|
||||
#ifndef METER_RECV_TIMEOUT_MS
|
||||
#define METER_RECV_TIMEOUT_MS (600) //电表数据接收超时时间 600ms
|
||||
#endif
|
||||
|
||||
#ifndef METER_COMM_MAX_CNT
|
||||
#define METER_COMM_MAX_CNT (2) //单路通信口 电表最大数量 - 一路485口最大有几个电表
|
||||
#endif
|
||||
|
||||
#define MAX_BUFFER_SIZE (128)
|
||||
/*------ Exported struct(结构体定义) --------------------*/
|
||||
|
||||
// 第三级:电表控制数据
|
||||
typedef struct {
|
||||
U8_T u8_enFlag; //使能标志 0不使能 1使能 用于区分该位置是否存在电表
|
||||
U8_T u8_isAC; // 是否为交流电表标识 (1:交流 0:直流)
|
||||
U8_T u8_highPrec; //是否支持高精度 0 不支持 1支持
|
||||
|
||||
U8_T u8_meterNumber[6]; // 6字节电表表号 (如: 0x12,0x34,0x56,0x78,0x9A,0xBC)
|
||||
|
||||
U8_T u8_comm_flag; //通信标识 0未通信 1已通信
|
||||
U16_T u16_timeout_cnt[3]; //通信超时计数 -- 三种数据 有一种接收超时则通信超时
|
||||
|
||||
U32_T u32_voltage; // 电压值(单位:0.0001V)
|
||||
U32_T u32_current; // 电流值(单位:0.0001A)
|
||||
U64_T u64_energy; // 电量值(单位:0.0001 kWh)
|
||||
} S_METER_DATA;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
U8_T u8_send_type; //发送数据类型 0电量 1电压 2电流
|
||||
U8_T u8_recv_flag; //数据接收标志 0未接收到 1已接收到
|
||||
|
||||
U16_T u16_recv_timeout_cnt; //接收超时计数 单位ms 最大超时时间 600ms
|
||||
|
||||
U8_T u8_buffer_len; // 当前缓冲区数据长度
|
||||
U8_T u8_buffer[MAX_BUFFER_SIZE]; // 静态接收缓冲区
|
||||
}S_METER_COMM_CTRL;
|
||||
|
||||
typedef struct {
|
||||
U8_T u8_gunCnt; //枪数量
|
||||
|
||||
S_METER_COMM_CTRL s_commCtrl[GUN_MAX_CNT];
|
||||
S_METER_DATA s_meters[GUN_MAX_CNT][METER_COMM_MAX_CNT]; // 电表控制数据数组(固定2个元素)
|
||||
} S_METER_CTRL_DATA_DATA;
|
||||
|
||||
// 解包结果结构体
|
||||
typedef struct {
|
||||
U8_T u8_gunNo; //枪号
|
||||
U8_T data_len; // 数据长度
|
||||
U8_T control_code; // 控制码
|
||||
|
||||
U32_T u32_datesign; //数据标识
|
||||
U8_T address[NUM_6]; // 电表地址(小端格式)
|
||||
U8_T data[NUM_32]; // 数据域
|
||||
} S_METER_FRAME;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
U32_T iFlagCode; //标识编码
|
||||
U16_T iLenth; //数据长度,如果为组合项,则为0
|
||||
U16_T iCoefficient; //系数
|
||||
} Meter_Data_Item;
|
||||
|
||||
|
||||
void v_Meter_Coll_task(void *argument);
|
||||
U8_T u8_get_meter_id_str(U8_T u8_gunNo ,U8_T u8_id ,U8_T *u8_getData , U8_T u8_len);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user