230 lines
9.1 KiB
C
230 lines
9.1 KiB
C
/*****************************************************************************
|
||
* @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(¤t_time); // 获取当前时间戳
|
||
timeinfo = localtime(¤t_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(¤t_time); // 获取当前时间
|
||
timeinfo = localtime(¤t_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;
|
||
} |