/** * @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 #include "rdb_client.h" #include #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; extern int RECALL_ONLY_FLAG; //lnk20260309添加一个全局变量,标志是否只运行补招程序 //lnk20250115end #ifdef DEBUG_SISCO SD_CONST static ST_CHAR *SD_CONST thisFileName = __FILE__; #endif #ifdef _OS_UNIX_ #include #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 ; iednon_clients; iedno++) { ied = g_node->clients[iedno]; for(chnl_no=0 ; chnl_nochncount; 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 ; cpunocpucount; cpuno++) { LD_info = &(ied_usr->LD_info[cpuno]); for(rpt_no=0 ; rpt_norptcount; 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_CODE(full_key_t_c,LOG_CODE_COMM,"【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 ; cpunocpucount; cpuno++) { LD_info = &(ied_usr->LD_info[cpuno]); //遍历监测点 //监测点日志的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 if (LD_info->cpuno==0){ // 仅在还没达到5次上限时打印 if (!LD_info->has_logged_regist) { LD_info->registcount++; if (LD_info->registcount <= 5) { DIY_ERRORLOG_CODE(full_key_m_c,LOG_CODE_REPORT,"【ERROR】监测点:%s - id:%s报告触发失败,监测点逻辑标识号为0,请检查装置对应的台账信息是否正确", LD_info->name,LD_info->mp_id); } // 到5次就不再打印,并标记 if (LD_info->registcount > 5) { LD_info->has_logged_regist = true; DIY_WARNLOG_CODE(full_key_m_c,LOG_CODE_REPORT,"【WARN】监测点:%s - id:%s监测点逻辑标识号错误日志已达本次记录上限,不再输出,请检查装置对应的台账信息是否正确", LD_info->name, LD_info->mp_id); } } continue; } for(rpt_no=0 ; rpt_norptcount; rpt_no++) { //遍历报告(映射文件中读取的报告控制) 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日志 // 仅在还没达到5次上限时打印 if (!LD_info->has_logged_regist) { LD_info->registcount++; if (LD_info->registcount <= 5) { DIY_WARNLOG_CODE(full_key_m_c,LOG_CODE_REPORT, "【WARN】监测点:%s - id:%s注册报告失败,报告名:%s", LD_info->name, LD_info->mp_id, rpt_inst_name); } // 到5次就不再打印,并标记 if (LD_info->registcount > 5) { LD_info->has_logged_regist = true; DIY_WARNLOG_CODE(full_key_m_c,LOG_CODE_REPORT,"【WARN】监测点:%s - id:%s注册报告失败日志已达本次注册上限,不再输出,请检查装置icd和映射文件是否匹配或者装置存在过多连接", LD_info->name, LD_info->mp_id); } } } 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日志 LD_info->has_logged_regist = FALSE; LD_info->registcount = 0; DIY_WARNLOG_CODE(full_key_m_c,LOG_CODE_REPORT,"【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_CODE(full_key_m_c,LOG_CODE_REPORT,"【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 ; cpunocpucount; 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 } } //lnk20250821时间转换:秒转本地时间 static void sec_to_timestr(long long sec, char *out, size_t out_sz) { time_t t = (time_t)sec; struct tm tmv; localtime_r(&t, &tmv); // 本地时间 strftime(out, out_sz, "%Y-%m-%d %H:%M:%S", &tmv); } 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 ; cpunocpucount; cpuno++) { LD_info = &(ied_usr->LD_info[cpuno]); //监测点日志的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 //日志控制块缺失 if (LD_info->logcount<=0){ // 仅在还没达到5次上限时打印 if (!LD_info->has_logged_regist) { LD_info->registcount++; if (LD_info->registcount <= 5) { DIY_ERRORLOG_CODE(full_key_m_c,LOG_CODE_RECALL,"【ERROR】监测点:%s - id:%s补招数据失败,监测点缺少日志控制块,请检查装置对应的装置类型是否有配对的icd模型", LD_info->name,LD_info->mp_id); } // 到5次就不再打印,并标记 if (LD_info->registcount > 5) { LD_info->has_logged_regist = true; DIY_WARNLOG_CODE(full_key_m_c,LOG_CODE_RECALL,"【WARN】监测点:%s - id:%s缺少日志控制块日志已达本次记录上限,不再输出,请检查装置对应的装置类型是否有配对的icd模型", LD_info->name, LD_info->mp_id); } } continue; } loginfo = LD_info->loginfo[0] ; apr_sleep(apr_time_from_sec(1) / 10); 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 = 28800;//秒 DIY_WARNLOG_CODE(full_key_m_c,LOG_CODE_RECALL,"【WARN】监测点:%s - id:%s开始补招数据,下发补招时间为utc时间,监测点对应装置型号:%s", LD_info->name,LD_info->mp_id,ied_usr->dev_type); } else{ utc_or_beijing = 0; DIY_WARNLOG_CODE(full_key_m_c,LOG_CODE_RECALL,"【WARN】监测点:%s - id:%s开始补招数据,下发补招时间为beijing时间,监测点对应装置型号:%s", LD_info->name,LD_info->mp_id,ied_usr->dev_type); } ////////////////////////////////////////////////////////////// //记录本次补招的时间范围 long long min_start_sec = LLONG_MAX; long long max_end_sec = 0; ///////////////////////////////////////////////////////////// for (i = 0; i < LD_info->autorecallcount; i++) { // ===== [新增] 累计全局最小/最大区间(直接用原始 start/end)===== if (LD_info->autorecall[i]->start < min_start_sec) { min_start_sec = LD_info->autorecall[i]->start; } if (LD_info->autorecall[i]->end > max_end_sec) { max_end_sec = LD_info->autorecall[i]->end; } // ===== [新增结束] ===== 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->start_time - utc_or_beijing * APR_USEC_PER_SEC;//下发utc时间需要减去8小时-秒 loginfo->end_time = loginfo->end_time - utc_or_beijing * APR_USEC_PER_SEC; ///////////////////////////////////////////////////// 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_CODE(full_key_m_c,LOG_CODE_RECALL,"【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); // ===== [新增] 组装上送的补招开始/结束时间字符串(本地时区,直接“秒转字符串”)===== char recallStartDate[32] = {0}; char recallEndDate[32] = {0}; if (min_start_sec == LLONG_MAX) { // 理论上不会进来:有 autorecallcount != 0。兜底写成当前时间 time_t nowt = time(NULL); sec_to_timestr((long long)nowt, recallStartDate, sizeof(recallStartDate)); sec_to_timestr((long long)nowt, recallEndDate, sizeof(recallEndDate)); } else { sec_to_timestr(min_start_sec, recallStartDate, sizeof(recallStartDate)); sec_to_timestr(max_end_sec, recallEndDate, sizeof(recallEndDate)); } //拼接result const char *ld_name_safe = (LD_info->name && LD_info->name[0]) ? LD_info->name : "-"; char result_buf[256]; snprintf(result_buf, sizeof(result_buf), "监测点:%s 补招时间最小值和最大值:%s ~ %s 本次补招执行结束", ld_name_safe, recallStartDate, recallEndDate); // guid 固定 "12345",step 固定 "2" //send_reply_to_kafka_c("12345", "2", result_buf); send_reply_to_kafka_recall_c("12345", "2", result_buf,LD_info->mp_id,recallStartDate,recallEndDate); // ===== [新增结束] ===== DIY_WARNLOG_CODE(full_key_m_c,LOG_CODE_RECALL,"【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; iwork_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; iwork_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_one_LD_real_soe_report_shoud_register(trigger[i].dev_idx,trigger[i].line_id); //clear_rpt_counter_by_trigger(trigger_work); clear_rpt_counter_by_trigger(trigger); trigger->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; iwork_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; irptinfo[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; iwork_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; ilogcount<=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; imodify_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; ichncount; 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; ichncount; 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 中的某个 ied,index 为删除的索引 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); printf("Log Level: %d\n", mon->log_level); } // 打印 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); printf("Log Level: %d\n", tmnl->log_level); // 打印监测点信息,如果监测点字段为空,则打印 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); //处理台账更新添加控制标志 if (RECALL_ONLY_FLAG != 1 || (g_node_id != STAT_DATA_BASE_NODE_ID)) { process_ledger_update(trigger_ledger_update_xml); //台账更新 } else{ printf("only process recall config, skip ledger update\n"); DIY_WARNLOG_CODE("process",LOG_CODE_SPACE_ALARM,"【WARN】当前配置为仅日志模式,统计数据进程跳过台账更新"); } } // 使用完后释放动态分配的内存 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 (freeSizeMBchnl_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_CODE(full_key_t_c,LOG_CODE_COMM,"【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_CODE(full_key_t_c,LOG_CODE_COMM,"【WARN】终端%s - ip/端口:%s:%d,从开始连接到目前已经%i秒,连接失败,断开连接!", ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->terminal_id,chnl_usr->ip_str,chnl_usr->chnl->port,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_CODE(full_key_t_c,LOG_CODE_COMM,"【WARN】终端%s - ip/端口:%s:%d,从开始连接到目前已经%i秒,连接失败,断开连接!", ((ied_usr_t*)(chnl_usr->chnl->ied->usr_ext))->terminal_id,chnl_usr->ip_str,chnl_usr->chnl->port,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(); //断联成功 apr_time_t t_now = apr_time_now(); connectlog_pgsql(ied_usr->terminal_id,convertMsToDateTimeString(t_now),0); //更新状态 chnl_usr->chnl->ied->status = STATUS_OVERTIME; chnl_usr->chnl->status = STATUS_OVERTIME; } } 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_CODE(full_key_t_c,LOG_CODE_COMM,"【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_CODE(full_key_t_c,LOG_CODE_COMM,"【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(); //断联 apr_time_t t_now = apr_time_now(); connectlog_pgsql(ied_usr->terminal_id,convertMsToDateTimeString(t_now),0); //更新状态 chnl_usr->chnl->ied->status = STATUS_OVERTIME; chnl_usr->chnl->status = STATUS_OVERTIME; } } } 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); //更新状态 chnl_usr->chnl->ied->status = STATUS_BREAKOFF; chnl_usr->chnl->status = STATUS_BREAKOFF; //mq日志 DIY_WARNLOG_CODE(full_key_t_c,LOG_CODE_COMM,"【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); //更新状态 chnl_usr->chnl->ied->status = STATUS_BREAKOFF; chnl_usr->chnl->status = STATUS_BREAKOFF; //mq日志 DIY_WARNLOG_CODE(full_key_t_c,LOG_CODE_COMM,"【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; } /* status-only direct-with-normal-security sbo-with-normal-security direct-with-enhanced-security sbo-with-enhanced-security */ #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; } //判断暂态记录非空,lnk20250818 int ld_has_qvvr_nonempty(const LD_info_t* info) { if (!info) return 0; /* 规则1:索引计数 > 0 直接认为非空 */ if (info->qvvr_idx > 0) return 1; /* 规则2:扫描数组,任意一项出现被使用/有时间/有名称/有数值 即认为非空 */ int i; for (i = 0; i < QVVR_NUM; ++i) { const QVVR_t* it = &info->qvvr[i]; if (it->used_status != 0 && it->QVVR_time != 0 && it->QVVR_PerTime != 0) { return 1; } } /* 都没有则为空 */ return 0; } 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_nochncount; 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_CODE(full_key_m_c,LOG_CODE_COMTRADE_FILE,"【WARN】监测点:%s - id:%s前置记录的录波事件上传的录波号段%d与从装置获取的录波文件列表匹配失败,装置没有对应的号段的录波文件,清除该记录", LD_info->name,LD_info->mp_id,LD_info->FltNum[i]); //lnk20250819装置没有对应的文件时清除录波号段 printf("监测点:%s - id:%s前置记录的录波事件上传的录波号段%d与从装置获取的录波文件列表匹配失败,装置没有对应的号段的录波文件,清除该记录", LD_info->name,LD_info->mp_id,LD_info->FltNum[i]); LD_info->FltNum[i] = -1; 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从终端获取录波文件成功", 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) { DIY_INFOLOG(full_key_m_c,"【NORMAL】监测点:%s - id:%s提取录波文件时间成功", LD_info->name,LD_info->mp_id); //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 = 0;//61850库已经转换过 } 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(ld_has_qvvr_nonempty(LD_info))//防止手动录波日志还一直上送 { //获取时间类型lnk20250520 XmlConfigC cfg; if (get_xml_config_by_dev_type(ied_usr->dev_type, &cfg)) { } else { printf("读取失败,未找到 dev_type\n"); } if(strcmp(cfg.UnitOfTimeUnit, "1") == 0){//持续时间上送的是秒1还是毫秒0 DIY_ERRORLOG_CODE(full_key_m_c,LOG_CODE_COMTRADE_FILE,"【ERROR】监测点:%s - id:%s 匹配录波文件失败,录波号段:%d,录波文件的开始时间:%lld,触发时间:%lld,映射配置的暂态持续时间单位是秒", LD_info->name,LD_info->mp_id,LD_info->FltNum[i],start_tm,trig_tm); } else{ DIY_ERRORLOG_CODE(full_key_m_c,LOG_CODE_COMTRADE_FILE,"【ERROR】监测点:%s - id:%s 匹配录波文件失败,录波号段:%d,录波文件的开始时间:%lld,触发时间:%lld,映射配置的暂态持续时间单位是毫秒", LD_info->name,LD_info->mp_id,LD_info->FltNum[i],start_tm,trig_tm); } } } } else{ DIY_ERRORLOG_CODE(full_key_m_c,LOG_CODE_COMTRADE_FILE,"【ERROR】监测点:%s - id:%s 下载录波文件%s和%s失败,录波号段:%d", LD_info->name,LD_info->mp_id,filenames[cfg_idx],filenames[dat_idx],LD_info->FltNum[i]); } } else { if (ied && chnl_usr){ echo_warn2("mms_mvla_fdir Failed: IED [%d] %s \n", ied->id , chnl_usr->ip_str) ; //mq日志 DIY_WARNLOG_CODE(full_key_m_c,LOG_CODE_COMTRADE_FILE,"【WARN】监测点:%s - id:%s召唤录波文件列表失败,放弃这个号段", LD_info->name,LD_info->mp_id); LD_info->FltNum[i] = -1; } 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; }