#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 #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); } }