添加单点补招pqdif文件的逻辑

This commit is contained in:
lnk
2026-07-17 16:25:44 +08:00
parent abc5f9e735
commit 86dc51d066

View File

@@ -103,6 +103,7 @@ static void format_time_ms(long long ms, char* buf, size_t buf_len){
} }
static apr_status_t ChannelCheckPQDIFFiles(chnl_usr_t *chnl_usr); static apr_status_t ChannelCheckPQDIFFiles(chnl_usr_t *chnl_usr);
static apr_status_t ChannelRecallPQDIFFiles(chnl_usr_t *chnl_usr, LD_info_t *LD_info, long long start_sec, long long end_sec);
//lnk20250122start //lnk20250122start
apr_status_t init_rem_dib_table() apr_status_t init_rem_dib_table()
{ {
@@ -580,21 +581,28 @@ void ChannelCheckIECLogs(chnl_usr_t *chnl_usr)
LD_info_t *LD_info; LD_info_t *LD_info;
loginfo_t *loginfo = NULL; loginfo_t *loginfo = NULL;
int cpuno; int cpuno;
int is_pqdif_process;
double now; double now;
static double last_check_recall_config_time = 0.0; static double last_check_recall_config_time = 0.0;
ied = chnl_usr->chnl->ied; ied = chnl_usr->chnl->ied;
ied_usr = GET_IEDEXT_ADDR(ied); ied_usr = GET_IEDEXT_ADDR(ied);
is_pqdif_process = (g_node_id == PQDIF_DATA_BASE_NODE_ID);
for(cpuno=0 ; cpuno<ied->cpucount; cpuno++) { for(cpuno=0 ; cpuno<ied->cpucount; cpuno++) {
LD_info = &(ied_usr->LD_info[cpuno]); LD_info = &(ied_usr->LD_info[cpuno]);
//添加保护,防止台账中测点号不连续导致的崩溃 //添加保护,防止台账中测点号不连续导致的崩溃
if (LD_info->cpuno == 0 || if (LD_info->cpuno == 0 ||
LD_info->LD_name == NULL || LD_info->LD_name == NULL)
LD_info->logcount <= 0 || {
LD_info->loginfo == NULL) continue;
}
if (!is_pqdif_process &&
(LD_info->logcount <= 0 ||
LD_info->loginfo == NULL))
{ {
continue; continue;
} }
@@ -620,11 +628,13 @@ void ChannelCheckIECLogs(chnl_usr_t *chnl_usr)
continue; continue;
}*/ }*/
if (!is_pqdif_process) {
loginfo = LD_info->loginfo[0] ; loginfo = LD_info->loginfo[0] ;
//添加保护,防止台账中测点号不连续导致的崩溃 //添加保护,防止台账中测点号不连续导致的崩溃
if (loginfo == NULL || loginfo->LD_info == NULL) if (loginfo == NULL || loginfo->LD_info == NULL)
continue; continue;
}
apr_sleep(apr_time_from_sec(1) / 10); apr_sleep(apr_time_from_sec(1) / 10);
@@ -636,14 +646,17 @@ void ChannelCheckIECLogs(chnl_usr_t *chnl_usr)
int failed_count = 0; int failed_count = 0;
/////////////////////////////////////////////////////根据配置文件控制下发补招时间为北京时间还是utc时间 /////////////////////////////////////////////////////根据配置文件控制下发补招时间为北京时间还是utc时间
long long utc_or_beijing = 0;
if (!is_pqdif_process) {
printf("~~~~~~~this dev type is %s~~~~~~~",ied_usr->dev_type); printf("~~~~~~~this dev type is %s~~~~~~~",ied_usr->dev_type);
XmlConfigC cfg1; XmlConfigC cfg1;
memset(&cfg1,0,sizeof(cfg1));
if (get_xml_config_by_dev_type(ied_usr->dev_type, &cfg1)) { if (get_xml_config_by_dev_type(ied_usr->dev_type, &cfg1)) {
printf("ValueOfTimeUnit = %s\n", cfg1.ValueOfTimeUnit); printf("ValueOfTimeUnit = %s\n", cfg1.ValueOfTimeUnit);
} else { } else {
printf("读取失败,未找到 dev_type\n"); printf("读取失败,未找到 dev_type\n");
} }
long long utc_or_beijing; utc_or_beijing = 0;
if(strcmp(cfg1.ValueOfTimeUnit, "utc") == 0){//装置时间是utc还是北京 if(strcmp(cfg1.ValueOfTimeUnit, "utc") == 0){//装置时间是utc还是北京
utc_or_beijing = 28800;//秒 utc_or_beijing = 28800;//秒
@@ -654,6 +667,11 @@ void ChannelCheckIECLogs(chnl_usr_t *chnl_usr)
DIY_WARNLOG_CODE(LD_info->mp_id,2,LOG_CODE_RECALL,"【WARN】监测点:%s - id:%s开始补招数据,注意!下发补招时间为beijing时间,监测点对应装置型号:%s", LD_info->name,LD_info->mp_id,ied_usr->dev_type); DIY_WARNLOG_CODE(LD_info->mp_id,2,LOG_CODE_RECALL,"【WARN】监测点:%s - id:%s开始补招数据,注意!下发补招时间为beijing时间,监测点对应装置型号:%s", LD_info->name,LD_info->mp_id,ied_usr->dev_type);
} }
}
else {
DIY_WARNLOG_CODE(LD_info->mp_id,2,LOG_CODE_RECALL,"[PQDIF][RECALL] monitor:%s - id:%s start recall PQDIF files, dev_type:%s", LD_info->name,LD_info->mp_id,ied_usr->dev_type);
}
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
//记录本次补招的时间范围 //记录本次补招的时间范围
long long min_start_sec = LLONG_MAX; long long min_start_sec = LLONG_MAX;
@@ -674,6 +692,9 @@ void ChannelCheckIECLogs(chnl_usr_t *chnl_usr)
LD_info->autorecallflag = 1;//正在补招 LD_info->autorecallflag = 1;//正在补招
//当前不区分稳态和暂态lnk20241030如果做区分修改Check_Recall_Config从xml文件获取数据后赋值给了LD_info //当前不区分稳态和暂态lnk20241030如果做区分修改Check_Recall_Config从xml文件获取数据后赋值给了LD_info
if (is_pqdif_process)
continue;
loginfo->need_steady = LD_info->autorecall[i]->need_steady; loginfo->need_voltage = LD_info->autorecall[i]->need_voltage; loginfo->need_steady = LD_info->autorecall[i]->need_steady; loginfo->need_voltage = LD_info->autorecall[i]->need_voltage;
loginfo->start_time = apr_time_from_sec(LD_info->autorecall[i]->start - 5);//保证时间开头 loginfo->start_time = apr_time_from_sec(LD_info->autorecall[i]->start - 5);//保证时间开头
@@ -726,6 +747,24 @@ void ChannelCheckIECLogs(chnl_usr_t *chnl_usr)
//不管是否成功这个补招文件必须删除可能出现一直失败循环读取文件和循环补招导致程序崩溃202050724lnk //不管是否成功这个补招文件必须删除可能出现一直失败循环读取文件和循环补招导致程序崩溃202050724lnk
//if (failed_count==0) {//成功 //if (failed_count==0) {//成功
if (is_pqdif_process) {
if (min_start_sec == LLONG_MAX || max_end_sec <= min_start_sec) {
failed_count++;
printf("[PQDIF][RECALL] invalid recall range mp_id=%s min=%lld max=%lld\n",
LD_info->mp_id, min_start_sec, max_end_sec);
}
else {
ret = ChannelRecallPQDIFFiles(chnl_usr, LD_info, min_start_sec, max_end_sec);
if (ret != APR_SUCCESS)
failed_count++;
}
g_dead_lock_counter = 0;
g_thread_blocked_times = 0;
now = sGetMsTime();
last_check_recall_config_time = now;
}
Delete_recall_Xml(LD_info->mp_id); Delete_recall_Xml(LD_info->mp_id);
// ===== [新增] 组装上送的补招开始/结束时间字符串(本地时区,直接“秒转字符串”)===== // ===== [新增] 组装上送的补招开始/结束时间字符串(本地时区,直接“秒转字符串”)=====
@@ -1652,7 +1691,8 @@ void CheckAllConnectedChannel()
if ( (g_node_id == SOE_COMTRADE_BASE_NODE_ID) || (g_node_id == HIS_DATA_BASE_NODE_ID) || (g_node_id == NEW_HIS_DATA_BASE_NODE_ID) || (g_node_id == RECALL_HIS_DATA_BASE_NODE_ID) || (g_node_id == RECALL_ALL_DATA_BASE_NODE_ID)) if ( (g_node_id == SOE_COMTRADE_BASE_NODE_ID) || (g_node_id == HIS_DATA_BASE_NODE_ID) || (g_node_id == NEW_HIS_DATA_BASE_NODE_ID) || (g_node_id == RECALL_HIS_DATA_BASE_NODE_ID) || (g_node_id == RECALL_ALL_DATA_BASE_NODE_ID))
ChannelCheckWaveFiles(chnl_usr);//录波文件 ChannelCheckWaveFiles(chnl_usr);//录波文件
if(g_node_id == HIS_DATA_BASE_NODE_ID || g_node_id == NEW_HIS_DATA_BASE_NODE_ID || g_node_id == RECALL_HIS_DATA_BASE_NODE_ID || (g_node_id == RECALL_ALL_DATA_BASE_NODE_ID)) if(g_node_id == HIS_DATA_BASE_NODE_ID || g_node_id == NEW_HIS_DATA_BASE_NODE_ID || g_node_id == RECALL_HIS_DATA_BASE_NODE_ID || (g_node_id == RECALL_ALL_DATA_BASE_NODE_ID)
|| g_node_id == PQDIF_DATA_BASE_NODE_ID)
ChannelCheckIECLogs(chnl_usr);//补招文件 ChannelCheckIECLogs(chnl_usr);//补招文件
if ( (sGetMsTime() - chnl_usr->m_LastPosRespTime) > 15*1000 ) //wait 15 secs隔15秒获取一次响应两次没响应后关闭 if ( (sGetMsTime() - chnl_usr->m_LastPosRespTime) > 15*1000 ) //wait 15 secs隔15秒获取一次响应两次没响应后关闭
@@ -2063,42 +2103,60 @@ static void pqdif_make_time_match(int cpu_no, time_t tm_value, char* out, size_t
tm_local.tm_hour); tm_local.tm_hour);
} }
static int pqdif_match_recent_interval(const char* filename, int cpu_no) static int pqdif_use_interval_end_time(const char* dev_type)
{
return (dev_type != NULL && strcmp(dev_type, "PS_NET_PQDIF") == 0);
}
static time_t pqdif_align_interval_start(long long sec)
{ {
time_t now_time;
time_t hour_floor; time_t hour_floor;
time_t interval_end; struct tm tm_local;
time_t interval_start;
struct tm tm_now;
char start_match[64];
char end_match[64];
int hour_mod; int hour_mod;
memset(&tm_local, 0, sizeof(tm_local));
hour_floor = (time_t)sec;
localtime_r(&hour_floor, &tm_local);
tm_local.tm_min = 0;
tm_local.tm_sec = 0;
hour_floor = mktime(&tm_local);
hour_mod = tm_local.tm_hour % PQDIF_AUTO_INTERVAL_HOURS;
return hour_floor - hour_mod * 3600;
}
static int pqdif_match_interval(const char* filename, int cpu_no, time_t interval_start, const char* dev_type)
{
time_t name_time;
char time_match[64];
if (!pqdif_has_pqd_suffix(filename)) if (!pqdif_has_pqd_suffix(filename))
return FALSE; return FALSE;
if (cpu_no <= 0) if (cpu_no <= 0)
return FALSE; return FALSE;
now_time = time(NULL); /* C# 原逻辑按设备类型决定文件名时间:
localtime_r(&now_time, &tm_now); * - 普通设备用周期开始时间;
tm_now.tm_min = 0; * - PS_NET_PQDIF 用周期结束时间。
tm_now.tm_sec = 0; * 这里保持同样规则,避免把普通设备下一周期的开始文件误认为本周期结束文件。
hour_floor = mktime(&tm_now);
/* 最近一个完整周期:
* 例如当前 11:xx、周期 2 小时,则完整周期是 08:00~10:00。
* 普通设备按周期开始时间命名PS_NET_PQDIF 振兴设备按周期结束时间命名。
* 这里同时匹配开始和结束时间,避免需要额外区分厂家。
*/ */
hour_mod = tm_now.tm_hour % PQDIF_AUTO_INTERVAL_HOURS; name_time = interval_start;
interval_end = hour_floor - hour_mod * 3600; if (pqdif_use_interval_end_time(dev_type))
interval_start = interval_end - PQDIF_AUTO_INTERVAL_HOURS * 3600; name_time += PQDIF_AUTO_INTERVAL_HOURS * 3600;
pqdif_make_time_match(cpu_no, interval_start, start_match, sizeof(start_match)); pqdif_make_time_match(cpu_no, name_time, time_match, sizeof(time_match));
pqdif_make_time_match(cpu_no, interval_end, end_match, sizeof(end_match)); return strstr(filename, time_match) != NULL;
}
return (strstr(filename, start_match) != NULL || strstr(filename, end_match) != NULL); static int pqdif_match_recent_interval(const char* filename, int cpu_no, const char* dev_type)
{
time_t now_time;
time_t interval_start;
now_time = time(NULL);
interval_start = pqdif_align_interval_start((long long)now_time) - PQDIF_AUTO_INTERVAL_HOURS * 3600;
return pqdif_match_interval(filename, cpu_no, interval_start, dev_type);
} }
static int pqdif_ensure_local_dir() static int pqdif_ensure_local_dir()
@@ -2199,6 +2257,141 @@ static int pqdif_get_file_from_device(chnl_usr_t *chnl_usr, const char *rem_file
return SD_SUCCESS; return SD_SUCCESS;
} }
static apr_status_t ChannelRecallPQDIFFiles(chnl_usr_t *chnl_usr, LD_info_t *LD_info, long long start_sec, long long end_sec)
{
ied_t *ied;
ied_usr_t *ied_usr;
char **filenames = NULL;
int filenum = 0;
int ret;
int file_idx;
int fetched = 0;
int existed = 0;
int missing = 0;
int failed = 0;
time_t interval_start;
time_t interval_step;
if (chnl_usr == NULL || chnl_usr->chnl == NULL || chnl_usr->chnl->ied == NULL ||
chnl_usr->chnl->ied->usr_ext == NULL || chnl_usr->net_info == NULL ||
LD_info == NULL) {
printf("[PQDIF][RECALL] invalid context, skip\n");
return APR_EGENERAL;
}
if (LD_info->cpuno == 0 || LD_info->LD_name == NULL || LD_info->mp_id[0] == '\0') {
printf("[PQDIF][RECALL] invalid LD_info, skip\n");
return APR_EGENERAL;
}
if (end_sec <= start_sec) {
printf("[PQDIF][RECALL] invalid time range mp_id=%s start=%lld end=%lld\n",
LD_info->mp_id, start_sec, end_sec);
return APR_EGENERAL;
}
ied = chnl_usr->chnl->ied;
ied_usr = (ied_usr_t*)ied->usr_ext;
if (pqdif_ensure_local_dir() != SD_SUCCESS) {
echo_warn1("[PQDIF][RECALL] create local dir failed: %s\n", PQDIF_LOCAL_DIR);
return APR_EGENERAL;
}
/* 单点补招与 C# 手动/补招逻辑保持一致:
* 1. 先读取装置 /PQDIF/ 目录;
* 2. 按 PQDIF_AUTO_INTERVAL_HOURS 将补招时间范围切成多个文件周期;
* 3. 普通设备用周期开始时间匹配文件名PS_NET_PQDIF 用周期结束时间匹配文件名;
* 4. 每个周期只取一份匹配文件,下载接口直接调用 mms_getFile。
*/
ret = mms_mvla_fdir(chnl_usr->net_info,(ST_CHAR*)PQDIF_REMOTE_DIR,
3*g_pt61850app->mmsOpTimeout,&filenames,&filenum,g_pt61850app->tmp_pool);
if (ret != SD_SUCCESS) {
echo_warn2("[PQDIF][RECALL] mms_mvla_fdir failed, IED=%d IP=%s\n", ied->id, chnl_usr->ip_str);
return APR_EGENERAL;
}
if (filenames == NULL || filenum <= 0) {
printf("[PQDIF][RECALL] no file in remote dir %s, mp_id=%s IP=%s\n",
PQDIF_REMOTE_DIR, LD_info->mp_id, chnl_usr->ip_str);
return APR_EGENERAL;
}
interval_step = PQDIF_AUTO_INTERVAL_HOURS * 3600;
interval_start = pqdif_align_interval_start(start_sec);
printf("[PQDIF][RECALL] begin mp_id=%s cpuno=%d range=%lld~%lld interval_start=%lld files=%d dev_type=%s\n",
LD_info->mp_id, LD_info->cpuno, start_sec, end_sec, (long long)interval_start,
filenum, ied_usr->dev_type);
while ((long long)interval_start < end_sec) {
int found_this_interval = FALSE;
for (file_idx = 0; file_idx < filenum; file_idx++) {
const char *base_name;
char remote_filename[256];
char only_filename_ret[256];
int ret_get;
if (filenames[file_idx] == NULL)
continue;
base_name = pqdif_basename(filenames[file_idx]);
if (base_name[0] == '\0')
continue;
if (!pqdif_match_interval(base_name, LD_info->cpuno, interval_start, ied_usr->dev_type))
continue;
found_this_interval = TRUE;
if (pqdif_local_file_exists(base_name)) {
existed++;
printf("[PQDIF][RECALL] local file exists, skip mp_id=%s interval=%lld file=%s\n",
LD_info->mp_id, (long long)interval_start, base_name);
break;
}
memset(remote_filename,0,sizeof(remote_filename));
memset(only_filename_ret,0,sizeof(only_filename_ret));
pqdif_build_remote_filename(filenames[file_idx], remote_filename, sizeof(remote_filename));
if (remote_filename[0] == '\0') {
failed++;
break;
}
ret_get = pqdif_get_file_from_device(chnl_usr, remote_filename, only_filename_ret, sizeof(only_filename_ret));
if (ret_get == SD_SUCCESS) {
fetched++;
chnl_usr->m_LastPosRespTime = sGetMsTime();
printf("[PQDIF][RECALL] fetch success mp_id=%s interval=%lld remote=%s local=%s/%s\n",
LD_info->mp_id, (long long)interval_start, remote_filename, PQDIF_LOCAL_DIR, only_filename_ret);
}
else {
failed++;
echo_warn3("[PQDIF][RECALL] fetch failed mp_id=%s remote=%s IP=%s\n",
LD_info->mp_id, remote_filename, chnl_usr->ip_str);
}
break;
}
if (!found_this_interval) {
missing++;
printf("[PQDIF][RECALL] no matched file mp_id=%s interval=%lld dev_type=%s\n",
LD_info->mp_id, (long long)interval_start, ied_usr->dev_type);
}
interval_start += interval_step;
}
printf("[PQDIF][RECALL] finish mp_id=%s fetched=%d existed=%d missing=%d failed=%d\n",
LD_info->mp_id, fetched, existed, missing, failed);
if (missing > 0 || failed > 0)
return APR_EGENERAL;
return APR_SUCCESS;
}
int pt61850_write_cn_file(chnl_usr_t *chnl_usr, ied_t *ied, char *rem_filename, char *only_filename_ret) int pt61850_write_cn_file(chnl_usr_t *chnl_usr, ied_t *ied, char *rem_filename, char *only_filename_ret)
{ {
@@ -2338,7 +2531,7 @@ static apr_status_t ChannelCheckPQDIFFiles(chnl_usr_t *chnl_usr)
* 这里保持同样的匹配方式,不依赖厂家前缀,也兼容普通设备用 * 这里保持同样的匹配方式,不依赖厂家前缀,也兼容普通设备用
* 周期开始时间命名、振兴类设备用周期结束时间命名的差异。 * 周期开始时间命名、振兴类设备用周期结束时间命名的差异。
*/ */
if (!pqdif_match_recent_interval(base_name, LD_info->cpuno)) if (!pqdif_match_recent_interval(base_name, LD_info->cpuno, ied_usr->dev_type))
continue; continue;
matched++; matched++;