Files

583 lines
22 KiB
C

#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