ems/ems_c/logic/logic_peakvalley.c

230 lines
9.1 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*****************************************************************************
* @copyright 2024-202, . POWER SUPPLY CO., LTD.
* @file logic_peakvalley.c
* @brief 削峰填谷控制策略
* @author Gary
* @date 2024/09/21
* @remark 初修订
*****************************************************************************/
#include "logic_comm.h"
// 削峰填谷参数数据库配置
peakvalley_zone_tab_t zone_tab_obj = {0}; // 定义一个结构体变量
peakvalley_zone_tab_t* zone_tab = &zone_tab_obj; // 使指针指向该变量
/*****************************************************************************
* @brief 将日期字符串转换为月份和日的整数表示
* @param[in] char* dateStr 日期字符串,例如 "MM-DD"
* @param[out] int* month 指向月份的指针
* @param[out] int* day 指向日期的指针
* @return 0 成功 其它值:失败
*****************************************************************************/
static int parseDate(const char* dateStr, int* month, int* day)
{
int result = sscanf(dateStr, "%d-%d", month, day); // 解析字符串获取月份和日期
return (result == 2) ? 0 : -1; // 返回0表示成功其他值表示失败
}
/*****************************************************************************
* @brief 获取削峰平谷实时运行参数接口
* @param[in] pv_time_config_t *ret_params(功率计划时间表) u_int16_t slotTabLen (实际表长)
* @return 目标功率
*****************************************************************************/
static float_t getPeakValleyPower(pv_time_config_t* ret_params, u_int16_t slotTabLen)
{
// 获取当前时间
time_t current_time;
struct tm* timeinfo;
time(&current_time); // 获取当前时间戳
timeinfo = localtime(&current_time); // 将时间戳转换为当地时间
uint32_t now_seconds = timeinfo->tm_hour * 3600 + timeinfo->tm_min * 60 + timeinfo->tm_sec; // 计算当前秒数
// EMS要求最小的时间间隔不得小于15分钟换算之后一天不可大于96个时段
if (slotTabLen > MAX_TIME_SEGMENTS)
{
// 检查传入的时间段表长度是否合法
KITLOG(LOG_LOGIC_EN, WARN_EN, "获取时段表的长度非法 len:%d\n", slotTabLen);
return 0; // 返回0表示未能获取有效功率
}
for (int loop = 0; loop < slotTabLen; loop++)
{
// 跳过开始时间和结束时间相同的时间段
if (ret_params[loop].startTime == ret_params[loop].endTime)
{
continue;
}
// 检查当前时间是否在时间段内
if (now_seconds >= ret_params[loop].startTime &&
now_seconds <= ret_params[loop].endTime)
{
return ret_params[loop].power; // 返回当前时间段的功率值
}
}
// 未命中任意时段则停机返回0
return 0;
}
/*****************************************************************************
* @brief 判断日期是否在给定时间段内
* @param[in] int dateMonth当前月
* @param[in] int dateDay当前日
* @param[in] const char* startDateStr开始日期字符串
* @param[in] const char* endDateStr结束日期字符串
* @return 1 在时间段内 0 不在时间段内 -1参数异常解析失败
*****************************************************************************/
static int isDateInRange(int dateMonth, int dateDay, const char* startDateStr, const char* endDateStr)
{
if (NULL == startDateStr || NULL == endDateStr)
{
// 检查开始和结束日期指针是否为空
KITLOG(LOG_LOGIC_EN, ERROR_EN, "入参指针为空");
return -1; // 返回-1表示参数异常
}
int startMonth, startDay, endMonth, endDay;
if (0 != parseDate(startDateStr, &startMonth, &startDay))
{
// 解析开始日期字符串失败
KITLOG(LOG_LOGIC_EN, ERROR_EN, "开始日期字符串匹配失败");
return -1; // 返回-1表示参数解析失败
}
if (0 != parseDate(endDateStr, &endMonth, &endDay))
{
// 解析结束日期字符串失败
KITLOG(LOG_LOGIC_EN, ERROR_EN, "结束日期字符串匹配失败");
return -1; // 返回-1表示参数解析失败
}
// 日期保护,确保开始日期在结束日期之前
if (startMonth > endMonth || (startMonth == endMonth && startDay > endDay))
{
return 0; // 返回0表示不在范围内
}
// 判断是否在日期内
if (dateMonth > startMonth || (dateMonth == startMonth && dateDay >= startDay))
{
if (dateMonth < endMonth || (dateMonth == endMonth && dateDay <= endDay))
{
return 1; // 返回1表示在时间段内
}
}
return 0; // 返回0表示不在范围内
}
/*****************************************************************************
* @brief 更新时区时段表
* @return 0 更新成功 -1 失败
*****************************************************************************/
int logic_peakValleyUpdate()
{
UT_array* pvDateConfigs;
// 清空之前的时段表
if (zone_tab->peakItem != NULL)
{
for (int j = 0; j < zone_tab->zoneTabLen; ++j)
{
if (zone_tab->peakItem[j].timeCfgTab != NULL)
free(zone_tab->peakItem[j].timeCfgTab);
zone_tab->peakItem[j].timeCfgTab = NULL;
}
free(zone_tab->peakItem);
zone_tab->peakItem = NULL;
}
// 从数据库获取新的削峰填谷时段表数据
if (kit_get_pv_date_cfg_db_data(&pvDateConfigs) != 0)
{
KITLOG(LOG_LOGIC_EN, ERROR_EN, "获取削峰填谷时段表失败");
return 1; // 返回错误码
}
// 获取数据个数
size_t numConfigs = utarray_len(pvDateConfigs);
// 检查时段表长度是否合法
if (MAX_DATA_SEGMENTS < numConfigs || numConfigs <= 0)
{
KITLOG(LOG_LOGIC_EN, ERROR_EN, "时区表表长异常: %zu", numConfigs);
utarray_free(pvDateConfigs); // 释放资源
return 1; // 返回错误码
}
// 分配空间
zone_tab->peakItem = (pv_date_config_t*)calloc(numConfigs, sizeof(pv_date_config_t));
if (zone_tab->peakItem == NULL)
{
KITLOG(LOG_LOGIC_EN, ERROR_EN, "内存分配失败");
utarray_free(pvDateConfigs); // 释放资源
return 1; // 返回错误码
}
// 更新日期配置个数
zone_tab->zoneTabLen = numConfigs;
// 遍历并复制数据 使用 memcpy 整体复制结构体
for (int i = 0; i < numConfigs; ++i)
{
pv_date_config_t* srcConfig = (pv_date_config_t*)utarray_eltptr(pvDateConfigs, i);
pv_date_config_t* dstConfig = &zone_tab->peakItem[i]; // 直接访问数组元素
// 复制 timeCfgTab 前先分配内存
dstConfig->timeCfgTab = (pv_time_config_t*)calloc(srcConfig->timeCfgLen, sizeof(pv_time_config_t));
if (dstConfig->timeCfgTab == NULL)
{
KITLOG(LOG_LOGIC_EN, ERROR_EN, "内存分配失败");
// 释放已分配的内存
for (int j = 0; j < i; ++j)
{
free(zone_tab->peakItem[j].timeCfgTab);
}
free(zone_tab->peakItem);
utarray_free(pvDateConfigs); // 释放资源
return 1; // 返回错误码
}
memcpy(dstConfig, srcConfig, sizeof(pv_date_config_t)); // 整体复制结构体
memcpy(dstConfig->timeCfgTab, srcConfig->timeCfgTab, srcConfig->timeCfgLen * sizeof(pv_time_config_t));
}
utarray_free(pvDateConfigs); // 释放 UT_array 资源
return 0; // 返回成功
}
/*****************************************************************************
* @brief 获取削峰平谷实时运行参数接口
* @param[in] bool sign 时间表是否更新标志
* @return 0 成功 -1 失败
*****************************************************************************/
int logicFun_peakValley()
{
// 获取任务参数
peakvalley_params_t* task_params = &stlogic.para_delivery.task_params.pkvly;
time_t current_time;
struct tm* timeinfo;
time(&current_time); // 获取当前时间
timeinfo = localtime(&current_time); // 转换为当地时间
// 获取日期
int month = timeinfo->tm_mon + 1; // 月份从0开始需要加1
int day = timeinfo->tm_mday; // 天
if (zone_tab == NULL)
{
// 检查时段表指针是否为空
KITLOG(LOG_LOGIC_EN, ERROR_EN, "入参指针为空");
return -1; // 返回-1表示失败
}
for (int loop = 0; loop < zone_tab->zoneTabLen; loop++)
{
// 检查当前日期是否在时段表中
if (1 == isDateInRange(month, day, (char*)zone_tab->peakItem[loop].startDate, (char*)zone_tab->peakItem[loop].endDate))
{
// 获取对应时间段的功率配置
task_params->power = getPeakValleyPower(zone_tab->peakItem[loop].timeCfgTab, zone_tab->peakItem[loop].timeCfgLen);
return 0; // 返回0表示成功
}
}
// 没找到合适的日期返回0
task_params->power = 0;
return 0;
}