Files
microser/mms/mms_process.c
2025-07-28 15:23:13 +08:00

2162 lines
85 KiB
C
Raw 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.

/**
* @file: $RCSfile: mms_process.c,v $
* @brief: $PROFIBUS 与SSRTDB交互
*
* @version: $Revision: 1.28 $
* @date: $Date: 2022/11/28 07:13:13 $
* @author: $Author: lizhongming $
* @state: $State: Exp $
*
* @latest: $Id: mms_process.c,v 1.28 2022/11/28 07:13:13 lizhongming Exp $
*/
#include <string.h>
#include "rdb_client.h"
#include <stdlib.h>
#include "db_interface.h"
#include "node.h"
#include "ied.h"
#include "../json/mms_json_inter.h"
#include "../cfg_parse/custom_printf.h"//lnk20250225
#include "../log4cplus/log4.h"//lnk添加log4
void clear_rpt_counter_by_trigger(trigger_t *trigger);
//lnk20241031
extern void SOEFileWeb(char* localpath,char* cloudpath,char* wavepath);
//lnk 2024-11-4 添加时间转换函数
char* convertMsToDateTimeString(int64_t msTime);
//lnk20250115
extern pthread_mutex_t mtx;
extern apr_pool_t* g_cfg_pool;
extern apr_pool_t* g_init_pool;
extern int g_DevFlag; //日志配置中读取的参数暂无特定使用lnk20250121
extern int IED_COUNT;
//lnk20250115end
#ifdef DEBUG_SISCO
SD_CONST static ST_CHAR *SD_CONST thisFileName = __FILE__;
#endif
#ifdef _OS_UNIX_
#include <sys/vfs.h>
#endif
extern uint32_t g_dead_lock_counter;
extern uint32_t g_thread_blocked_times;
extern uint16_t g_client_id;
extern RPT_TYPEIDS g_rpt_typeids;
//extern rdb_t *g_rdb ;
extern node_t *g_node ;
extern char g_my_conf_fname[256];
extern apr_pool_t *g_init_pool;
extern apr_pool_t *g_run_pool;
extern pt61850app_t *g_pt61850app;
//extern char *g_sysfile_filedir;
extern char g_onlyIP[255]; //直连某个IP仅仅为方便测试
//extern int g_sysfile_appid; //add by rzx
//extern apr_time_t g_file_valid_time;
extern uint32_t g_min_free_size;
/////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
extern uint32_t g_node_id;
extern int three_secs_enabled;
extern int auto_register_report_enabled;
extern int FRONT_MP_NUM;
///////////////////////////////////////////////////////////////////////////////
extern int FILE_FLAG;
extern int recall_len;
extern int recall_sta;
extern int recall_daily;
extern char* UDS_UPLOAD_URL;
///////////////////////////////////////////////////////////////////
//lnk20250122start
apr_status_t init_rem_dib_table()
{
int pos = 0;
int iedno,chnl_no;
ied_t *ied;
struct in_addr ip;
chnl_usr_t *chnl_usr;
if(IED_COUNT < g_pt61850app->chnl_counts){
set_rem_dib_table_size( g_pt61850app->chnl_counts );//按照最大的终端数来申请
g_pt61850app->chnl_usr = apr_pcalloc( g_init_pool,g_pt61850app->chnl_counts*sizeof(chnl_usr_t*) );
printf( "set_rem_dib_table_size %d \n",g_pt61850app->chnl_counts );
}
else{
set_rem_dib_table_size( IED_COUNT );//按照最大的终端数来申请
g_pt61850app->chnl_usr = apr_pcalloc( g_init_pool,IED_COUNT*sizeof(chnl_usr_t*) );
printf( "set_rem_dib_table_size %d \n",IED_COUNT );
}
for(iedno=0 ; iedno<g_node->n_clients; iedno++) {
ied = g_node->clients[iedno];
for(chnl_no=0 ; chnl_no<ied->chncount; chnl_no++) {
chnl_usr = ied->channel[chnl_no].connect;
g_pt61850app->chnl_usr[pos] = chnl_usr;
ip.s_addr = htonl(ied->channel[chnl_no].addr);
strcpy(chnl_usr->ip_str,inet_ntoa(ip));
printf( " add_rem_dib_table %s:%d \n",chnl_usr->ip_str ,ied->channel[chnl_no].port );
add_rem_dib_table (pos++,chnl_usr->ip_str,ied->channel[chnl_no].port );
{
char comm_str[256];
memset(comm_str,0,256);
apr_snprintf(comm_str,sizeof(comm_str),"%16s:%d\t\tinited",chnl_usr->ip_str,ied->channel[chnl_no].port);
add_comm_log(comm_str);
}
}
}
return APR_SUCCESS;
}
//lnk20250122end
void CloseIECReports(chnl_usr_t *chnl_usr)
{
ied_t *ied;
ied_usr_t *ied_usr;
LD_info_t *LD_info;
rptinfo_t *rptinfo = NULL;
int cpuno,rpt_no;
ST_RET ret;
ied = chnl_usr->chnl->ied;
ied_usr = GET_IEDEXT_ADDR(ied);
for(cpuno=0 ; cpuno<ied->cpucount; cpuno++) {
LD_info = &(ied_usr->LD_info[cpuno]);
for(rpt_no=0 ; rpt_no<LD_info->rptcount; rpt_no++) {
char rpt_inst_name[65];
rptinfo = LD_info->rptinfo[rpt_no];
if ( ! rptinfo->rpt_registered )
continue;
if ( rptinfo->chnl_id != chnl_usr->chnl_id)
continue;
rptinfo->rpt_registered = FALSE;
//注销报告后取消10分钟 再注册一次的限制,可立即注册
rptinfo->m_LastRegisterFailedTime = sGetMsTime() -10*60*1000;
rptinfo->m_rcb_info = NULL;
}
}
}
void closeChannel(chnl_usr_t *chnl_usr)
{
//终端日志的key,lnk20250526
char full_key_t_c[256]; // 分配足够空间
char full_key_t_d[256]; // 分配足够空间
snprintf(full_key_t_c, sizeof(full_key_t_c), "terminal.%s.COM", ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->terminal_id);
snprintf(full_key_t_d, sizeof(full_key_t_d), "terminal.%s.DATA", ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->terminal_id);
//终端日志的key,lnk20250526
char comm_str[256];
memset(comm_str,0,256);
apr_snprintf(comm_str,sizeof(comm_str),"%16s:%d\t\tdisconnected !!!",chnl_usr->ip_str,chnl_usr->chnl->port);
add_comm_log(comm_str);
FRONT_MP_NUM--;
echo_warn1("Close Channel IP: %s",chnl_usr->ip_str );
CloseIECReports(chnl_usr);
echo_warn1("-------Close Channel IP: %s success!!!!!!!!!", chnl_usr->ip_str);
if (chnl_usr->net_info) {
ALL_RCB_INFO *all_rcb_info;
RCB_INFO *rcb_info;
ST_RET ret;
if(chnl_usr->net_info->user_info)
{
all_rcb_info = (ALL_RCB_INFO *)chnl_usr->net_info->user_info;
while((rcb_info = (RCB_INFO *)list_get_first(&all_rcb_info->rcb_info_list)) != NULL)
rcb_info_destroy (rcb_info);
chk_free(all_rcb_info);
chnl_usr->net_info->user_info=NULL;
}
chnl_usr->net_info->rem_vmd = NULL;
echo_warn("---------start disconnectFromServer!\n");
ret = mms_disconnectFromServer(chnl_usr->net_info,&chnl_usr->m_reqCtrl);
echo_warn("---------end disconnectFromServer!\n");
DIY_WARNLOG(full_key_t_c,"【WARN】前置与终端%s - ip端口%s:%d 断开连接", ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->terminal_id,chnl_usr->ip_str,chnl_usr->chnl->port);
if (ret != SD_SUCCESS){
echo_warn("---------disconnectFromServer success!\n");
echo_warn2( "CHANNEL %s,NetInfo= %x mms_disconnectFromServer failed ,Disconnect it roughly! \n",chnl_usr->ip_str,chnl_usr->net_info);
mvl_free_req_ctrl(chnl_usr->m_reqCtrl);
chnl_usr->net_info->user_ext = NULL;
chnl_usr->net_info = NULL;
chnl_usr->m_reqCtrl = NULL;
chnl_usr->net_info = NULL;
chnl_usr->m_state = CHANNEL_DISCONNECTED;
chnl_usr->m_ClosedMsTime = sGetMsTime();
chnl_usr->chnl->status = STATUS_BREAKOFF;
}
else{
chnl_usr->m_state = CHANNEL_DISCONNECTING;
chnl_usr->m_StartDisconnectingTime = sGetMsTime();
}
}else {
chnl_usr->m_state = CHANNEL_DISCONNECTED;
chnl_usr->m_ClosedMsTime = sGetMsTime();
chnl_usr->chnl->status = STATUS_BREAKOFF;
}
}
ST_VOID Callback_channel_disconnect_ind(MVL_NET_INFO * NetInfo, ST_INT discType)
{
chnl_usr_t *chnl_usr;
chnl_usr = (chnl_usr_t*)NetInfo->user_ext;
if ( chnl_usr ) {
if(chnl_usr->m_state == CHANNEL_CONNECTING)
{ //do nothing;
printf("Do nothing,m_state == CHANNEL_CONNECTING ,NetInfo = %x",NetInfo);
}else if ( chnl_usr->m_state == CHANNEL_CONNECTED )
{
closeChannel(chnl_usr);
}else if ( chnl_usr->m_state == CHANNEL_DISCONNECTING )
{ //do nothing;
printf("Do nothing,m_state == CHANNEL_DISCONNECTING ,NetInfo = %x",NetInfo);
}else if ( chnl_usr->m_state == CHANNEL_DISCONNECTED )
{ //do nothing;
printf("Do nothing,m_state == CHANNEL_DISCONNECTED ,NetInfo = %x",NetInfo);
}
chnl_usr->net_info = NULL;
NetInfo->user_ext = NULL;
}
printf(" Callback_channel_disconnect_ind ,NetInfo = %x",NetInfo);
//zw修改 2023 - 8 - 17 通讯中断回调函数 PG库记录当日中断次数
ied_usr_t* ied_usr = (ied_usr_t*)(chnl_usr->chnl[0].ied->usr_ext);
}
void IECReport_tryGI(chnl_usr_t *chnl_usr,rptinfo_t *rptinfo)
{
char varName[64] = "";
ST_BOOLEAN GI = TRUE; /* call GI */
if ( (sGetMsTime() -rptinfo->m_LastGITime) < g_pt61850app->giTime*1000 )
return;
rptinfo->m_LastGITime = sGetMsTime();
get_rpt_inst_name(rptinfo,varName);
strcat(varName, "$GI");
mms_named_var_write (chnl_usr->net_info, varName, DOM_SPEC, rptinfo->LD_info->LD_name,
g_rpt_typeids.mmsbool, (ST_CHAR *) &GI, g_pt61850app->mmsOpTimeout);
}
//增加处理根据稳态,或暂态功能等,决定 报告是否需要注册、取消注册或 不做任何处理
int judge_rpt_next_should_do(rptinfo_t *rptinfo)
{
int should_register_state = 1; //各功能默认注册
int is_real_report = (rptinfo->report_PQ_type & REPORT_TYPE_REAL);//报告控制中包含的类型
int is_soe_report = (rptinfo->report_PQ_type & REPORT_TYPE_SOE);
if (three_secs_enabled) {
should_register_state = 0; //3秒功能模块默认不注册
if (is_real_report) {//映射中包含控制块且触发
should_register_state = rptinfo->LD_info->real_data;
}
if (is_soe_report) {
should_register_state = rptinfo->LD_info->soe_data;
}
}
if (should_register_state==rptinfo->rpt_registered)//已经触发/没有触发,没有变动
return SHOULD_DO_NOTHING;
else if (should_register_state)//有变动且要触发
return SHOULD_REGISTER;
else
return SHOULD_UNREGISTER;//有变动,不触发
}
void ChannelCheckIECReports(chnl_usr_t *chnl_usr)
{
ied_t *ied;
ied_usr_t *ied_usr;
LD_info_t *LD_info;
rptinfo_t *rptinfo = NULL;
channel_t *channel = NULL;
int cpuno,rpt_no;
char rpt_inst_name[65];
ST_RET ret;
ied = chnl_usr->chnl->ied;
ied_usr = GET_IEDEXT_ADDR(ied);
channel = chnl_usr->chnl;
for(cpuno=0 ; cpuno<ied->cpucount; cpuno++)
{
LD_info = &(ied_usr->LD_info[cpuno]); //遍历监测点
if (LD_info->cpuno==0)
continue;
for(rpt_no=0 ; rpt_no<LD_info->rptcount; rpt_no++) { //遍历报告(映射文件中读取的报告控制)
//监测点日志的key,lnk20250526
char full_key_m_c[256]; // 分配足够空间
char full_key_m_d[256]; // 分配足够空间
snprintf(full_key_m_c, sizeof(full_key_m_c), "monitor.%s.COM", LD_info->mp_id);
snprintf(full_key_m_d, sizeof(full_key_m_d), "monitor.%s.DATA", LD_info->mp_id);
//监测点日志的key,lnk20250526
rptinfo = LD_info->rptinfo[rpt_no] ;
if (judge_rpt_next_should_do(rptinfo)==SHOULD_DO_NOTHING)//检查是否触发
{
//DIY_DEBUGLOG(full_key_m_c,"【DEBUG】监测点:%s - id:%s不注册报告", LD_info->name,LD_info->mp_id);
continue;
}
if(rptinfo->m_curRptSuffix==-1)
rptinfo->m_curRptSuffix = g_pt61850app->rptSuffix[g_client_id][0] ;
get_rpt_inst_name(rptinfo,rpt_inst_name);//获取报告名
if ( ! rptinfo->rpt_registered ) {
if ( (sGetMsTime() -rptinfo->m_LastRegisterFailedTime) > 20*1000 ) {
//注册失败后间隔 20秒 再注册一次
RCB_INFO *rcb_info;
printf("start mms_register_iec_rpt................................\n");
//mq日志
DIY_INFOLOG(full_key_m_c,"【NORMAL】监测点:%s - id:%s开始注册报告,报告名:%s", LD_info->name,LD_info->mp_id,rpt_inst_name);
if ( strstr(rptinfo->rptID,"LLN0$BR$brcbFlickerData") )
rptinfo->IntgPd = 600; //10分钟
/////////////////////////WW 2023-08-30 增加设备类型与报告绑定
rcb_info = mms_register_iec_rpt (chnl_usr->net_info, &g_rpt_typeids,
LD_info->LD_name,rpt_inst_name,g_pt61850app->mmsOpTimeout, rptinfo->IntgPd,rptinfo->TrgOpt,
(ST_UINT8*)rptinfo->m_EntryID ,(ST_UINT8*)rptinfo->OptFlds);
//WW end
///////////////////////////
if( !rcb_info )
{
if ( ++rptinfo->m_curRptSuffix > g_pt61850app->rptSuffix[g_client_id][1] )
rptinfo->m_curRptSuffix = g_pt61850app->rptSuffix[g_client_id][0] ;
rptinfo->m_LastRegisterFailedTime = sGetMsTime() ;
echo_err9("\n注册报告失败Rregister iec_rpt failed !!! IED_ID=%d ,CPU=%d , domain: %s ,rpt_inst_name: %s ,ip: %s:%d,chnl_id: %d ,IntgPd=%d ,TrgOpt=0x%x \n",
APR_EGENERAL, LD_info->ied->id,LD_info->cpuno,LD_info->LD_name,rpt_inst_name,chnl_usr->ip_str,chnl_usr->chnl->port,
chnl_usr->chnl_id, rptinfo->IntgPd,rptinfo->TrgOpt );
//mq日志
DIY_WARNLOG(full_key_m_c,"【WARN】监测点:%s - id:%s注册报告失败,报告名:%s", LD_info->name,LD_info->mp_id,rpt_inst_name);
}
else
{
double GIoffset;
rptinfo->rpt_registered = TRUE;
rptinfo->m_rcb_info = rcb_info;
rptinfo->chnl_id = chnl_usr->chnl_id;
chnl_usr->m_NegRespTimes = 0;
chnl_usr->m_LastPosRespTime = sGetMsTime();
echo_msg11("\nRegister iec_rpt succeed, IED_ID=%d ,CPU=%d ,domain: %s ,rpt_inst_name: %s ,ip: %s:%d,chnl_id: %d ,IntgPd=%d ,TrgOpt=0x%x ,OptFlds=0x%x%x \n",
LD_info->ied->id,LD_info->cpuno,LD_info->LD_name,rpt_inst_name,chnl_usr->ip_str,chnl_usr->chnl->port,chnl_usr->chnl_id,
rptinfo->IntgPd,rptinfo->TrgOpt,rptinfo->OptFlds[0],rptinfo->OptFlds[1] );
//mq日志
DIY_WARNLOG(full_key_m_c,"【WARN】监测点:%s - id:%s注册报告成功,报告名:%s", LD_info->name,LD_info->mp_id,rpt_inst_name);
// add here to GI not the same time
GIoffset = 0.5 * g_pt61850app->giTime;
rptinfo->m_LastGITime = sGetMsTime() - GIoffset*1000;
}
printf("end mms_register_iec_rpt................................\n");
//mq日志
DIY_INFOLOG(full_key_m_c,"【NORMAL】监测点:%s - id:%s注册报告结束,报告名:%s", LD_info->name,LD_info->mp_id,rpt_inst_name);
}
}
else { //rpt_registered ==TRUE
if ( (sGetMsTime() -rptinfo->m_LastUnRegisterFailedTime) > 20*1000 ) {
//取消注册失败后间隔 20秒 再取消注册一次
printf("start mms_unregister_iec_rpt................................\n");
//mq日志
DIY_INFOLOG(full_key_m_c,"【NORMAL】监测点:%s - id:%s开始注销报告,报告名:%s", LD_info->name,LD_info->mp_id,rpt_inst_name);
ret = mms_unregister_iec_rpt (chnl_usr->net_info, &g_rpt_typeids,
LD_info->LD_name,rpt_inst_name,g_pt61850app->mmsOpTimeout);
if( ret == SD_FAILURE ) {
rptinfo->m_LastUnRegisterFailedTime = sGetMsTime() ;
echo_err6("\n取消注册报告失败UnRregister iec_rpt failed !!! IED_ID=%d ,CPU=%d , domain: %s ,rpt_inst_name: %s ,ip: %s,chnl_id: %d \n",
APR_EGENERAL, LD_info->ied->id,LD_info->cpuno,LD_info->LD_name,rpt_inst_name,chnl_usr->ip_str,chnl_usr->chnl_id);
//mq日志
DIY_WARNLOG(full_key_m_c,"【WARN】监测点:%s - id:%s注销报告失败,报告名:%s", LD_info->name,LD_info->mp_id,rpt_inst_name);
}
else {
rptinfo->rpt_registered = FALSE;
echo_msg7("\nUnRegister iec_rpt succeed, IED_ID=%d ,CPU=%d ,domain: %s ,rpt_inst_name: %s ,ip: %s:%d,chnl_id: %d \n",
LD_info->ied->id,LD_info->cpuno,LD_info->LD_name,rpt_inst_name,chnl_usr->ip_str,chnl_usr->chnl->port,chnl_usr->chnl_id );
//mq日志
DIY_INFOLOG(full_key_m_c,"【NORMAL】监测点:%s - id:%s注销报告成功,报告名:%s", LD_info->name,LD_info->mp_id,rpt_inst_name);
}
printf("end mms_unregister_iec_rpt................................\n");
//mq日志
DIY_INFOLOG(full_key_m_c,"【NORMAL】监测点:%s - id:%s注销报告结束,报告名:%s", LD_info->name,LD_info->mp_id,rpt_inst_name);
}
}
}
}
}
void ChannelCheckWaveFiles(chnl_usr_t *chnl_usr)
{
ied_t *ied;
ied_usr_t *ied_usr;
LD_info_t *LD_info;
int cpuno;
ied = chnl_usr->chnl->ied;
ied_usr = GET_IEDEXT_ADDR(ied);
for(cpuno=0 ; cpuno<ied->cpucount; cpuno++) {
LD_info = &(ied_usr->LD_info[cpuno]);
if (LD_info->line_id<=0) {
continue;
}
call_cn_wavelist(LD_info); //try to call wave file
}
}
void ChannelCheckIECLogs(chnl_usr_t *chnl_usr)
{
ST_RET ret;
ied_t *ied;
ied_usr_t *ied_usr;
LD_info_t *LD_info;
loginfo_t *loginfo = NULL;
int cpuno;
double now;
static double last_check_recall_config_time = 0.0;
ied = chnl_usr->chnl->ied;
ied_usr = GET_IEDEXT_ADDR(ied);
for(cpuno=0 ; cpuno<ied->cpucount; cpuno++) {
LD_info = &(ied_usr->LD_info[cpuno]);
if (LD_info->logcount<=0)
continue;
loginfo = LD_info->loginfo[0] ;
apr_sleep(apr_time_from_sec(1) / 10);
//监测点日志的key,lnk20250526
char full_key_m_c[256]; // 分配足够空间
char full_key_m_d[256]; // 分配足够空间
snprintf(full_key_m_c, sizeof(full_key_m_c), "monitor.%s.COM", LD_info->mp_id);
snprintf(full_key_m_d, sizeof(full_key_m_d), "monitor.%s.DATA", LD_info->mp_id);
//监测点日志的key,lnk20250526
Check_Recall_Config(LD_info->mp_id);//尝试获取xml结构
//补招数量不为0且不是正在补招
if (LD_info->autorecallcount != 0 && LD_info->autorecallflag != 1) {
int i;
int failed_count = 0;
/////////////////////////////////////////////////////根据配置文件控制下发补招时间为北京时间还是utc时间
printf("~~~~~~~this dev type is %s~~~~~~~",ied_usr->dev_type);
XmlConfigC cfg1;
if (get_xml_config_by_dev_type(ied_usr->dev_type, &cfg1)) {
printf("ValueOfTimeUnit = %s\n", cfg1.ValueOfTimeUnit);
} else {
printf("读取失败,未找到 dev_type\n");
}
long long utc_or_beijing;
if(strcmp(cfg1.ValueOfTimeUnit, "utc") == 0){//装置时间是utc还是北京
utc_or_beijing = 28800000;
DIY_WARNLOG(full_key_m_c,"【WARN】监测点:%s - id:%s开始补招数据,下发补招时间为utc时间,监测点对应装置型号:%s", LD_info->name,LD_info->mp_id,ied_usr->dev_type);
}
else{
utc_or_beijing = 0;
DIY_WARNLOG(full_key_m_c,"【WARN】监测点:%s - id:%s开始补招数据,下发补招时间为beijing时间,监测点对应装置型号:%s", LD_info->name,LD_info->mp_id,ied_usr->dev_type);
}
//////////////////////////////////////////////////////////////
for (i = 0; i < LD_info->autorecallcount; i++) {
LD_info->autorecallflag = 1;//正在补招
//当前不区分稳态和暂态lnk20241030如果做区分修改Check_Recall_Config从xml文件获取数据后赋值给了LD_info
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->end_time = apr_time_from_sec(LD_info->autorecall[i]->end - 5);
/////////////////////////////////////////////////////根据配置文件控制下发补招时间为北京时间还是utc时间,上送的数据61850库会转换成北京时间
loginfo->start_time = loginfo->end_time - utc_or_beijing;//下发utc时间需要减去8小时
loginfo->end_time = loginfo->end_time - utc_or_beijing;
/////////////////////////////////////////////////////
if (loginfo->need_steady == 0 && loginfo->need_voltage == 0)
continue;
if (loginfo->end_time <= loginfo->start_time)
continue;
printf("start mms_jread................................\n");
echo_msg6("\n mms_jread IED_ID=%d ,CPU=%d ,domain: %s ,logName: %s ,ip: %s,chnl_id: %d \n",
LD_info->ied->id, LD_info->cpuno, LD_info->LD_name, loginfo->logName, chnl_usr->ip_str, chnl_usr->chnl_id);
//mq日志
//DIY_WARNLOG(full_key_m_c,"【WARN】监测点:%s - id:%s开始补招数据", LD_info->name,LD_info->mp_id);
ret = mms_jread(loginfo, chnl_usr->net_info, loginfo->LD_info->LD_name, loginfo->logName,
loginfo->start_time, loginfo->end_time, g_pt61850app->mmsOpTimeout, chnl_usr->ip_str);
if (ret != SD_SUCCESS) {
echo_warn6("\n mms_jread Failed! IED_ID=%d ,CPU=%d ,domain: %s ,logName: %s ,ip: %s,chnl_id: %d \n",
LD_info->ied->id, LD_info->cpuno, LD_info->LD_name, loginfo->logName, chnl_usr->ip_str, chnl_usr->chnl_id);
//mq日志
DIY_ERRORLOG(full_key_m_c,"【ERROR】监测点:%s - id:%s补招数据失败 - 失败时间点:%lld 至 %lld", LD_info->name,LD_info->mp_id,loginfo->start_time,loginfo->end_time);
failed_count++;
}
del_mvl_type_ctrl();
loginfo->need_steady = 0;
loginfo->need_voltage = 0;
loginfo->start_time = loginfo->end_time;
g_dead_lock_counter = 0;
g_thread_blocked_times = 0; //防止补招时间过长导致进程退出
now = sGetMsTime();
last_check_recall_config_time = now;
printf("end ==============%.2f================\n", last_check_recall_config_time);
printf("end mms_jread................................\n");
}
//不管是否成功这个补招文件必须删除可能出现一直失败循环读取文件和循环补招导致程序崩溃202050724lnk
//if (failed_count==0) {//成功
Delete_recall_Xml(LD_info->mp_id);
DIY_WARNLOG(full_key_m_c,"【WARN】监测点:%s - id:%s结束补招数据", LD_info->name,LD_info->mp_id);
//}
}
}
}
void process_3s_config(trigger_3s_xml_t *trigger_3s_xml)
{
int i,j;
trigger_t *trigger;
trigger_t *trigger_work;
int trigger_num;
ied_t *ied;
LD_info_t *LD_info;
int need_write_file;
int new_in_work_found;
printf("start process_3s_config\n");
need_write_file = FALSE;
trigger = trigger_3s_xml->new_triggers; //3s配置文件中的newtrigger数组如果没有newtrigger这里就是0
trigger_num = trigger_3s_xml->new_trigger_num;
for (i=0; i<trigger_num; i++){
new_in_work_found = FALSE;
for (j=0; j<trigger_3s_xml->work_trigger_num; j++){
trigger_work = &trigger_3s_xml->work_triggers[j];
if (trigger_work->dev_idx==trigger[i].dev_idx && trigger_work->line_id==trigger[i].line_id ) {
if (trigger[i].real_data>=0)
trigger_work->real_data = trigger[i].real_data;//更新rtdata标志
if (trigger[i].soe_data>=0)
trigger_work->soe_data = trigger[i].soe_data; //更新soe标志
trigger_work->limit = trigger[i].limit;
clear_rpt_counter_by_trigger(trigger_work);//清理报告
new_in_work_found = TRUE;
}
}
if (!new_in_work_found) { //newtrigger为0这是全新的触发不是正在工作的触发
clear_rpt_counter_by_trigger(&trigger[i]);
if (trigger[i].real_data<0)
trigger[i].real_data = 0;
if (trigger[i].soe_data<0)
trigger[i].soe_data = 0;
trigger_3s_xml->work_triggers[trigger_3s_xml->work_trigger_num++] = trigger[i];
}
need_write_file = TRUE;//需要将新增的点写到文件中
}
trigger = trigger_3s_xml->delete_triggers; //如果没有deletetrigger这里就是0
trigger_num = trigger_3s_xml->delete_trigger_num;
for (i=0; i<trigger_num; i++){
for (j=0; j<trigger_3s_xml->work_trigger_num; j++){
trigger_work = &trigger_3s_xml->work_triggers[j];
if (trigger_work->dev_idx==trigger[i].dev_idx && trigger_work->line_id==trigger[i].line_id ) {
clear_rpt_counter_by_trigger(trigger_work);
trigger_work->dev_idx = INVALID_DEV_IDX;
}
}
need_write_file = TRUE;
}
trigger = trigger_3s_xml->modify_triggers; //如果没有modifytrigger这里就是0
trigger_num = trigger_3s_xml->modify_trigger_num;
for (i=0; i<trigger_num; i++){
for (j=0; j<trigger_3s_xml->work_trigger_num; j++){
trigger_work = &trigger_3s_xml->work_triggers[j];
if (trigger_work->dev_idx==trigger[i].dev_idx && trigger_work->line_id==trigger[i].line_id ) {
*trigger_work = trigger[i];
clear_rpt_counter_by_trigger(trigger_work);
}
}
need_write_file = TRUE;
}
clear_all_LD_real_soe_report_shoud_register(); //清空所有需要注册的报告根据实时配置文件来。这里使LD_info->real_data = 0;LD_info->soe_data = 0;就不会触发报告
trigger = trigger_3s_xml->work_triggers; //文件的work块
trigger_num = trigger_3s_xml->work_trigger_num;
for (i=0; i<trigger_num; i++){ //遍历work块中所有触发点
int real_report_count = 0; //实时报告数量
if (trigger[i].dev_idx==INVALID_DEV_IDX){//跳过错误的终端号
printf("dev_idx incaild\n");
continue;
}
ied = find_ied_from_dev_idx(trigger[i].dev_idx); //通过文件中的dev号找到配置的设备找不到说明不在这个进程里跳过多进程的实现是否有必要如果要实现就得每个进程一个操作目录
if (!ied){
printf("can't find ied by dev_idx\n");
continue;
}
LD_info = find_LD_info_from_line_id(ied,trigger[i].line_id); //用文件中的检测点号找到配置的监测点
if (!LD_info){
printf("can't find line by line_idx\n");
continue;
}
real_report_count = get_real_report_count(LD_info); //获取监测点的实时报告数量LD_info->rptinfo[rpt_no]->count
//调试用
printf("terminal:%s - mp:%s real_report_count %d\n", ((ied_usr_t*)(ied->usr_ext))->terminal_id, LD_info->mp_id, real_report_count);
trigger[i].count = real_report_count; //记录此时这个监测点的实时报告数量
if (trigger[i].real_data && trigger[i].limit && (real_report_count>trigger[i].limit) ) {//监测点的实时报告数量大于当前文件中的限制,这个监测点的数据清零不再触发
trigger[i].real_data = 0;
trigger[i].limit = 0;
trigger[i].count = 0;
need_write_file = TRUE;
}
LD_info->real_data = trigger[i].real_data; //根据文件中的配置来设置,然后在报告触发中触发实时报告
LD_info->soe_data = trigger[i].soe_data;
LD_info->limit = trigger[i].limit;
LD_info->count = trigger[i].count;
if (LD_info->limit > 0) //限制数更新记录
need_write_file = TRUE;
if ( trigger[i].real_data==0 && trigger[i].soe_data==0 )//不触发条件记录
need_write_file = TRUE;
}
if (need_write_file)
create_3s_xml(trigger_3s_xml); //写入文件的work块
}
void del_process_recall_config(recall_xml_t* recall_xml)
{
int i,j;
recall_t *recall;
recall_t *recall_work;
int recall_num;
ied_t *ied;
LD_info_t *LD_info;
loginfo_t *loginfo = NULL;
int need_write_file;
need_write_file = FALSE;
recall = recall_xml->new_recalls;
recall_num = recall_xml->new_recall_num;
for (i=0; i<recall_num; i++){
recall_xml->work_recalls[recall_xml->work_recall_num++] = recall[i];
need_write_file = TRUE;
}
recall = recall_xml->work_recalls;
recall_num = recall_xml->work_recall_num;
for (i=0; i<recall_num; i++){
LD_info = find_LD_info_only_from_line_id(recall[i].line_id);
if (!LD_info)
continue;
if (LD_info->logcount<=0)
continue;
loginfo = LD_info->loginfo[0] ;
loginfo->need_steady = recall[i].need_steady;
loginfo->need_voltage = recall[i].need_voltage;
loginfo->start_time = recall[i].start_time;
loginfo->end_time = recall[i].end_time;
}
}
void check_3s_config()
{
double now;
static double last_check_3s_config_time = 0.0;//初始化时间
trigger_3s_xml_t trigger_3s_xml; //3s触发文件
if (!three_secs_enabled) //cfg_3s_data进程才会开启
return;
now = sGetMsTime(); //当前时间
if ( fabs(now - last_check_3s_config_time) < 3*1000 ) //wait 3secs //当前进程任务执行时查看当前时间和上次执行时间间隔小于3秒不执行大于等于3秒往下执行
return;
//调试用
//printf("begin 3s config...\n");
last_check_3s_config_time = now; //记录本次运行时间
while (APR_SUCCESS==parse_3s_xml(&trigger_3s_xml)){ //处理3秒文件一次处理一个
//处理实时触发加台账锁lnk20250114
//pthread_mutex_lock(&mtx); printf("3s hold lock !!!!!!!!!!!");
process_3s_config(&trigger_3s_xml); //根据文件处理数据
//pthread_mutex_unlock(&mtx); printf("3s free lock !!!!!!!!!!!");
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//lnk20250114参照实时数据方法处理台账信息
int isValidModelId(const char* model_id) {
size_t i;
if (model_id == NULL)
{
printf("!!!model_id null!!!\n");
return 0;
} // NULL 无效
size_t len = strlen(model_id);
if (len < 4) {
printf("!!!model_id length < 4!!!\n");
return 0; // 长度 < 4 无效
}
// 检查是否全是空格
for (i = 0; i < len; i++) {
if (!isspace((unsigned char)model_id[i])) {
return 1; // 只要包含非空格字符,就是合法的
}
}
printf("!!!model_id only space!!!\n");
return 0; // 仅包含空格,无效
}
void process_ledger_update(trigger_update_xml_t *ledger_update_xml)
{
int i,j;
terminal *update;
terminal *update_work;
int update_num;
ied_t *ied;
LD_info_t *LD_info;
int new_in_work_found;
ied_usr_t* ied_usr;
int chnl_no;
chnl_usr_t *chnl_usr = NULL;
printf("!!!start update ledger!!!\n");
update = ledger_update_xml->new_updates; //处理新增台账部分
update_num = ledger_update_xml->new_update_num;
printf("add ledger num:%d\n",update_num);
for (i=0; i<update_num; i++){ //查看所有需要新增的台账
new_in_work_found = FALSE;
if(update[i].terminal_id != NULL){
printf("add ledger of %s\n",update[i].terminal_id);
ied = find_ied_from_terminal_id(update[i].terminal_id); //通过文件中的终端号找到配置的设备
if (ied){
printf("find ied by terminal_id, terminal already exsist\n");
new_in_work_found = TRUE; //当前运行的台账中找到已有台账,变为更新
// 将当前终端从 new_updates 数组中移到 modify_updates 数组
if (ledger_update_xml->modify_update_num < MAX_UPDATEA_NUM) {
// 添加到 modify_updates 数组
ledger_update_xml->modify_updates[ledger_update_xml->modify_update_num] = update[i];
ledger_update_xml->modify_update_num++; // 更新 modify 数组的计数器
// 删除该终端(从 new_updates 数组中移除)
for (j = i; j < ledger_update_xml->new_update_num - 1; j++) {
ledger_update_xml->new_updates[j] = ledger_update_xml->new_updates[j + 1]; // 向前移动元素
}
ledger_update_xml->new_update_num--; // 更新 new_update_num减少一个元素
continue;
} else {
fprintf(stderr, "Exceeded MAX_UPDATEA_NUM limit for modify_updates!\n");
}
}
}
if (!new_in_work_found) { //这是全新的台账,在台账数组中没有包含这个设备
int terminal_index;
int ied_take = 0; //当前台账数组里有不使用的ied空间0没有1有
//进行台账添加和初始化操作
//1-申请新的内存空间//////////////////////////////
//新的台账数量 (n_clients) 和原有的台账数量 (原数量为 g_node->n_clients)
int new_client_count = g_node->n_clients + 1; // 增加一个新的台账
//判断新增进程后是否超过原有的台账数组如果超过了就从不使能的ied空间中找可用空间如果还是没有那就提示不能荷载更多终端
if(new_client_count > IED_COUNT){ //不更新台账数量
ied_t *ied_unused = NULL;
ied_usr_t* ied_usr_unused = NULL;
ied_unused = find_ied_unused();//遍历g_node找到第一个不使用的ied这个ied之前初始化时分配过空间直接占用
if(ied_unused != NULL){
ied_usr_unused = (ied_usr_t*)ied_unused->usr_ext;
ied = ied_unused; //新增的ied指向已有的未使用的ied空间
terminal_index = ied_usr_unused->dev_idx; //记录这个ied的编号即g_node的下标
//打印提示
printf("!!!!!!!!ied index:%d ,origin terminal_id:%s has been taken!!!!!!!!!!\n",ied_usr_unused->dev_idx,ied_usr_unused->terminal_id);
//终端初始化,不需要再申请空间
ied_usr = (ied_usr_t*)ied->usr_ext;
ied_usr->last_call_wavelist_time = sGetMsTime() + g_pt61850app->giTime * 1000;
ied_usr->dev_flag = ENABLE;//终端有效
ied->chncount = 1; //通信端口数
//不再分配通道空间
ied->channel[0].ied = ied; //通道的所属设备
ied->channel[0].status = STATUS_BREAKOFF;//初始化为通信中断
ied->cpucount = 0; //监测点数初始为0读取监测点台账时写入
ied_take = 1;//ied之前存在
}
else{ //在已有的全部台账中都没找到不使用的ied说明这个进程可挂的台账满了不再处理
printf("!!!!!!!!!!ledger array is full!!!!!!\n");
//添加mq响应台账添加失败台账挂满
//update[i].guid
char msg[256];
sprintf(msg, "终端 id: %s 台账更新失败, 这个进程的台账空间已满,达到了配置台账数量的最大值", update[i].terminal_id);
send_reply_to_kafka_c(update[i].guid, "2", msg);
return;
}
}//新台账数量小于等于设定的台账数量
else{
//调试用
printf("!!!!!!!!!!gnodeindex:%d!!!!!!\n",new_client_count - 1);
// 新增的台账在已有的台账数组中记录
g_node->clients[new_client_count - 1] = (ied_t*)apr_pcalloc(g_cfg_pool, sizeof(ied_t));
// 更新台账数量
g_node->n_clients = new_client_count; // 更新台账数量
//1-申请新的内存空间//////////////////////////////
//2-处理终端台账///////////////////////////////////
ied = g_node->clients[new_client_count - 1];//终端台账指针定向到ied数组当前位置的后一位
terminal_index = new_client_count;//新的台账终端
ied_usr = (ied_usr_t*)apr_pcalloc(g_init_pool, sizeof(ied_usr_t));
ied->usr_ext = ied_usr;//内存挂到ied上
if (ied_usr == NULL){
char msg[256];
snprintf(msg, sizeof(msg), "终端 id: %s 台账更新失败,没有找到台账的终端空间", update[i].terminal_id);
send_reply_to_kafka_c(update[i].guid, "2", msg);
return ;
}
ied_usr->last_call_wavelist_time = sGetMsTime() + g_pt61850app->giTime * 1000;//从FeProject/子功能目录/etc/pt61850netd_pqfe.xml中读取的总查询时间
ied_usr->LD_info = (LD_info_t*)apr_pcalloc(g_init_pool, MAX_CPUNO * sizeof(LD_info_t));//内存挂到ied上
if (ied_usr->LD_info == NULL){
char msg[256];
snprintf(msg, sizeof(msg), "终端 id: %s 台账更新失败,没有找到台账的监测点空间", update[i].terminal_id);
send_reply_to_kafka_c(update[i].guid, "2", msg);
return ;
}
ied_usr->dev_flag = ENABLE;//终端有效
ied->chncount = 1; //通信端口数,一个终端一般只有一个
ied->channel = (channel_t*)apr_pcalloc(g_cfg_pool, sizeof(channel_t) * ied->chncount);//内存挂到ied上
ied->channel[0].ied = ied; //通道的所属设备
ied->channel[0].status = STATUS_BREAKOFF;//初始化为通信中断
ied->cpucount = 0; //监测点数初始为0读取监测点台账时写入
//2-处理终端台账///////////////////////////////////
}
//3-写入台账内容//////////////////////////////
int ret = update_one_terminal_ledger(update,i,ied,terminal_index,ied_take);
if(ret){
printf("ledger can not be update!!!!!quit process!!!!!\n");
char msg[256];
snprintf(msg, sizeof(msg), "终端 id: %s 台账更新失败,无法写入台账", update[i].terminal_id);
send_reply_to_kafka_c(update[i].guid, "2", msg);
clearIed(ied);//添加失败清空整个ied
return ;
}
//3-写入台账内容///////////////////////////////////
//4-配置映射文件//////////////////////////////
char model[64] = {0};
// 获取模型ID检查是否返回 NULL
parse_model_cfg_web_one(ied,&model);//存储在/FeProject/dat/
if (isValidModelId(model)) { //lnk20250313防止拿不到映射文件
// 安全拷贝字符串到 model 数组
strncpy(model, model, sizeof(model) - 1);
model[sizeof(model) - 1] = '\0'; // 确保以 null 结尾
printf("ledger Model ID: %s\n", model);
} else {
printf("ledger No model ID found.quit\n");
char msg[256];
snprintf(msg, sizeof(msg), "终端 id: %s 台账更新失败,没有找到装置型号", update[i].terminal_id);
send_reply_to_kafka_c(update[i].guid, "2", msg);
clearIed(ied);//添加失败清空整个ied
return ;
}
char full_path[128];
snprintf(full_path, sizeof(full_path), "/FeProject/dat/%s.xml", model); // 拼接路径
// 打印模型路径
printf("ledger icd config file full path: %s\n", full_path);
//映射文件解析
Set_xml_nodeinfo_one(ied_usr->dev_type);
//4-配置映射文件///////////////////////////////////
//5-报告块初始化//////////////////////////////
parse_rpt_log_ini_one(ied);
//5-报告块初始化///////////////////////////////////
//调试
printf("ledger id: %s\n", ((ied_usr_t*)ied->channel[0].ied->usr_ext)->terminal_id);
//6-init_rem_dib_table//////////////////////////////
init_rem_dib_table_one(ied);
//6-init_rem_dib_table///////////////////////////////////
//7启动终端日志
init_loggers_bydevid(((ied_usr_t*)ied->channel[0].ied->usr_ext)->terminal_id);
//8响应添加成功
//update[i].guid
char msg[256];
snprintf(msg, sizeof(msg), "终端 id: %s 台账添加成功", update[i].terminal_id);
send_reply_to_kafka_c(update[i].guid, "2", msg);
}
}
//////////////////////////////////////////////////////////////////////////////modify
update = ledger_update_xml->modify_updates; //处理修改台账部分
update_num = ledger_update_xml->modify_update_num;
printf("modify ledger num:%d\n",update_num);
for (i=0; i<update_num; i++){ //查看所有需要新增的台账
new_in_work_found = FALSE;
if(update[i].terminal_id != NULL){
printf("modify ledger of %s\n",update[i].terminal_id);
ied = find_ied_from_terminal_id(update[i].terminal_id); //通过文件中的终端号找到配置的设备
if (ied){
printf("find ied by terminal_id, terminal already exsist\n");
new_in_work_found = TRUE; //当前运行的台账中找到已有台账,更新它
//关闭这个终端的所有连接//////////////////////////////////////////////////////////////////////
for(chnl_no=0 ; chnl_no<ied->chncount; chnl_no++) {
chnl_usr = (chnl_usr_t*)ied->channel[chnl_no].connect;
if (chnl_usr->m_state!=CHANNEL_CONNECTED){//跳过未连接的通道
continue;
}
closeChannel(chnl_usr);//关闭更新台账后,任务会自动连接
}
//更新数据/////////////////////////////////////////////////////////////////////////////////
//3-写入台账内容//////////////////////////////
ied_usr = ied->usr_ext;
//清空logger
remove_loggers_by_terminal_id(update[i].terminal_id);
//写入前先清空已有数据
clearIedUsr(ied_usr);//不会清空dev_idx
//ied_usr->last_call_wavelist_time = sGetMsTime() + g_pt61850app->giTime * 1000;//从FeProject/子功能目录/etc/pt61850netd_pqfe.xml中读取的总查询时间
ied_usr->dev_flag = ENABLE;//终端有效
int ret = update_one_terminal_ledger(update,i,ied,ied_usr->dev_idx,1);//1更新已有的ied
if(ret){
printf("ledger can not be update!!!!!quit process!!!!!\n");
char msg[256];
snprintf(msg, sizeof(msg), "终端 id: %s 台账更新失败,台账无法写入", update[i].terminal_id);
send_reply_to_kafka_c(update[i].guid, "2", msg);
clearIed(ied);//添加失败清空整个ied
return ;
}
//3-写入台账内容////////////////////////////////////////////
//4-配置映射文件///////////////////////////////////////////
char model[64] = {0};
// 获取模型ID检查是否返回 NULL
parse_model_cfg_web_one(ied,&model);//存储在/FeProject/dat/
if (isValidModelId(model)) {
// 安全拷贝字符串到 model 数组
strncpy(model, model, sizeof(model) - 1);
model[sizeof(model) - 1] = '\0'; // 确保以 null 结尾
printf("ledger Model ID: %s\n", model);
} else {
printf("ledger No model ID found.\n");
char msg[256];
snprintf(msg, sizeof(msg), "终端 id: %s 台账更新失败,没有找到装置型号", update[i].terminal_id);
send_reply_to_kafka_c(update[i].guid, "2", msg);
clearIed(ied);//添加失败清空整个ied
return ;
}
char full_path[128];
snprintf(full_path, sizeof(full_path), "/FeProject/dat/%s.xml", model); // 拼接路径
// 打印模型路径
printf("ledger icd config file full path: %s\n", full_path);
//映射文件解析
Set_xml_nodeinfo_one(ied_usr->dev_type);
//4-配置映射文件///////////////////////////////////
//5-报告块初始化//////////////////////////////
parse_rpt_log_ini_one(ied);
//5-报告块初始化///////////////////////////////////
//6-init_rem_dib_table//////////////////////////////
init_rem_dib_table_one(ied);
//6-init_rem_dib_table///////////////////////////////////
//更新数据//////////////////////////////////////////////////////////////////////
//7启动终端日志
init_loggers_bydevid(((ied_usr_t*)ied->channel[0].ied->usr_ext)->terminal_id);
//8响应添加成功
//update[i].guid
char msg[256];
snprintf(msg, sizeof(msg), "终端 id: %s 台账修改成功", update[i].terminal_id);
send_reply_to_kafka_c(update[i].guid, "2", msg);
}
}
if (!new_in_work_found){
printf("modify_updates can not find ied!!!!!!\n");
//添加mq响应台账添加失败台账找不到
//update[i].guid
char msg[256];
sprintf(msg, "终端 id: %s 台账修改失败, 无法找到这个终端", update[i].terminal_id);
send_reply_to_kafka_c(update[i].guid, "2", msg);
}
}
///////////////////////////////////////////////////////////////////////////////delete
update = ledger_update_xml->delete_updates; //处理删除台账部分
update_num = ledger_update_xml->delete_update_num; //删除的数量
printf("delete ledger num:%d\n",update_num);
for (i=0; i<update_num; i++){ //查看所有需要删除的台账
new_in_work_found = FALSE; //找到现存台账的标志
if(update[i].terminal_id != NULL){ //处理非空台账
printf("delete ledger of %s\n",update[i].terminal_id);
ied = find_ied_from_terminal_id(update[i].terminal_id); //通过文件中的终端号找到配置的设备,没找到就不处理
if (ied){
printf("find ied by terminal_id, terminal already exsist\n");
new_in_work_found = TRUE; //当前运行的台账中找到已有台账,更新它
//关闭这个终端的所有连接//////////////////////////////////////////////////////////////////////
for(chnl_no=0 ; chnl_no<ied->chncount; chnl_no++) {
chnl_usr = (chnl_usr_t*)ied->channel[chnl_no].connect;
if (chnl_usr->m_state!=CHANNEL_CONNECTED){ //跳过没连接的通道,一般一个终端只有一个
continue;
}
closeChannel(chnl_usr);//关闭更新台账后如果是删除则不会再连接关闭连接时这个ied的报告会被清除报告控制会被注销
}
//更新数据//////////////////////////////////////////////////////////////////////
//3-删除台账内容//////////////////////////////
//调试用
// 假设要删除 g_node->clients 中的某个 iedindex 为删除的索引
int index_to_remove = 0;
ied_t* ied_find = NULL;
int iedno;
ied_usr_t* ied_usr_find = NULL; //遍历的ied
ied_usr = (ied_usr_t*)ied->usr_ext;//要删除的ied
for (iedno = 0; iedno < g_node->n_clients; iedno++) {
ied_find = g_node->clients[iedno];
ied_usr_find = (ied_usr_t*)ied_find->usr_ext;
if (ied_usr_find && strcmp(ied_usr_find->terminal_id, ied_usr->terminal_id) == 0) {
index_to_remove = iedno;//找到要删除的ied在g_node中的位置
break; //找到退出
}
}
// 先清理要删除的 ied 的内容
ied_t* ied_to_remove = g_node->clients[index_to_remove];//指向g_node对应的内存区
if(ied_to_remove == ied){//通过终端id找到的ied应该也指向g_node对应的内存区
printf("this ied is ied_to_remove\n");
}
//调试用
//清空logger
remove_loggers_by_terminal_id(update[i].terminal_id);
//清空ied的内容
clearIed(ied_to_remove);
//3-删除台账内容///////////////////////////////////
//4-配置映射文件//////////////////////////////
//映射文件保留如果再次添加这个ied如果是相同的映射文件则可以直接加载
//4-配置映射文件///////////////////////////////////
//5-报告块//////////////////////////////
//关闭连接时这个ied的报告已清空。报告控制块保留因为报告控制块是根据设备类型设置的如果删除会影响其他同类型的ied。这个ied对应的控制块在清空内容时已清空
//5-报告块///////////////////////////////////
//6-init_rem_dib_table//////////////////////////////
//rem_dib_table的内容将会被保留如果有新的台账使用这个位置将会直接覆盖
//6-init_rem_dib_table///////////////////////////////////
//更新数据//////////////////////////////////////////////////////////////////////
//7响应添加成功
//update[i].guid
char msg[256];
snprintf(msg, sizeof(msg), "终端 id: %s 台账删除成功", update[i].terminal_id);
send_reply_to_kafka_c(update[i].guid, "2", msg);
}
}
if (!new_in_work_found) {
printf("modify_updates can not find ied!!!!!!\n");
//添加mq响应台账添加失败台账找不到
//update[i].guid
char msg[256];
sprintf(msg, "终端 id: %s 台账删除失败, 无法找到这个终端", update[i].terminal_id);
send_reply_to_kafka_c(update[i].guid, "2", msg);
}
}
//////////////////////////////////////////////////////////////////////////////
if (ledger_update_xml->modify_update_num || ledger_update_xml->new_update_num || ledger_update_xml->delete_update_num){
create_ledger_log(ledger_update_xml); //写入文件
}
}
//台账更新调试打印函数
// 检查字符串是否为空
// 检查字符串是否为空
int is_empty(const char* str) {
return (str == NULL || str[0] == '\0');
}
// 打印 monitor 结构体信息
void print_monitor(const monitor* mon) {
printf("Monitor ID: %s\n", is_empty(mon->monitor_id) ? "N/A" : mon->monitor_id);
printf("Terminal Code: %s\n", is_empty(mon->terminal_code) ? "N/A" : mon->terminal_code);
printf("Monitor Name: %s\n", is_empty(mon->monitor_name) ? "N/A" : mon->monitor_name);
printf("Logical Device Sequence: %s\n", is_empty(mon->logical_device_seq) ? "N/A" : mon->logical_device_seq);
printf("Voltage Level: %s\n", is_empty(mon->voltage_level) ? "N/A" : mon->voltage_level);
printf("Terminal Connect: %s\n", is_empty(mon->terminal_connect) ? "N/A" : mon->terminal_connect);
printf("Timestamp: %s\n", is_empty(mon->timestamp) ? "N/A" : mon->timestamp);
printf("Status: %s\n", is_empty(mon->status) ? "N/A" : mon->status);
}
// 打印 terminal 结构体信息
void print_terminal(const terminal* tmnl) {
printf("GUID: %s\n", is_empty(tmnl->guid) ? "N/A" : tmnl->guid);//lnk20250506
printf("Terminal ID: %s\n", is_empty(tmnl->terminal_id) ? "N/A" : tmnl->terminal_id);
printf("Terminal Code: %s\n", is_empty(tmnl->terminal_code) ? "N/A" : tmnl->terminal_code);
printf("Organization Name: %s\n", is_empty(tmnl->org_name) ? "N/A" : tmnl->org_name);
printf("Maintenance Name: %s\n", is_empty(tmnl->maint_name) ? "N/A" : tmnl->maint_name);
printf("Station Name: %s\n", is_empty(tmnl->station_name) ? "N/A" : tmnl->station_name);
printf("Factory Name: %s\n", is_empty(tmnl->tmnl_factory) ? "N/A" : tmnl->tmnl_factory);
printf("Terminal Status: %s\n", is_empty(tmnl->tmnl_status) ? "N/A" : tmnl->tmnl_status);
printf("Device Type: %s\n", is_empty(tmnl->dev_type) ? "N/A" : tmnl->dev_type);
printf("Device Key: %s\n", is_empty(tmnl->dev_key) ? "N/A" : tmnl->dev_key);
printf("Device Series: %s\n", is_empty(tmnl->dev_series) ? "N/A" : tmnl->dev_series);
printf("Address: %s\n", is_empty(tmnl->addr_str) ? "N/A" : tmnl->addr_str);
printf("Port: %s\n", is_empty(tmnl->port) ? "N/A" : tmnl->port);
printf("Timestamp: %s\n", is_empty(tmnl->timestamp) ? "N/A" : tmnl->timestamp);
// 打印监测点信息,如果监测点字段为空,则打印 N/A
int i;
for (i = 0; i < 10 && !is_empty(tmnl->line[i].monitor_id); ++i) {
printf(" Monitor %d:\n", i + 1);
print_monitor(&tmnl->line[i]);
}
}
// 打印 trigger_update_xml_t 结构体信息
void print_trigger_update_xml(const trigger_update_xml_t* trigger_update) {
printf("Work Updates Count: %d\n", trigger_update->work_update_num);
printf("New Updates Count: %d\n", trigger_update->new_update_num);
printf("Delete Updates Count: %d\n", trigger_update->delete_update_num);
printf("Modify Updates Count: %d\n", trigger_update->modify_update_num);
printf("\nWork Updates:\n");
int i;
for (i = 0; i < trigger_update->work_update_num; ++i) {
printf("Work Update %d:\n", i + 1);
print_terminal(&trigger_update->work_updates[i]);
}
printf("\nNew Updates:\n");
for (i = 0; i < trigger_update->new_update_num; ++i) {
printf("New Update %d:\n", i + 1);
print_terminal(&trigger_update->new_updates[i]);
}
printf("\nDelete Updates:\n");
for (i = 0; i < trigger_update->delete_update_num; ++i) {
printf("Delete Update %d:\n", i + 1);
print_terminal(&trigger_update->delete_updates[i]);
}
printf("\nModify Updates:\n");
for (i = 0; i < trigger_update->modify_update_num; ++i) {
printf("Modify Update %d:\n", i + 1);
print_terminal(&trigger_update->modify_updates[i]);
}
}
void check_ledger_update()//lnk20250113
{
double now;
static double last_check_3s_config_time = 0.0;//初始化时间
now = sGetMsTime(); //当前时间
if ( fabs(now - last_check_3s_config_time) < 3*1000 ) //wait 3secs //当前进程任务执行时查看当前时间和上次执行时间间隔小于3秒不执行大于等于3秒往下执行
return;
// 动态分配内存给 trigger_ledger_update_xml 结构体
trigger_update_xml_t* trigger_ledger_update_xml = (trigger_update_xml_t*)malloc(sizeof(trigger_update_xml_t));
if (trigger_ledger_update_xml == NULL) {
printf("Memory allocation failed!\n");
return; // 如果内存分配失败,返回
}
//每次都初始化防止重复
memset(trigger_ledger_update_xml, 0, sizeof(trigger_update_xml_t));
//printf("check ledger update...trigger_ledger_update_xml:%d\n",trigger_ledger_update_xml->modify_update_num);//减少不必要的打印
last_check_3s_config_time = now; //记录本次运行时间
//判断是否满足执行条件,一个文件就是一个终端,读取文件后删除文件
if (APR_SUCCESS==parse_ledger_update_xml(trigger_ledger_update_xml)){ //处理台账更新文件,如果有可以更新或者添加台账的
//调试用
print_trigger_update_xml(trigger_ledger_update_xml);
//处理台账更新加台账锁lnk20250114
//pthread_mutex_lock(&mtx); printf("ledgerupdate hold lock !!!!!!!!!!!");
process_ledger_update(trigger_ledger_update_xml); //台账更新
//pthread_mutex_unlock(&mtx); printf("ledgerupdate free lock !!!!!!!!!!!");
}
// 使用完后释放动态分配的内存
free(trigger_ledger_update_xml);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef _OS_UNIX_
uint32_t get_freedisk_MB(uint32_t* totalSizeMB)
{
struct statfs diskInfo;
unsigned long long totalBlocks = 0;
unsigned long long totalSize = 0;
size_t mbTotalsize = 0;
size_t freeDisk=0;
size_t mbFreeDisk = 0;
statfs("/home/pq", &diskInfo);
totalBlocks = diskInfo.f_bsize;
totalSize = totalBlocks * diskInfo.f_blocks;
mbTotalsize = totalSize >> 20;
freeDisk = diskInfo.f_bfree*totalBlocks;
mbFreeDisk = freeDisk >> 20;
printf("/home/pq total=%dMB, free=%dMB \n", mbTotalsize, mbFreeDisk);
if (totalSizeMB)
*totalSizeMB = mbTotalsize;
return mbFreeDisk;
}
#else
uint32_t get_freedisk_MB(uint32_t* totalSizeMB) {
if (totalSizeMB)
*totalSizeMB = 1000;
return 5;
}
#endif
void check_disk_quota()
{
double now;
static double last_check_time = -10000000.0;
uint32_t freeSizeMB,totalSizeMB;
if(g_node_id != STAT_DATA_BASE_NODE_ID || g_front_seg_index != 1)//lnk20250527只有稳态进程1监测磁盘
return;
now = sGetMsTime();
if ( fabs(now - last_check_time) < 15*1000 ) //wait 15 secs
return;
last_check_time = now;
freeSizeMB = get_freedisk_MB(&totalSizeMB);
//printf("Current user disk free size: %dMB ,total size: %dMB \n",freeSizeMB,totalSizeMB);
if (freeSizeMB<g_min_free_size){
echo_warn2("Current user disk free size: %dMB < %dMB, please check!\n",freeSizeMB,g_min_free_size);
DIY_WARNLOG("process","【WARN】前置磁盘检测 当前磁盘的可用空间为%dMB,小于最小可用空间%dMB,请检查磁盘",freeSizeMB,g_min_free_size);
}
if ((freeSizeMB/(totalSizeMB/100+1) )<10){
echo_warn2("Current user disk free size percent < 10%%, free size: %dMB ,total size: %dMB ,please check!\n",
freeSizeMB,totalSizeMB);
DIY_WARNLOG("process","【WARN】前置磁盘检测 当前磁盘的可用空间的百分比小于10%%,可用空间为%dMB,总空间为%dMB,请检查磁盘",freeSizeMB,totalSizeMB);
}
}
void create_recall_xml()
{
//生成待补招xml文件
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)) {
DeletcRecallXml();
CreateRecallXml();
}
}
void Delete_recall_Xml(char* 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)) {
delete_recall_xml(id);
}
}
void Check_Recall_Config(char *id) //检查补招配置文件Recall.xml
{
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)) {
recall_xml_t recall_xml;
memset((char*)&recall_xml, 0, sizeof(recall_xml_t));
parse_recall_xml(&recall_xml,id); //解析补招文件,目录下所有属于这个监测点的文件都读取
process_recall_config(&recall_xml); //解析的补招数据赋值到全局变量
}
}
void CheckAllConnectedChannel()
{
chnl_usr_t *chnl_usr;
static uint32_t chnl_sequence_no = 0;
//lnk20250514如果进程启动没有台账则不往下执行等待台账更新
if(g_pt61850app->chnl_counts == 0)return;
//lnk20250514如果进程启动没有台账则不往下执行等待台账更新
//一次访问一个终端
do {
chnl_usr = g_pt61850app->chnl_usr[chnl_sequence_no];
chnl_sequence_no = (chnl_sequence_no+1) % g_pt61850app->chnl_counts;
if (chnl_usr == NULL) continue; // 安全防护lnk20250701
} while ( (g_onlyIP[0]!=0) && (strcmp(g_onlyIP,chnl_usr->ip_str)!=0) );
if (chnl_usr == NULL || chnl_usr->chnl == NULL || chnl_usr->chnl->ied == NULL || chnl_usr->chnl->ied->usr_ext == NULL) {
printf("chnl_usr or nested member is NULL, skip...\n");
return;
}
if(chnl_usr->m_state == CHANNEL_CONNECTED)
{
ChannelCheckIECReports(chnl_usr);//报告
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);//录波文件
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))
ChannelCheckIECLogs(chnl_usr);//补招文件
if ( (sGetMsTime() - chnl_usr->m_LastPosRespTime) > 15*1000 ) //wait 15 secs隔15秒获取一次响应两次没响应后关闭
{
char** varnames ;
int varnum;
ST_RET ret = mms_mvla_getnam(chnl_usr->net_info, VMD_SPEC, NULL,MMS_CLASS_DOM,g_pt61850app->mmsOpTimeout,
&varnames,&varnum,g_pt61850app->tmp_pool);
chnl_usr->m_LastPosRespTime = sGetMsTime();
if (ret == SD_SUCCESS) {
chnl_usr->m_NegRespTimes = 0;
}else {
printf("%d--------------\n", chnl_usr->m_NegRespTimes);
chnl_usr->m_NegRespTimes++;
}
}
if ( chnl_usr->m_NegRespTimes >=2 ) {
closeChannel(chnl_usr);//???特殊情况是否需要 mms_release_connection
chnl_usr->m_NegRespTimes = 0;
chnl_usr->m_LastPosRespTime = sGetMsTime();
}
}
}
void CheckNextNotConnectedChannel()
{
static uint32_t chnl_total_no = 0;
chnl_usr_t *chnl_usr;
//lnk20250514如果进程启动没有台账则不往下执行等待台账更新
if(g_pt61850app->chnl_counts == 0)return;
//lnk20250514如果进程启动没有台账则不往下执行等待台账更新
do {
chnl_usr = g_pt61850app->chnl_usr[chnl_total_no];
chnl_total_no = (chnl_total_no+1) % g_pt61850app->chnl_counts;
if (chnl_usr == NULL) continue; // 安全防护lnk20250701
} while ( (g_onlyIP[0]!=0) && (strcmp(g_onlyIP,chnl_usr->ip_str)!=0) );
if (chnl_usr == NULL || chnl_usr->chnl == NULL || chnl_usr->chnl->ied == NULL || chnl_usr->chnl->ied->usr_ext == NULL) {
printf("chnl_usr or nested member is NULL, skip...\n");
return;
}
//终端日志的key,lnk20250526
char full_key_t_c[256]; // 分配足够空间
char full_key_t_d[256]; // 分配足够空间
snprintf(full_key_t_c, sizeof(full_key_t_c), "terminal.%s.COM", ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->terminal_id);
snprintf(full_key_t_d, sizeof(full_key_t_d), "terminal.%s.DATA", ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->terminal_id);
//终端日志的key,lnk20250526
//10-11-01 22:03 beijing
if( ( (chnl_total_no+1)==g_pt61850app->chnl_counts) || (g_onlyIP[0]!=0) ){
if(g_pt61850app->initNum<255)
g_pt61850app->initNum++;
}
if(chnl_usr->m_state == CHANNEL_CONNECTING)//正在连接
{
MVL_REQ_PEND* reqCtrl= chnl_usr->m_reqCtrl ;
if( reqCtrl->done == SD_TRUE)
{
if(reqCtrl->result == SD_SUCCESS)
{
ALL_RCB_INFO *all_rcb_info;
echo_warn4("\nCHANNEL_CONNECTED %s:%d ,NetInfo= %x chnl_usr= %x \n",
chnl_usr->ip_str,chnl_usr->chnl->port,chnl_usr->net_info,chnl_usr);
//mq日志
((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->lastconnectstat = true;
((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->has_logged_disconnect = false;
DIY_WARNLOG(full_key_t_c,"【WARN】终端%s - ip端口:%s:%d连接成功", ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->terminal_id,chnl_usr->ip_str,chnl_usr->chnl->port);
mvl_free_req_ctrl(chnl_usr->m_reqCtrl);
chnl_usr->m_reqCtrl = NULL;
chnl_usr->m_state = CHANNEL_CONNECTED;
chnl_usr->net_info->user_ext = chnl_usr;
all_rcb_info = (ALL_RCB_INFO *)chk_calloc(1, sizeof (ALL_RCB_INFO));
all_rcb_info->rpt_typeids = &g_rpt_typeids;
if (chnl_usr->net_info->user_info != NULL) {
echo_warn("chnl_usr->net_info->user_info is not NULL\n");
}
chnl_usr->net_info->user_info = all_rcb_info;
chnl_usr->chnl->ied->status = STATUS_NORMAL;
chnl_usr->chnl->status = STATUS_NORMAL;
{
char comm_str[256];
memset(comm_str,0,256);
apr_snprintf(comm_str,sizeof(comm_str),"%16s:%d\t\tconnected",chnl_usr->ip_str,chnl_usr->chnl->port);
add_comm_log(comm_str);
FRONT_MP_NUM++;
}
//lnk202411-1添加连接成功的记录
ied_usr_t* ied_usr = (ied_usr_t*)chnl_usr->chnl->ied->usr_ext;
apr_time_t t_now = apr_time_now();
printf("msTime:%ld",t_now);
connectlog_pgsql(ied_usr->terminal_id,convertMsToDateTimeString(t_now),1);//1成功
}
else
{// solaris 9 上 224秒
int secsSince = (int)(sGetMsTime() - chnl_usr->m_StartConnectingTime)/1000 ;
ied_usr_t* ied_usr = (ied_usr_t*)chnl_usr->chnl->ied->usr_ext;
printf( "reqCtrl->result == FAIL, Since StartConnecting %i sec ,channel IP %s:%d \n",secsSince,chnl_usr->ip_str,chnl_usr->chnl->port);
//mq日志
if(true == ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->lastconnectstat){
((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->lastconnectstat = false;
((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->has_logged_disconnect = true;
DIY_WARNLOG(full_key_t_c,"【WARN】终端%s - ip/端口:%s:%d - 识别码/秘钥:%s/%s,从开始连接到目前已经%i秒,连接失败,断开连接!", ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->terminal_id,chnl_usr->ip_str,chnl_usr->chnl->port,((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->dev_series,((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->dev_key,secsSince);
}
else if(false == ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->lastconnectstat && false == ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->has_logged_disconnect){
((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->has_logged_disconnect = true;
DIY_WARNLOG(full_key_t_c,"【WARN】终端%s - ip/端口:%s:%d - 识别码/秘钥:%s/%s,从开始连接到目前已经%i秒,连接失败,断开连接!", ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->terminal_id,chnl_usr->ip_str,chnl_usr->chnl->port,((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->dev_series,((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->dev_key,secsSince);
}
mvl_free_req_ctrl(chnl_usr->m_reqCtrl);
chnl_usr->m_reqCtrl = NULL;
chnl_usr->net_info->rem_vmd = NULL;
chnl_usr->m_state = CHANNEL_DISCONNECTED;
chnl_usr->m_ClosedMsTime = sGetMsTime();
}
}
else
{
if ( (sGetMsTime() - chnl_usr->m_StartConnectingTime) > 300*1000 ) //300*1000 ) //wait 300 secs
{
ied_usr_t* ied_usr = (ied_usr_t*)chnl_usr->chnl->ied->usr_ext;
echo_warn2( "reqCtrl->done未完成,but time over 300 secs, close channel IP %s,NetInfo= %x ",chnl_usr->ip_str,chnl_usr->net_info);
//mq日志
if(true == ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->lastconnectstat){
((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->lastconnectstat = false;
((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->has_logged_disconnect = true;
DIY_WARNLOG(full_key_t_c,"【WARN】终端%s - ip端口:%s:%d,从开始连接到目前已经300秒,未能获取连接响应,断开连接!", ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->terminal_id,chnl_usr->ip_str,chnl_usr->chnl->port);
}
else if(false == ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->lastconnectstat && false == ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->has_logged_disconnect){
((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->has_logged_disconnect = true;
DIY_WARNLOG(full_key_t_c,"【WARN】终端%s - ip端口:%s:%d,从开始连接到目前已经300秒,未能获取连接响应,断开连接!", ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->terminal_id,chnl_usr->ip_str,chnl_usr->chnl->port);
}
if (chnl_usr->net_info->req_pend_list) {
echo_warn("reqCtrl->done未完成,but time over 300 secs!!!!!!!!\n");
mvl_free_req_ctrl(chnl_usr->m_reqCtrl);
chnl_usr->m_reqCtrl = NULL;
}
mms_release_connection(chnl_usr->net_info);
chnl_usr->net_info->rem_vmd = NULL;
chnl_usr->m_state = CHANNEL_DISCONNECTED;
chnl_usr->m_ClosedMsTime = sGetMsTime();
}
}
}
else if(chnl_usr->m_state == CHANNEL_DISCONNECTED)
{
if ( (sGetMsTime() - chnl_usr->m_ClosedMsTime) > NEXT_CONNECT_TIME ) //wait 10 secs
{
ST_RET ret;
ST_CHAR serverARName[32];
ied_usr_t *ied_usr = (ied_usr_t*)chnl_usr->chnl->ied->usr_ext;
apr_snprintf(serverARName,sizeof(serverARName),"%s:%d",chnl_usr->ip_str,chnl_usr->chnl->port);
if (chnl_usr->chnl->ied->cpucount != NULL && chnl_usr->chnl->ied->cpucount > 0 && ied_usr->dev_flag == ENABLE) {//2023-09-26 czy 如果line count<0 不需要连接//lnk20250121如果终端无效则不连接
//mq日志
//DIY_WARNLOG(full_key_t_c,"【WARN】重新连接终端%s - ip端口:%s:%d", ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->terminal_id,chnl_usr->ip_str,chnl_usr->chnl->port);
ret = mms_connectToServer(ied_usr->dev_key, ied_usr->dev_series, serverARName, &(chnl_usr->net_info), &(chnl_usr->m_reqCtrl));
if (ret == SD_SUCCESS)
{
echo_msg3("mms_connectToServer IP %s:%d ,NetInfo= %x \n", chnl_usr->ip_str, chnl_usr->chnl->port, chnl_usr->net_info);
chnl_usr->m_state = CHANNEL_CONNECTING;
chnl_usr->m_StartConnectingTime = sGetMsTime();
//mq日志
//DIY_WARNLOG(full_key_t_c,"【WARN】正在重新连接终端%s - ip端口:%s:%d - 识别码/秘钥:%s/%s", ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->terminal_id,chnl_usr->ip_str,chnl_usr->chnl->port,((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->dev_series,((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->dev_key);
}
else
{
chnl_usr->m_ClosedMsTime = sGetMsTime();
echo_warn3("FAILED: mms_connectToServer IP %s:%d ,NetInfo= %x \n", chnl_usr->ip_str, chnl_usr->chnl->port, chnl_usr->net_info);
//mq日志
//DIY_WARNLOG(full_key_t_c,"【WARN】重新连接终端%s - ip端口:%s:%d 失败!", ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->terminal_id,chnl_usr->ip_str,chnl_usr->chnl->port);
}
}
}
}
else if(chnl_usr->m_state == CHANNEL_DISCONNECTING) //need check timeout?会不会永远停留在这里
{
MVL_REQ_PEND* reqCtrl= chnl_usr->m_reqCtrl ;
if( reqCtrl->done == SD_TRUE)
{
echo_warn3( "CHANNEL_DISCONNECTING done %s:%d,NetInfo= %x ",chnl_usr->ip_str,chnl_usr->chnl->port,chnl_usr->net_info);
mvl_free_req_ctrl(chnl_usr->m_reqCtrl);
if(chnl_usr->net_info)
chnl_usr->net_info->user_ext = NULL;
chnl_usr->m_reqCtrl = NULL;
chnl_usr->net_info = NULL;
chnl_usr->m_state = CHANNEL_DISCONNECTED;
chnl_usr->m_ClosedMsTime = sGetMsTime();
//断联成功
ied_usr_t* ied_usr = (ied_usr_t*)chnl_usr->chnl->ied->usr_ext;
apr_time_t t_now = apr_time_now();
connectlog_pgsql(ied_usr->terminal_id,convertMsToDateTimeString(t_now),0);
//mq日志
DIY_WARNLOG(full_key_t_c,"【WARN】终端%s - ip端口:%s:%d 断连完成,关闭连接通道", ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->terminal_id,chnl_usr->ip_str,chnl_usr->chnl->port);
}
else
{
echo_warn2( "CHANNEL_DISCONNECTING waiting ... %s,NetInfo= %x ",chnl_usr->ip_str,chnl_usr->net_info);
if ( (sGetMsTime() - chnl_usr->m_StartDisconnectingTime) > 30*1000 ) // //wait 30 secs ?????
{
echo_warn2( "CHANNEL_DISCONNECTING reqCtrl->done未完成,but time over 180 secs, close channel IP %s,NetInfo= %x ",chnl_usr->ip_str,chnl_usr->net_info);
mvl_free_req_ctrl(chnl_usr->m_reqCtrl);
chnl_usr->net_info->user_ext = NULL;
mms_release_connection(chnl_usr->net_info);
chnl_usr->net_info->rem_vmd = NULL;
chnl_usr->m_reqCtrl = NULL;
chnl_usr->net_info = NULL;
chnl_usr->m_state = CHANNEL_DISCONNECTED;
chnl_usr->m_ClosedMsTime = sGetMsTime();
//超时断联成功
ied_usr_t* ied_usr = (ied_usr_t*)chnl_usr->chnl->ied->usr_ext;
apr_time_t t_now = apr_time_now();
connectlog_pgsql(ied_usr->terminal_id,convertMsToDateTimeString(t_now),0);
//mq日志
DIY_WARNLOG(full_key_t_c,"【WARN】终端%s - ip端口:%s:%d 断连未完成,但是已经超时180秒,关闭连接通道", ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->terminal_id,chnl_usr->ip_str,chnl_usr->chnl->port);
}
}
}
}
//////////////////////*********相关控制操作**********////////////////////////////////////////////////
static ST_RET Read_Named_Var(LD_info_t *LD_info,chnl_usr_t *chnl_usr,char* VarName,ST_INT type_id, ST_VOID *dataDest, ST_INT timeOut)
{
ST_RET ret= SD_FAILURE;
ST_CHAR *domName = LD_info->LD_name;
int Need_Destroy_Typeid = FALSE;
assert(chnl_usr && LD_info);
if (type_id==-1) {
Need_Destroy_Typeid = TRUE;
type_id= mms_var_type_id_create (chnl_usr->net_info,DOM_SPEC, domName, VarName,timeOut);
if (type_id==-1) ret = SD_FAILURE; else ret = SD_SUCCESS;
if (ret == SD_FAILURE) return SD_FAILURE;
}
ret=mms_named_var_read (chnl_usr->net_info,VarName,DOM_SPEC, domName,type_id, dataDest, timeOut);
if (Need_Destroy_Typeid)
if (type_id!=-1) mvl_type_id_destroy(type_id);
return ret;
}
static ST_RET Write_Named_Var(LD_info_t *LD_info,chnl_usr_t *chnl_usr,char* VarName,ST_INT type_id, ST_VOID *dataDest, ST_INT timeOut)
{
ST_RET ret= SD_FAILURE;
ST_CHAR *domName = LD_info->LD_name;
int Need_Destroy_Typeid = FALSE;
assert(chnl_usr && LD_info);
if (type_id==-1) {
Need_Destroy_Typeid = TRUE;
type_id= mms_var_type_id_create (chnl_usr->net_info,DOM_SPEC, domName, VarName,timeOut);
if (type_id==-1) ret = SD_FAILURE; else ret = SD_SUCCESS;
if (ret == SD_FAILURE) return SD_FAILURE;
}
ret=mms_named_var_write(chnl_usr->net_info,VarName,DOM_SPEC, domName,type_id, dataDest, timeOut);
if (Need_Destroy_Typeid)
if (type_id!=-1) mvl_type_id_destroy(type_id);
return ret;
}
/*
<EnumType id="ctlModelsEnum">
<EnumVal ord="0">status-only</EnumVal>
<EnumVal ord="1">direct-with-normal-security</EnumVal>
<EnumVal ord="2">sbo-with-normal-security</EnumVal>
<EnumVal ord="3">direct-with-enhanced-security</EnumVal>
<EnumVal ord="4">sbo-with-enhanced-security</EnumVal>
</EnumType>
*/
#define TICKS_PER_MIN 60LL*1000LL*1000LL
int pt61850_write_cn_file(chnl_usr_t *chnl_usr, ied_t *ied, char *rem_filename, char *only_filename_ret)
{
int ret ;
char loc_filename[128], loc_file_fullname[256], rem_file_fullname[256],only_filename_str[256];
char *only_filename,*the_full_file;
memset(loc_filename,0,sizeof(loc_filename));
memset(only_filename_str,0,sizeof(only_filename_str) );
strcat(only_filename_str,rem_filename);
only_filename = only_filename_str;
the_full_file = only_filename_str;
while ( *the_full_file != 0 ) {
if (*the_full_file == '/')
only_filename=the_full_file+1;
the_full_file++;
}
memset(rem_file_fullname,0,sizeof(rem_file_fullname) );
if (strstr(rem_filename,"/")!= NULL) {
strcat(rem_file_fullname,rem_filename);
}
else {
strcat(rem_file_fullname,g_pt61850app->accPath);
strcat(rem_file_fullname,rem_filename);
}
memset(loc_file_fullname,0,sizeof(loc_file_fullname) );
apr_snprintf(loc_file_fullname,sizeof(loc_file_fullname),"../comtrade/%s",only_filename);
apr_snprintf(only_filename_ret,256,"%s",only_filename);
printf("\n >>>>>>>>>****** Get wave file : %s from %s\n",loc_file_fullname,rem_file_fullname);
ret = mms_getFile(chnl_usr->net_info,loc_file_fullname,rem_file_fullname,g_pt61850app->mmsOpTimeout);
if (ret==SD_SUCCESS) {
}
else {
echo_warn3(">>>>>**** Error!!! %s mms_getFile wave file : %s from %s\n",chnl_usr->ip_str ,loc_file_fullname,rem_file_fullname);
}
return ret;
}
apr_status_t prepare_call_cn_wavelist(LD_info_t *LD_info, int FltNum)
{
int i;
for (i=0;i<256;i++) {
if (LD_info->FltNum[i]<=0) {
LD_info->FltNum[i] = FltNum;
break;
}
}
return APR_SUCCESS;
}
apr_status_t call_cn_wavelist(LD_info_t *LD_info )
{
int ret ;
int chnl_no;
chnl_usr_t *chnl_usr = NULL;
char **filenames;
int filenum ;
int i;
char file_match_str[65];
char *ldstr;
ied_t *ied = LD_info->ied;
ied_usr_t *ied_usr = GET_IEDEXT_ADDR(ied);
int have_new_files = FALSE;
//监测点日志的key,lnk20250526
char full_key_m_c[256]; // 分配足够空间
char full_key_m_d[256]; // 分配足够空间
snprintf(full_key_m_c, sizeof(full_key_m_c), "monitor.%s.COM", LD_info->mp_id);
snprintf(full_key_m_d, sizeof(full_key_m_d), "monitor.%s.DATA", LD_info->mp_id);
//监测点日志的key,lnk20250526
for (i=0;i<256;i++) {
if (LD_info->FltNum[i]<=0)
continue;
//LD名称规范四字节PQMn, 或者PQLDn ,有5个字节 但也只取4个字符进行匹配
ldstr = LD_info->LD_name+strlen(LD_info->LD_name)-4;
apr_snprintf(file_match_str,sizeof(file_match_str),"%s_%06d",ldstr,LD_info->FltNum[i]);
printf(">>>>>>>> IED [%d]: %s is calling cn wavefile, !!!!!!!! file_match_str=%s \n",ied->id,ied->name,file_match_str);
//mq日志
DIY_INFOLOG(full_key_m_c,"【NORMAL】监测点:%s - id:%s开始召唤录波文件", LD_info->name,LD_info->mp_id);
ret = SD_FAILURE;
filenum = 0;
assert(ied->chncount);
for(chnl_no=0 ; chnl_no<ied->chncount; chnl_no++) {
chnl_usr = (chnl_usr_t*)ied->channel[chnl_no].connect;
if (chnl_usr->m_state!=CHANNEL_CONNECTED)
continue;
ret = mms_mvla_fdir(chnl_usr->net_info,g_pt61850app->accPath,3*g_pt61850app->mmsOpTimeout,&filenames,&filenum,g_pt61850app->tmp_pool);
if (ret==SD_SUCCESS)
break;
}
if (ret==SD_SUCCESS) {
int cfg_idx,dat_idx;
char file_base_name[128];
char file_yyyymm[128];
char cfg_only_filename_ret[256];
char dat_only_filename_ret[256];
int ret2,ret3;
//mq日志
DIY_INFOLOG(full_key_m_c,"【NORMAL】监测点:%s - id:%s获取录波文件列表成功,开始匹配录波文件", LD_info->name,LD_info->mp_id);
//WW 2023-11-01将录波段号由字符串匹配修改为int的fltnum匹配
ret2 = parse_file_names_by_fltnum(LD_info->FltNum[i], ldstr, filenames, filenum, &cfg_idx, &dat_idx, file_base_name, file_yyyymm);
//WW 2023-11-01 end
if (ret2 !=APR_SUCCESS){
//mq日志
DIY_WARNLOG(full_key_m_c,"【WARN】监测点:%s - id:%s匹配录波文件失败", LD_info->name,LD_info->mp_id);
return ret2;
}
//完成录波文件记录在文件名dat/cfg_only_filename_ret中两个后缀不一样的录波文件要上传两次
ret2 = pt61850_write_cn_file(chnl_usr,ied,filenames[cfg_idx],cfg_only_filename_ret);
ret3 = pt61850_write_cn_file(chnl_usr,ied,filenames[dat_idx],dat_only_filename_ret);
have_new_files = TRUE;
/////////////////////////////////////////////////////////////////////////////////////////////上传文件
if (ret2==SD_SUCCESS && ret3==SD_SUCCESS ) { //两个文件都写好了
//mq日志
DIY_INFOLOG(full_key_m_c,"【NORMAL】监测点:%s - id:%s从终端获取录波文件成功,开始上传录波文件到web并更新暂态事件", LD_info->name,LD_info->mp_id);
QVVR_t *qvvr; //暂态事件
long long start_tm,trig_tm,end_tm;
ret2 = extract_timestamp_from_cfg_file(filenames[cfg_idx],&start_tm,&trig_tm);//提取文件的开始时间和触发时间
printf(">>>>>>>> extract_timestamp_from_cfg_file end \n");
if (ret2 ==APR_SUCCESS) {
//to find the paired qvvr by the time of trig_tm
printf(">>>>>>>> extract_timestamp_from_cfg_file success \n");
qvvr = find_qvvr_by_trig_tm(LD_info,trig_tm); //根据文件的触发时间查找检测点记录的匹配上的暂态事件
if (qvvr) {
char* uuid_cfg = (char*)malloc(65 * sizeof(char));//上传文件后获取到的路径
char* uuid_dat = (char*)malloc(65 * sizeof(char));
char* filename_cfg = (char*)malloc(100 * sizeof(char));//上传文件后获取到的文件名
char* filename_dat = (char*)malloc(100 * sizeof(char));
//lnk202411-5 记录web返回的路径+文件名
char* wavepath_cfg = (char*)malloc(256 * sizeof(char));
char* wavepath_dat = (char*)malloc(256 * sizeof(char));
/*上传.cfg和.dat两个文件*/
char linux_cmd[256] = {0};
printf(">>>>>>>> qvvr ok end \n");
apr_snprintf(linux_cmd,sizeof(linux_cmd),"./sftp_upload %s %s/%04d",cfg_only_filename_ret,file_yyyymm,LD_info->line_id);//没有使用
char loc_file_fullname_cfg[256];//本地文件名
memset(loc_file_fullname_cfg, 0, sizeof(loc_file_fullname_cfg));
apr_snprintf(loc_file_fullname_cfg, sizeof(loc_file_fullname_cfg), "/home/pq/FeProject/comtrade/%s", cfg_only_filename_ret);
char oss_file_fullname_cfg[256];//远端文件名
memset(oss_file_fullname_cfg, 0, sizeof(oss_file_fullname_cfg));
apr_snprintf(oss_file_fullname_cfg, sizeof(oss_file_fullname_cfg), "comtrade/wave/%s/%s", LD_info->mp_id, cfg_only_filename_ret);
if (FILE_FLAG == 1) {
PutOSS(oss_file_fullname_cfg, loc_file_fullname_cfg);//使用buffer推送文件
}
else if (FILE_FLAG == 2) {
OBSFile(loc_file_fullname_cfg, oss_file_fullname_cfg, "putObject");//这里并没有上传文件流
}
else if(FILE_FLAG==3){
WebAPI_Uds_Upload(UDS_UPLOAD_URL, loc_file_fullname_cfg, uuid_cfg, filename_cfg);//通过form-data上传文件
}
//LNK20241031使用JSON编码文件上传-具体的远端路径可以用原本代码的硬编码或者在配置文件中获取
else if (FILE_FLAG == 4) {
char mq_file_fullname_cfg[256]; // 远端文件名
memset(mq_file_fullname_cfg, 0, sizeof(mq_file_fullname_cfg));
// 使用修改后的文件名传入 apr_snprintf
apr_snprintf(mq_file_fullname_cfg, sizeof(mq_file_fullname_cfg), "comtrade/%s/",
LD_info->ied->channel[0].addr_str);
SOEFileWeb(loc_file_fullname_cfg, mq_file_fullname_cfg, wavepath_cfg);
printf("\n>>>>>>!! %s %s...... \n", mq_file_fullname_cfg, loc_file_fullname_cfg);
}
if (FILE_FLAG == 4) {
}
else{
printf("\n>>>>>>!! %s %s...... \n", oss_file_fullname_cfg, loc_file_fullname_cfg);
}
apr_snprintf(linux_cmd,sizeof(linux_cmd),"./sftp_upload %s %s/%04d",dat_only_filename_ret,file_yyyymm,LD_info->line_id);//先通过sftp上传才发json过去
char loc_file_fullname_dat[256];
memset(loc_file_fullname_dat, 0, sizeof(loc_file_fullname_dat));
apr_snprintf(loc_file_fullname_dat, sizeof(loc_file_fullname_dat), "/home/pq/FeProject/comtrade/%s", dat_only_filename_ret);
char oss_file_fullname_dat[256];
memset(oss_file_fullname_dat, 0, sizeof(oss_file_fullname_dat));
apr_snprintf(oss_file_fullname_dat, sizeof(oss_file_fullname_dat), "comtrade/wave/%s/%s", LD_info->mp_id, dat_only_filename_ret);
if (FILE_FLAG == 1) {
PutOSS(oss_file_fullname_dat, loc_file_fullname_dat);//使用buffer推送文件
}
else if (FILE_FLAG == 2) {
OBSFile(loc_file_fullname_dat, oss_file_fullname_dat, "putObject");//这里并没有上传文件流
}
else if(FILE_FLAG==3){
WebAPI_Uds_Upload(UDS_UPLOAD_URL, loc_file_fullname_dat, uuid_dat, filename_dat);//通过form-data上传文件
}
//LNK20241031使用JSON编码文件上传-具体的远端路径可以用原本代码的硬编码或者在配置文件中获取
else if (FILE_FLAG == 4) {
char mq_file_fullname_dat[256]; // 远端文件名
memset(mq_file_fullname_dat, 0, sizeof(mq_file_fullname_dat));
// 使用修改后的文件名传入 apr_snprintf
apr_snprintf(mq_file_fullname_dat, sizeof(mq_file_fullname_dat), "comtrade/%s/",
LD_info->ied->channel[0].addr_str);
SOEFileWeb(loc_file_fullname_dat, mq_file_fullname_dat, wavepath_dat);
printf("\n>>>>>>!! %s %s...... \n", mq_file_fullname_dat, loc_file_fullname_dat);
}
if (FILE_FLAG == 4) {
}
else{
printf("\n>>>>>>!! %s %s...... \n", oss_file_fullname_dat, loc_file_fullname_dat);
}
/*上传.cfg和.dat两个文件*/
/*上传消息*/
/////////////////////////////////////////////////////lnk20250520
//根据配置文件控制上传的暂态事件时间为北京时间的ms数
printf("~~~~~~~this dev type is %s~~~~~~~",ied_usr->dev_type);
XmlConfigC cfg1;
if (get_xml_config_by_dev_type(ied_usr->dev_type, &cfg1)) {
printf("UnitOfTimeUnit = %s\n", cfg1.UnitOfTimeUnit);
printf("ValueOfTimeUnit = %s\n", cfg1.ValueOfTimeUnit);
} else {
printf("读取失败,未找到 dev_type\n");
}
double s_or_ms;
long long utc_or_beijing;
if(strcmp(cfg1.UnitOfTimeUnit, "1") == 0){//持续时间上送的是秒1还是毫秒0
s_or_ms = 0.001;
}
else{
s_or_ms = 1.0;
}
if(strcmp(cfg1.ValueOfTimeUnit, "utc") == 0){//上送的是utc还是北京
utc_or_beijing = 28800000;
}
else{
utc_or_beijing = 0;
}
/////////////////////////////////////////////////////
//to send json of this qvvr and rdre
end_tm = (long long)(qvvr->QVVR_PerTime*1000) + trig_tm; //结束时间是持续时间加触发时间
if (FILE_FLAG == 3) {
size_t cfg_len = strlen(uuid_cfg) + strlen(filename_cfg) + 3; // 额外的3个字符用于"-"和结束符'\0'
char* cfg_result = (char*)malloc(cfg_len * sizeof(char)); // 动态分配足够的内存空间
size_t dat_len = strlen(uuid_dat) + strlen(filename_dat) + 3; // 额外的3个字符用于"-"和结束符'\0'
char* dat_result = (char*)malloc(dat_len * sizeof(char)); // 动态分配足够的内存空间
if (cfg_result != NULL && dat_result != NULL) {
snprintf(cfg_result, cfg_len, "%s-%s", uuid_cfg, filename_cfg); // 拼接字符串并确保不会溢出目标缓冲区
snprintf(dat_result, dat_len, "%s-%s", uuid_dat, filename_dat); // 拼接字符串并确保不会溢出目标缓冲区
ret3 = transfer_json_qvvr_data(g_node_id,
LD_info->line_id,
(double)qvvr->QVVR_Amg,
(double)qvvr->QVVR_PerTime/s_or_ms, //lnk20250520上送的暂态持续时间为毫秒
start_tm + utc_or_beijing, //lnk20250520上送的暂态时间为北京时间
end_tm + utc_or_beijing, //lnk20250520上送的暂态时间为北京时间
qvvr->QVVR_type,
cfg_result,
dat_result,
LD_info->mp_id,
qvvr->QVVR_Rptname,
ied_usr->dev_type);
}
free(cfg_result); // 使用完毕后释放动态分配的内存空间
free(dat_result); // 使用完毕后释放动态分配的内存空间
}
else if(FILE_FLAG==4)//lnk20241031发送暂态web消息
{
//内存分配部分不修改
size_t cfg_len = strlen(wavepath_cfg) + 1;
char* cfg_result = (char*)malloc(cfg_len * sizeof(char)); // 动态分配足够的内存空间
size_t dat_len = strlen(wavepath_dat) + 1;
char* dat_result = (char*)malloc(dat_len * sizeof(char)); // 动态分配足够的内存空间
if (cfg_result != NULL && dat_result != NULL) {
snprintf(cfg_result, cfg_len, wavepath_cfg); // 拼接字符串并确保不会溢出目标缓冲区
snprintf(dat_result, dat_len, wavepath_dat); // 拼接字符串并确保不会溢出目标缓冲区
ret3 = transfer_json_qvvr_data(g_node_id, //这个参数没有使用
LD_info->line_id, //监测点序号
(double)qvvr->QVVR_Amg,
(double)qvvr->QVVR_PerTime/s_or_ms, //lnk20250520上送的暂态持续时间为毫秒
qvvr->QVVR_time + utc_or_beijing, //lnk20250520上送的暂态时间为北京时间 //这里不使用文件中的开始时间start_tm因为会影响写库使用QVVR的时间QVVR_time//lnk20250311
end_tm + utc_or_beijing, //lnk20250520上送的暂态时间为北京时间
qvvr->QVVR_type, //伏值、持续时间、开始时间、结束时间、暂态类型
cfg_result,
dat_result, //两个文件路径
LD_info->mp_id,
qvvr->QVVR_Rptname,
ied_usr->dev_type);//监测点号,文件和监测点暂态事件匹配上的暂态报告名,终端类型
}
free(cfg_result); // 使用完毕后释放动态分配的内存空间
free(dat_result); // 使用完毕后释放动态分配的内存空间
}
else if (FILE_FLAG == 1 || FILE_FLAG == 2)
{
size_t cfg_len = strlen(oss_file_fullname_cfg) + strlen(cfg_only_filename_ret) + 3; // 额外的3个字符用于"-"和结束符'\0'
char* cfg_result = (char*)malloc(cfg_len * sizeof(char)); // 动态分配足够的内存空间
size_t dat_len = strlen(oss_file_fullname_dat) + strlen(dat_only_filename_ret) + 3; // 额外的3个字符用于"-"和结束符'\0'
char* dat_result = (char*)malloc(dat_len * sizeof(char)); // 动态分配足够的内存空间
if (cfg_result != NULL && dat_result != NULL) {
snprintf(cfg_result, cfg_len, "%s-%s", oss_file_fullname_cfg, cfg_only_filename_ret); // 拼接字符串并确保不会溢出目标缓冲区
snprintf(dat_result, dat_len, "%s-%s", oss_file_fullname_dat, dat_only_filename_ret); // 拼接字符串并确保不会溢出目标缓冲区
ret3 = transfer_json_qvvr_data(g_node_id,
LD_info->line_id,
(double)qvvr->QVVR_Amg,
(double)qvvr->QVVR_PerTime/s_or_ms, //lnk20250520上送的暂态持续时间为毫秒
start_tm + utc_or_beijing, //lnk20250520上送的暂态时间为北京时间
end_tm + utc_or_beijing, //lnk20250520上送的暂态时间为北京时间
qvvr->QVVR_type,
cfg_result,
dat_result,
LD_info->mp_id,
qvvr->QVVR_Rptname,
ied_usr->dev_type);
}
free(cfg_result); // 使用完毕后释放动态分配的内存空间
free(dat_result); // 使用完毕后释放动态分配的内存空间
}
/*上传消息*/
qvvr->used_status = QVVR_DATA_NOT_USED;//上传信息后这个qvvr的状态置为未使用
free(uuid_cfg);
free(uuid_dat);
free(filename_cfg);
free(filename_dat);
//lnk 202411-5
free(wavepath_cfg);
free(wavepath_dat);
}
}
}
}
else {
if (ied && chnl_usr){
echo_warn2("mms_mvla_fdir Failed: IED [%d] %s \n", ied->id , chnl_usr->ip_str) ;
//mq日志
DIY_WARNLOG(full_key_m_c,"【WARN】监测点:%s - id:%s召唤录波文件失败", LD_info->name,LD_info->mp_id);
}
return APR_EAGAIN;
}
LD_info->FltNum[i]= -1;
}
if (have_new_files)
clear_old_comtrade_files();
return APR_SUCCESS;
}
//lnk 2024-11-4 添加时间转换函数
char* convertMsToDateTimeString(int64_t usTime) {
// 确保时间戳是有效的
if (usTime < 0) {
return "Invalid timestamp";
}
// 将 `apr_time_t` 微秒us转换为 `time_t` 秒s
time_t seconds = usTime / 1000000;
// 用 `struct tm` 变量存储时间
struct tm timeInfo;
// **使用 `localtime_r()` 考虑系统时区**
localtime_r(&seconds, &timeInfo);
// 静态分配的字符数组存储转换后的字符串
static char buffer[30];
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &timeInfo);
return buffer;
}