/**
* @file:    $RCSfile: mmsclient.c,v $
* @brief:   $IEC 61850 Protocol  
*
* @version: $Revision: 1.12 $
* @date:    $Date: 2022/11/28 07:13:13 $
* @author:  $Author: lizhongming $
* @state:   $State: Exp $
*
* @latest:  $Id: mmsclient.c,v 1.12 2022/11/28 07:13:13 lizhongming Exp $
*
*/

//$Header: /JoyProject/jspqfe/src/pt61850netd_pqfe/source/mms/mmsclient.c,v 1.12 2022/11/28 07:13:13 lizhongming Exp $
/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*      	1986 - 2005, All Rights Reserved		        */
/*   (c) Copyright GDNZ Company, Inc.,	*/
/*      	2006- 2006, All Rights Reserved		        */
/*									*/
/* MODULE NAME : mmsclient.c                                              	*/
/* PRODUCT(S)  : MMSEASE-LITE						*/
/*									*/
/* MODULE DESCRIPTION : 						*/
/*									*/
/*   This module include the functions would be use by others							*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*	NONE								*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/*2006-06-28  Lzm  101    Change the file to be functions used by others .			*/
/* 09/09/05  JRB     55    Don't print BUILD_NUM.			*/
/* 09/10/97  MDE     01    MMS-LITE V4.0 Release			*/
/************************************************************************/

#define HARD_CODED_CFG   	
#if 1
  /* move outside #if to enable	*/

  #define ACSE_AUTH_ENABLED	/* for password authentication without	*/
				/* security				*/
/*  S_SEC_ENABLED		for certificate based authentication &	*/
/*				SSL encryption, put this define in	*/
/*				application's DSP/MAK file 		*/
#endif

/************************************************************************/
#include "rdb_client.h"

#include "glbtypes.h"
#include "sysincs.h"
#include "signal.h"
#include "mmsdefs.h"
#include "mms_pvmd.h"
#include "mms_pvar.h"
#include "mms_vvar.h"
#include "mms_err.h"
#include "mms_pcon.h"
#include "asn1defs.h"
#include "mmsop_en.h"

#include "lean_a.h" 	//add by lzm
#include "tp0_sock.h"
#include "mvl_defs.h"
#include "acse2.h"

#include "mvl_acse.h"
#include "mvl_log.h"
#include "tp4api.h"
#include "clntobj.h"
#include "mmsclient.h"
#include "tp4.h"

#include "../mms/db_interface.h"

#include <ctype.h> //lnk20241119
#include "../cfg_parse/custom_printf.h"//lnk20250225

#include "../log4cplus/log4.h"

extern uint32_t	g_node_id;
extern char		subdir[128];
unsigned int	g_no_auth = 0;
DEV_TYPE_LIST												gDev_rcb_list; //全局报告控制块链表
#if defined(MVL_GOOSE_SUPPORT)
/* Need parts of "iecgoose" sample app	*/
#include "iec_demo.h"	/* definitions from "iecgoose" sample app	*/
/* Prototype here because it needs struct def from "iec_demo.h".	*/
IEC_GOOSE_RX_USER_INFO *goose_init (ST_VOID);
#endif

/*----------------------------------------------------------------------*/
/* NOTE: The MMS-EASE Lite Secured applications (Client, Server) 	*/
/*	 are designed to work with Security Toolkit for MMS-EASE Lite	*/
/*	 (LITESECURE-000-001).						*/
/*       The S_SEC_ENABLED delimits the security related code.		*/
/*----------------------------------------------------------------------*/
#if defined(S_SEC_ENABLED) && defined(ACSE_AUTH_ENABLED)
#error Only one S_SEC_ENABLED or ACSE_AUTH_ENABLED may be defined
#endif

#if defined(S_SEC_ENABLED)
#include "mmslusec.h"
#endif

#include "fkeydefs.h"
#ifdef kbhit	/* CRITICAL: fkeydefs may redefine kbhit. DO NOT want that.*/
#undef kbhit
#endif

/************************************************************************/
/* For debug version, use a static pointer to avoid duplication of 	*/
/* __FILE__ strings.							*/
/************************************************************************/

#ifdef DEBUG_SISCO
SD_CONST static ST_CHAR *SD_CONST thisFileName = __FILE__;
#endif

/************************************************************************/
/* Identify server information						*/

IDENT_RESP_INFO identify_response_info =
  {
  "Joyway JoyCom",  		/* Vendor 	*/
  MMSLITE_NAME,  	/* Model  	*/
  MMSLITE_VERSION,  0,	/* Version 	*/
  };


/************************************************************************/
	
extern pt61850app_t *g_pt61850app;

extern TP0_CONN *tp0_conn_arr;	/* ptr to array of "max_num_conns" structs	*/
		
static ST_VOID disc_ind_fun (MVL_NET_INFO *cc, ST_INT discType);

ST_VOID test_simple_requests (MVL_NET_INFO *clientNetInfo);


ST_INT timeOut = 60;	/* timeout (in seconds) passed to waitReqDone	*/
 

INIT_INFO callingInitInfo;
INIT_INFO initRespInfo;

MVL_CFG_INFO mvlCfg;
RPT_TYPEIDS g_rpt_typeids;

/************************************************************************/
#if defined(HARD_CODED_CFG)

DIB_ENTRY localDibTable[] =
  {
    {
    0, 		/* reserved		*/
    "local1",	/* name[65]		*/
    SD_TRUE,	/* local		*/
    SD_TRUE,	/* AP_title_pres	*/
      {		/* AP_title		*/
      4,	/* num_comps		*/
      1, 3, 9999, 105
      },
    SD_FALSE,	/* AP_inv_id_pres	*/
    0,		/* AP_invoke_id		*/
    SD_TRUE,	/* AE_qual_pres		*/
    33,		/* AE_qual		*/
    SD_FALSE,	/* AE_inv_id_pres	*/
    0,		/* AE_invoke_id		*/
#if defined(REDUCED_STACK)
    105		/* adlcAddr		*/
#else
      {				/* PRES_ADDR				*/
      4,			/* P-selector length			*/
      {'\x00','\x00','\x00','\x01'},	/* P-selector				*/
      2,			/* S-selector length			*/
      "\x00\x01",		/* S-selector				*/
      TP_TYPE_TCP,// change by lzm		/* Transport type			*/
      2,			/* T-selector length			*/
      "\x00\x01",		/* T-selector				*/
      0,			/* Network address length		*/
      ""			/* Network address (ignored on local)	*/
      }
#endif
    }
  };

#endif

ST_VOID set_rem_dib_table_size (ST_INT size)
{
	mvlCfg.num_calling = size; 
	num_rem_dib_entries = size;
	rem_dib_table = malloc(sizeof(DIB_ENTRY)*size);	
}

ST_VOID add_rem_dib_table (ST_INT pos,ST_CHAR *remAr,unsigned short port)
{
	DIB_ENTRY *de;
	de = &rem_dib_table[pos];
	
	de->reserved = 0;
	/*de->name = (ST_CHAR *)chk_malloc(strlen(remAr) + 1);
	strcpy(de->name, remAr);*/
	de->name = (ST_CHAR *)chk_malloc(32);
	apr_snprintf(de->name,32,"%s:%d",remAr,port);

	de->local = SD_TRUE;
	de->AP_title.num_comps = 4;
	de->AP_title.comps[0] = 1;
	de->AP_title.comps[1] = 3;
	de->AP_title.comps[2] = 9999;
	de->AP_title.comps[3] = 106;
	de->AP_title_pres = SD_TRUE;    /* defaults to FALSE    */
	
	de->AE_qual_pres = SD_TRUE;   /* defaults to FALSE    */
	de->AE_qual = 33;
	
	de->AP_inv_id_pres = SD_FALSE;
	de->AP_invoke_id = 0;
	de->AE_inv_id_pres = SD_FALSE;
	de->AE_invoke_id = 0;
	
	de->pres_addr.psel[0] = 0;
	de->pres_addr.psel[1] = 0;
	de->pres_addr.psel[2] = 0;
	de->pres_addr.psel[3] = 1;
	de->pres_addr.psel_len = 4;
	de->pres_addr.ssel[0] = 0;
	de->pres_addr.ssel[1] = 1;
	de->pres_addr.ssel_len = 2;
	de->pres_addr.tp_type = TP_TYPE_TCP;
	de->pres_addr.tsel[0] = 0;
	de->pres_addr.tsel[1] = 1;
	de->pres_addr.tsel_len = 2;	
	de->pres_addr.nsap_len = 4;
	de->pres_addr.netAddr.ip = tp0_convert_ip(remAr);
//	printf (" %x",	de->pres_addr.netAddr.ip );
//	de->pres_addr.netAddr.nsap[0] = '\xAC';	
//	de->pres_addr.netAddr.nsap[1] = '\x15';
//	de->pres_addr.netAddr.nsap[2] = '\x64';
//	de->pres_addr.netAddr.nsap[3] = '\x64';
	de->pres_addr.port = port;//IPPORT_RFC1006;	
}

static ST_VOID init_mem (ST_VOID);
#if defined (DEBUG_SISCO)
// ST_VOID init_log_cfg (ST_VOID);
#endif

/************************************************************************/
/* Global variables							*/
/************************************************************************/
typedef struct
  {
  ST_INT cmd_term_num_va;	/* num of vars received in CommandTermination*/
  ST_CHAR oper_name [MAX_IDENT_LEN +1];
  } MY_CONTROL_INFO;
/* NOTE: this global variable can be avoided if a (MVL_CONTROL_INFO *)	*/
/*   is saved in the "user_info" member of MVL_NET_INFO, but to do so,	*/
/*   you must make sure "user_info" is not used for something else.	*/
MY_CONTROL_INFO my_control_info;

/************************************************************************/
/*			mms_var_type_id_create				*/
/* Get the type for any domain-specific var and create a type_id.	*/
/************************************************************************/
ST_INT mms_var_type_id_create (MVL_NET_INFO *clientNetInfo, ST_INT scope,
	ST_CHAR *dom_name, ST_CHAR *var_name, int iTimeOut)
  {
//MVL_REQ_PEND *reqCtrl;
//reqCtrl 必须初始化为 NULL，防止 mvla_getvar 失败后释放野指针
    MVL_REQ_PEND *reqCtrl = NULL;

GETVAR_REQ_INFO getvar_req;
ST_INT type_id = -1;	/* start with invalid type id	*/
ST_RET ret;

//参数合法性检查
    if (clientNetInfo == NULL || dom_name == NULL || var_name == NULL)
    {
        printf("[GETVAR] invalid arg netInfo=%p dom=%p var=%p\n",
               clientNetInfo, dom_name, var_name);
        return -1;
    }

    //结构体清零，避免未初始化字段导致异常
    memset(&getvar_req, 0, sizeof(getvar_req));

  /* Get the type of this "Oper" attribute & create type.	*/
  /* Would be more efficient to do this just once before this function.*/
  getvar_req.req_tag = GETVAR_NAME;
  
  getvar_req.name.object_tag = scope;	/* set scope	*/
  if (scope == DOM_SPEC)
  	getvar_req.name.domain_id= dom_name;
  getvar_req.name.obj_name.vmd_spec = var_name;

  //增加调试打印，确认崩溃点
    printf("[GETVAR] start dom=%s var=%s\n", dom_name, var_name);
  
  ret = mvla_getvar (clientNetInfo, &getvar_req, &reqCtrl);

    //打印 mvla_getvar 返回值和 reqCtrl
    printf("[GETVAR] mvla_getvar ret=0x%X reqCtrl=%p\n", ret, reqCtrl);

  if (ret == SD_SUCCESS){
    ret = waitReqDone (reqCtrl, iTimeOut);
    //打印 waitReqDone 返回值
     printf("[GETVAR] wait ret=0x%X\n", ret);
  }
    
  if (ret != SD_SUCCESS){
    echo_warn2 ("Error getting type of variable '%s' in domain '%s'\n", var_name, dom_name);
  } 
  else
    {
    /* Don't care about name so pass NULL.			*/
    //type_id = mvl_type_id_create (NULL, reqCtrl->u.getvar.resp_info->type_spec.data,
    //                     reqCtrl->u.getvar.resp_info->type_spec.len);
    //严格检查 resp_info/type_spec，避免空指针崩溃
      if (ret == SD_SUCCESS &&
          reqCtrl != NULL &&
          reqCtrl->u.getvar.resp_info != NULL &&
          reqCtrl->u.getvar.resp_info->type_spec.data != NULL &&
          reqCtrl->u.getvar.resp_info->type_spec.len > 0)
      {
          type_id = mvl_type_id_create(
              NULL,
              reqCtrl->u.getvar.resp_info->type_spec.data,
              reqCtrl->u.getvar.resp_info->type_spec.len);

          printf("[GETVAR] create type_id=%d len=%d\n",
                 type_id,
                 reqCtrl->u.getvar.resp_info->type_spec.len);
      }
      else
      {
          printf("[GETVAR] failed dom=%s var=%s ret=0x%X\n",
                 dom_name, var_name, ret);
      }
    }
    //只有 reqCtrl 非空才释放
  if (reqCtrl != NULL)mvl_free_req_ctrl (reqCtrl);	/* Done with request struct	*/
  return (type_id);
  }

/************************************************************************/
/*			mms_var_type_id_create				*/
/* Get the type for any domain-specific var and create a type_id.	*/
/************************************************************************/
ST_INT mms_get_datatype_from_type_id (ST_INT type_id, ST_UCHAR* datatype,ST_INT *len)
{
	ST_RET ret; 
	RUNTIME_CTRL rt_ctrl;
	 
	if(type_id < 0) return	SD_FAILURE;  
	
	 ret = mvl_get_runtime (type_id, &rt_ctrl.rt_first,&rt_ctrl.rt_num);
	 if (ret == SD_SUCCESS)
	    {//只返回 rt_ctrl.rt_num 中第一个数据的类型和 长度	    
	      RUNTIME_TYPE *rt_type = &rt_ctrl.rt_first[0];
	  	  *datatype = rt_type->el_tag;
	  	  *len = rt_type->u.p.el_len;
		}
	return ret;
}

ST_INT32 u_safe_chang_double_to_int32 (ST_DOUBLE value, ST_CHAR *FileName, ST_INT32 line)
{
	ST_INT32 gInt32;
	if(value > LONG_MAX) {
		gInt32 = LONG_MAX;
		MVL_LOG_NERR2 ("%s:%d Change double to int32 max overflow", FileName, line);	
		printf("	%s:%ld Change double to int32 max overflow!\n", FileName, line);
	}
	else if(value < LONG_MIN) {
		gInt32 = LONG_MIN;
		MVL_LOG_NERR2 ("%s:%d Change double to int32 min overflow", FileName, line);	
		printf("	%s:%ld Change double to int32 mxin overflow!\n", FileName, line);
	}
	else {
		gInt32 = (ST_INT32)(value + 0.5);
	}

	return(gInt32);
}

void u_get_current_utc_time(MMS_UTC_TIME *utc_time)
{
	ST_DOUBLE	temp = 0.0;
	apr_time_t	us;
	apr_time_t	tm = apr_time_now();

	us = tm%1000000LL;
	utc_time->secs = (ST_UINT32)(tm/1000000LL);
	temp = (ST_DOUBLE)us/1000000 * 0x00FFFFFF;
	utc_time->fraction = u_safe_chang_double_to_int32(temp, __FILE__, __LINE__);
	utc_time->qflags = 0x00;
}

void convert_apr_time_to_utc_time(apr_time_t tm,MMS_UTC_TIME *utc_time)
{
	ST_DOUBLE	temp = 0.0;
	apr_time_t	us;
	 
	us = tm%1000000LL;
	utc_time->secs = (ST_UINT32)(tm/1000000LL);
	temp = (ST_DOUBLE)us/1000000 * 0x00FFFFFF;
	utc_time->fraction = u_safe_chang_double_to_int32(temp, __FILE__, __LINE__);
	utc_time->qflags = 0x00;
}

 

void mms_waitEventForever()
{
  while (1)
	{
	  doCommService ();	
	}
}

void exit_MMS()
{
  dyn_mem_ptr_statistics (0);	
  mvl_end_acse ();
  dyn_mem_ptr_status ();	/* is everything freed up?	*/	
}
/************************************************************************/
/*                      init_MMS                        				*/
/************************************************************************/
void init_MMS()
  {
  ST_RET ret;
  	  	
  setbuf (stdout, NULL);    /* do not buffer the output to stdout   */
  setbuf (stderr, NULL);    /* do not buffer the output to stderr   */

  printf ("%s Version %s\n", MMSLITE_NAME, MMSLITE_VERSION);
#if defined(S_SEC_ENABLED)
  printf ("%s Version %s\n", S_SEC_LITESECURE_NAME, S_SEC_LITESECURE_VERSION_STR);
#endif
  puts (MMSLITE_COPYRIGHT);


#ifdef S_MT_SUPPORT
  /* init glbsem explicitly to avoid auto-initialization calls from multiple threads */
  if ((ret = gs_init()) != SD_SUCCESS)
    {
    printf ("gs_init() failed");
    exit (1);
    }
#endif

  /* init stime explicitly to avoid auto-initialization calls from multiple threads */
  if ((ret = stimeInit()) != SD_SUCCESS)
    {
    printf ("stimeInit() failed");
    exit (1);
    }

#if defined(NO_GLB_VAR_INIT)
  mvl_init_glb_vars ();
#endif

  init_mem ();		/* Set up memory allocation tools 		*/


  echo_msg2 ("%s Version %s \n", MMSLITE_NAME, MMSLITE_VERSION);

/* We want to know about connection activity				*/
  u_mvl_disc_ind_fun     = disc_ind_fun;

#if !defined(MAP30_ACSE)

#if defined(HARD_CODED_CFG)
  num_loc_dib_entries = sizeof(localDibTable)/sizeof(DIB_ENTRY);
  loc_dib_table = localDibTable;
//  num_rem_dib_entries = sizeof(rem_dib_table)/sizeof(DIB_ENTRY);

  memset(&gDev_rcb_list, 0, sizeof(DEV_TYPE_LIST)); //WW 2023-08-29 全局装置报告指针初始化
  
 // tp4_config();		/* see tp4_hc.c		*/	
  tp0_cfg.max_tpdu_len = 8192;//8192;
  tp0_cfg.max_num_conns = 8;		/* see tp4_hc.c		*/
   //printf("OLD tp0_cfg.keepalive = %i \n" ,tp0_cfg.keepalive );
  tp0_cfg.keepalive = 1;
  //printf("OLD tp0_cfg.max_spdu_outst = %i \n" ,tp0_cfg.max_spdu_outst );
  tp0_cfg.max_spdu_outst = 50;
#if 0	/* Only call config functions necessary for stack being used.	*/
  tp4_config();		/* see tp4_hc.c		*/
  clnp_config();	/* see clnp_hc.c	*/  
  adlcConfigure();	/* see adlc_hc.c	*/
#endif
  /* Fill in mvlCfg.	*/
 
  mvlCfg.num_called = 0;
  mvlCfg.max_msg_size = 320000; //to enlage to fit the 主设备保护
  strcpy (mvlCfg.local_ar_name, "local1");

#else /* #if defined(HARD_CODED_CFG) */

/* Read the configuration from a file */
  ret = osicfgx ("osicfg.xml", &mvlCfg);	/* This fills in mvlCfg	*/
  if (ret != SD_SUCCESS)
    {
    printf ("\n Stack configuration failed, err = 0x%X. Check configuration.\n", ret );
    exit (1);
    }
#endif /* HARD_CODED_CFG */

#else	/* MAP30_ACSE */
  mvlCfg.num_calling = 4;
  mvlCfg.num_called = 0;
  mvlCfg.max_msg_size = 8000;
  strcpy (mvlCfg.local_ar_name, "local1");
#endif /* MAP30_ACSE */


#if defined(S_SEC_ENABLED)
  /* set security configuration file name, MUST be before mvl_start_acse*/
  secManCfgXmlFile = "secmancfg.xml";
#endif  /* defined(S_SEC_ENABLED) */

  ret = mvl_start_acse (&mvlCfg);	/* MAKE SURE mvlCfg is filled in*/
  if (ret != SD_SUCCESS)
    {
    printf ("\n mvl_start_acse () failed, err = 0x%X.\n", ret );
    exit (1);
    }

  if ((ret = rpt_typeids_find (&g_rpt_typeids)) != SD_SUCCESS)
    {
    printf ("rpt_typeids_find () failed = 0x%X.\n", ret );
    exit (1);
    }

#if defined(MVL_GOOSE_SUPPORT)
  /* NOTE: this GOOSE initialization must be AFTER mvl_start_acse so	*/
  /*   that required types can be found.				*/
  goosehandle = goose_init ();
#endif

  chk_malloc(1);	/* memory marker */

#if !defined(NO_KEYBOARD) && !defined(_WIN32) && !defined(__QNX__)
  //term_init ();	 /* makes getchar work right	*/
#endif
}
//
 

ST_RET mms_mvla_identify (MVL_NET_INFO  *net_info,char** ident,int iTimeout)
{
  static char ident_str[256];
  MVL_REQ_PEND *reqCtrl;
  IDENT_RESP_INFO *ident_resp;
  ST_RET ret = mvla_identify (net_info, &reqCtrl);

  memset(ident_str,0,sizeof(ident_str));
  if (ret == SD_SUCCESS)
    ret = waitReqDone (reqCtrl, iTimeout);
  if (ret == SD_SUCCESS)
    {		
		ident_resp = reqCtrl->u.ident.resp_info;
		apr_snprintf( ident_str, sizeof(ident_str), "Vendor = %s, Model = %s, Rev = %s", ident_resp->vend,
			ident_resp->model, ident_resp->rev);
		printf ("%s\n",ident_str);
		*ident = ident_str;
    }
  else
    printf ("\n mvl_identify () Error, ret = 0x%X.", ret);
  mvl_free_req_ctrl (reqCtrl);	
  return ret;		
}

#if (MMS_STATUS_EN & REQ_EN)
ST_RET mms_mvla_status (MVL_NET_INFO  *net_info,int iTimeout)
{
  ST_RET ret;
  STATUS_REQ_INFO status_req;	
  MVL_REQ_PEND *reqCtrl;
  status_req.extended = SD_FALSE;
  ret = mvla_status (net_info, &status_req, &reqCtrl);
  if (ret == SD_SUCCESS)
    ret = waitReqDone (reqCtrl, iTimeout);
  if (ret != SD_SUCCESS)
    printf ("\n mvl_status () Error, ret = 0x%x ", ret);
  else
    {
//    printf (" mvl_status OK \n");
//    printf ("\n   logical status  = %d", reqCtrl->u.status.resp_info->logical_stat);
//    printf ("\n   physical status = %d", reqCtrl->u.status.resp_info->physical_stat);
//    if (reqCtrl->u.status.resp_info->local_detail_pres == SD_FALSE)
//      printf ("\n   No local detail");
//    else
//      {
//      printf ("\n   Local detail present");
//      printf ("\n     Local detail len = %d bits", 
//	       reqCtrl->u.status.resp_info->local_detail_len);
//      }
    }
  mvl_free_req_ctrl (reqCtrl);
  return ret;
}
#endif

#if (MMS_OBTAINFILE_EN & REQ_EN)
ST_RET mms_mvla_obtfile (MVL_NET_INFO  *net_info,ST_CHAR *srcfilename,
	ST_CHAR *destfilename,int iTimeout)
{
  MVL_REQ_PEND *reqCtrl;
  ST_RET ret = mvla_obtfile (net_info, srcfilename, destfilename, &reqCtrl);
  if (ret == SD_SUCCESS)
    ret = waitReqDone (reqCtrl, iTimeout);
  if (ret != SD_SUCCESS)
    printf ("\n mvl_obtfile () Error obtaining file '%s', ret = 0x%X.",
            srcfilename, ret);
  else
    printf ("\n mvl_obtfile () of '%s' OK.", srcfilename);
  mvl_free_req_ctrl (reqCtrl);
  return ret;
}
#endif

#if (MMS_FDELETE_EN & REQ_EN)
ST_RET mms_mvla_fdelete (MVL_NET_INFO  *net_info,ST_CHAR *file_to_delete,int iTimeout)
{
  MVL_REQ_PEND *reqCtrl;
  ST_RET ret = mvla_fdelete (net_info, file_to_delete, &reqCtrl);
  if (ret == SD_SUCCESS)
    ret = waitReqDone (reqCtrl, iTimeout);
  if (ret != SD_SUCCESS)
    printf ("\n mvl_fdelete () Error deleting file '%s', ret = 0x%X.",
            file_to_delete, ret);
  else
    printf ("\n mvl_fdelete () of '%s' OK.", file_to_delete);
  mvl_free_req_ctrl (reqCtrl);
  return ret;
}
#endif

//用于发送文件目录请求，获取远程设备的文件列表，并返回符合条件的文件名。
#if (MMS_FDIR_EN & REQ_EN)
ST_RET mms_mvla_fdir (MVL_NET_INFO  *net_info,ST_CHAR *filespec,int iTimeout,
	char*** filenames ,int* filenum,apr_pool_t *pool)
{
  MVL_REQ_PEND *reqCtrl;
  MVL_FDIR_RESP_INFO *fdir_resp;
  ST_RET ret;
  ST_CHAR ca_filename[255];
  ST_BOOLEAN more_follows = SD_FALSE; 
  ST_INT i;
  char**	filenames2;

  *filenum = 0; 
//        "*.*",	/* fs_name	*/
  do { 
  	if (more_follows)         
  		ret = mvla_fdir (net_info, filespec, ca_filename ,&reqCtrl);
  	else 
		ret = mvla_fdir (net_info, filespec, NULL ,&reqCtrl);
	if (ret == SD_SUCCESS)
		ret = waitReqDone (reqCtrl, iTimeout);
	if (ret != SD_SUCCESS)	{
		printf ("\n mvl_fdir () Error, ret = 0x%X. \n", ret);
		mvl_free_req_ctrl (reqCtrl);	/* CRITICAL:		*/
		break;
	}
	else
	{
		fdir_resp = reqCtrl->u.fdir.resp_info;
		more_follows = fdir_resp->more_follows;		
		printf ("\n mvl_fdir results: num=%i \n",fdir_resp->num_dir_ent);
		
		filenames2 = apr_pcalloc( pool,(*filenum+fdir_resp->num_dir_ent)*sizeof(char*) );
		for ( i = 0; i < *filenum; ++i)
			filenames2[i] =(*filenames)[i];
		(*filenames) = filenames2;


		for (i = 0; i < fdir_resp->num_dir_ent;  i++)
		{
		  //printf ("\n   File #%d: %s", i, fdir_resp->dir_ent[i].filename);
		  (*filenames)[*filenum+i] = apr_pstrdup(pool,fdir_resp->dir_ent[i].filename);
	 	}  
	}
	if (fdir_resp->num_dir_ent >0 ) 
		strcpy(ca_filename,fdir_resp->dir_ent[fdir_resp->num_dir_ent-1].filename);
	*filenum += fdir_resp->num_dir_ent;
	mvl_free_req_ctrl (reqCtrl);	/* CRITICAL:		*/
  } while (more_follows);
  return ret;
}

#endif


#if (MMS_GETNAMES_EN & REQ_EN)   
//#define MMS_CLASS_VAR		0
//#define MMS_CLASS_SCAT_ACC	1
//#define MMS_CLASS_VARLIST	2
//#define MMS_CLASS_TYPE		3
//#define MMS_CLASS_SEM		4
//#define MMS_CLASS_EV_COND	5
//#define MMS_CLASS_EV_ACT	6
//#define MMS_CLASS_EV_ENROLL	7
//#define MMS_CLASS_JOU		8
//#define MMS_CLASS_DOM		9
//#define MMS_CLASS_PI	       10
//#define MMS_CLASS_OPER_STA     11
	
ST_RET mms_mvla_getnam (MVL_NET_INFO  *net_info,ST_INT scope, 
	ST_CHAR *domName,ST_INT16 mms_class, int iTimeout,
	char*** varnames ,int* varnum,apr_pool_t *pool)
{
  MVL_REQ_PEND *reqCtrl;
  NAMELIST_REQ_INFO getnam_req;
  NAMELIST_RESP_INFO *getnam_resp;
  ST_RET ret;
  ST_CHAR continue_after[255];
  ST_BOOLEAN more_follows = SD_FALSE;
  ST_CHAR **nptr;
  ST_INT i;
  char**	varnames2;

  *varnum=0; 
  getnam_req.cs_objclass_pres = SD_FALSE;
  getnam_req.obj.mms_class = mms_class;  

  getnam_req.objscope = scope;
  if (scope == DOM_SPEC)
  	strcpy (getnam_req.dname, domName);
    //getnam_req.dname = domName;	/* set domain name	*/  
  getnam_req.cont_after_pres = SD_FALSE;  
  do {	
	  if (more_follows)
	  	{
	  		getnam_req.cont_after_pres = SD_TRUE;
	  		strcpy(getnam_req.continue_after,continue_after);
	  	}	      
	  ret = mvla_getnam (net_info, &getnam_req, &reqCtrl);
	  if (ret == SD_SUCCESS)
	    ret = waitReqDone (reqCtrl, iTimeout);
	  if (ret != SD_SUCCESS)	{
	    printf ("\n mvl_getnam () Error, ret = 0x%x.", ret);
	    more_follows = SD_FALSE;
		}
	  else
	    {
//	    printf ("\n mvl_getnam OK");
	    getnam_resp = (NAMELIST_RESP_INFO *) reqCtrl->u.ident.resp_info;
	    more_follows = getnam_resp->more_follows;
		//if (more_follows)	{
	 //     printf ("\n   More Follows : TRUE");
		//}
		//else	{
	 //     printf ("\n   More Follows : FALSE");
		//}
	    printf("★");//
		//printf ("mms_mvla_getname   %d Names returned . \n",getnam_resp->num_names);

		varnames2 = apr_pcalloc( pool,(*varnum+getnam_resp->num_names)*sizeof(char*) );
		for ( i = 0; i < *varnum; ++i)
			varnames2[i] =(*varnames)[i];
		(*varnames) = varnames2;

	    //printf ("\n   Name List :");		
	    nptr = (ST_CHAR **) (getnam_resp + 1);	
	    for ( i = 0; i < getnam_resp->num_names; ++i)
	      (*varnames)[*varnum +i] = apr_pstrdup(pool, nptr[i] );
	      //printf ("\n%d     %s ",total+i,nptr[i]);
	    if (getnam_resp->num_names >0 ) 
	    	strcpy(continue_after,nptr[getnam_resp->num_names-1]);
	    *varnum += getnam_resp->num_names;	  
	    }	  	    
	  mvl_free_req_ctrl (reqCtrl);
  } while (more_follows); 
  return ret; 
} 
#endif

ST_RET mms_rcb_setEntryID(MVL_NET_INFO *netInfo, ST_CHAR *domName,
	ST_CHAR *rcbName, RPT_TYPEIDS *rpt_typeids, ST_INT timeOut, ST_UINT8* pEntryID)
{
	ST_RET ret;
	ST_CHAR varName [MAX_IDENT_LEN + 1];
	apr_snprintf (varName, sizeof(varName), "%s$EntryID", rcbName);
	ret = mms_named_var_write (netInfo, varName, DOM_SPEC, domName, rpt_typeids->ostring8, (ST_CHAR *)pEntryID, timeOut);
	if (ret == SD_SUCCESS)	
		printf ("\n Report setEntryID successed,EntryID: %02x %02x %02x %02x %02x %02x %02x %02x \n",
		*pEntryID, *(pEntryID+1), *(pEntryID+2), *(pEntryID+3), *(pEntryID+4), *(pEntryID+5), 
		*(pEntryID+6), *(pEntryID+7) );
	else
		echo_warn8("\n Report setEntryID failed,EntryID: %02x %02x %02x %02x %02x %02x %02x %02x \n",
		*pEntryID, *(pEntryID+1), *(pEntryID+2), *(pEntryID+3), *(pEntryID+4), *(pEntryID+5), 
		*(pEntryID+6), *(pEntryID+7) );
	return ret;
}

/************************************************************************/
/*			 mms_register_iec_rpt					*/
/* This function sets up to receive IEC-61850 or UCA reports on this connection*/
// RCB (e.g. "LLN0$BR$PosReport")
/************************************************************************/
RCB_INFO* mms_register_iec_rpt (MVL_NET_INFO *clientNetInfo, RPT_TYPEIDS *rpt_typeids,
		ST_CHAR *dom_name,ST_CHAR *rcb_name,ST_INT timeOut, 	
		ST_INT scanRate,ST_UCHAR trgops,ST_UINT8* pEntryID,ST_UINT8* OptFlds	)
{
ST_RET ret;
RCB_INFO *rcb_info;		/* UCA/IEC Report Control Block info	*/
//ST_UCHAR OptFlds [2];		/* 10 bit bitstring but only allow write of 9 bits*/
ST_UCHAR TrgOps [1];		/* 8 bit bitstring			*/
  rcb_info = rcb_info_create (clientNetInfo, dom_name, rcb_name, rpt_typeids, timeOut);
  if (rcb_info)
    {
    //先注销报告先
    ST_BOOLEAN RptDisEna = 0;		/* 0 = disable the report	*/
    ST_CHAR varName [MAX_IDENT_LEN + 1];
    apr_snprintf (varName,sizeof(varName), "%s$RptEna", rcb_name);
    mms_named_var_write (clientNetInfo, varName, DOM_SPEC, dom_name, rpt_typeids->mmsbool, (ST_CHAR *) &RptDisEna, timeOut);
    	
    printf ("rcb_info->numDsVar = %d\n", rcb_info->numDsVar);
    /* Enable options we want to see in report.	*/
    //OptFlds [0] = 0;
    /* These options valid for IEC BRCB, IEC URCB, or UCA.	*/
    //BSTR_BIT_SET_ON(OptFlds, OPTFLD_BITNUM_SQNUM);
    //BSTR_BIT_SET_ON(OptFlds, OPTFLD_BITNUM_TIMESTAMP);
    //BSTR_BIT_SET_ON(OptFlds, OPTFLD_BITNUM_REASON);
    //BSTR_BIT_SET_ON(OptFlds, OPTFLD_BITNUM_DATSETNAME);
    /* NOTE: these options ONLY available for IEC-61850 (not UCA)
     * SUBSEQNUM is only set by the server, so don't try to set it.
     */
    if (rcb_info->rcb_type == RCB_TYPE_IEC_BRCB)
      {	/* These only valid for IEC BRCB	*/
//      BSTR_BIT_SET_ON(OptFlds, OPTFLD_BITNUM_BUFOVFL);
      BSTR_BIT_SET_ON(OptFlds, OPTFLD_BITNUM_ENTRYID);
   		if ((pEntryID[0]!=0xff)||(pEntryID[1]!=0xff)||(pEntryID[2]!=0xff)||(pEntryID[3]!=0xff)
   			||(pEntryID[4]!=0xff)||(pEntryID[5]!=0xff)||(pEntryID[6]!=0xff)||(pEntryID[7]!=0xff) )
				mms_rcb_setEntryID(clientNetInfo, dom_name, rcb_name, rpt_typeids, timeOut, pEntryID);
      }
    //if (rcb_info->rcb_type != RCB_TYPE_UCA)
    //  {	/* These only valid for IEC BRCB or IEC URCB	*/
    //  BSTR_BIT_SET_ON(OptFlds, OPTFLD_BITNUM_DATAREF);
    //  BSTR_BIT_SET_ON(OptFlds, OPTFLD_BITNUM_CONFREV);
    //  }

    TrgOps [0] = trgops;	 
    ret = rcb_enable (clientNetInfo, dom_name, rcb_name, OptFlds, TrgOps,
                      scanRate*1000,		/* Integrity Period (ms)	*/
                      rpt_typeids,
                      timeOut);		/* timeout (s)	*/

	if(ret == SD_SUCCESS)
		{
			list_add_last(&((ALL_RCB_INFO *)clientNetInfo->user_info)->rcb_info_list, rcb_info);	/* add RCB to list*/
		}
	else{
			rcb_info_destroy(rcb_info); 
			rcb_info = NULL;
			echo_warn2 ("rcb_enable error for dom='%s', rcb='%s'\n", dom_name, rcb_name);  
		}   
    }
  else
  	{
      printf ("rcb_info_create error for dom='%s', rcb='%s'\n", dom_name, rcb_name);
      rcb_info = NULL;
    }
  return (rcb_info);
}

///////////////////////////////////////////
//WW 2023-08-29 增加设备类型处理,优化报告触发速度
RCB_INFO* mms_register_iec_rpt_by_devtype(MVL_NET_INFO *clientNetInfo, RPT_TYPEIDS *rpt_typeids,
	ST_CHAR *dom_name, ST_CHAR *rcb_name, ST_INT timeOut, ST_CHAR *dev_type, ST_CHAR *ip, int port,
	ST_INT scanRate, ST_UCHAR trgops, ST_UINT8* pEntryID, ST_UINT8* OptFlds)
{
	ST_RET ret;
	//ST_UCHAR OptFlds [2];		/* 10 bit bitstring but only allow write of 9 bits*/
	ST_UCHAR TrgOps[1];		/* 8 bit bitstring			*/


	//一、装置类型查找判断---------------------------------------------------------------------------------------------------------------------------
	ST_BOOLEAN bFindDevType = 0; //是否在链表中查找到装置类型	
	Dev_RCB_INFO *dev_rcb = NULL;//装置报告结构指针
	for (dev_rcb = gDev_rcb_list.dev_rcb_info_Head; dev_rcb != NULL; dev_rcb = (Dev_RCB_INFO *)list_get_next(gDev_rcb_list.dev_rcb_info_Head, dev_rcb)) //①遍历 全局装置报告控制块链表
	{
		//LOG_INFO("(报告触发)遍历gDev_rcb_list链表, dev_type_name= %s", dev_rcb->dev_type_name); //仅调试使用！ zl 2019-12-24 00:16:31
		if ((strcmp(dev_rcb->dev_type_name, dev_type) == 0)) //匹配 装置类型
		{
			bFindDevType = 1; //查找到装置类型
			//LOG_INFO("(报告触发)匹配到装置类型，dev_type_name= %s", dev_rcb->dev_type_name); //仅调试使用！ zl 2019-12-24 00:23:55
			break;
		}
	} //遍历 装置类型链表 结束
	if (!bFindDevType) //未查找到装置类型
	{
		dev_rcb = (Dev_RCB_INFO *)chk_calloc(1, sizeof(Dev_RCB_INFO)); //调用 申请连续内存空间，并初始化为0，返回首地址指针，相当于New
		strcpy(dev_rcb->dev_type_name, dev_type); //装置类型
		//LOG_INFO("(报告触发)新增装置类型，dev_type_name= %s", dev_rcb->dev_type_name); //仅调试使用！ zl 2019-12-24 00:23:55
		list_add_last(&gDev_rcb_list.dev_rcb_info_Head, dev_rcb); //添加 全局装置报告控制块链表
	}

	//二、装置IP、端口号查找判断---------------------------------------------------------------------------------------------------------------------------------------------------
	ST_BOOLEAN bFindIpPort = 0; //是否在链表中查找到装置IP、端口号
	Dev_IP_Port_INFO *dev_info = NULL; //装置IP、端口号结构指针
	for (dev_info = dev_rcb->dev_ip_port_list; dev_info != NULL; dev_info = (Dev_IP_Port_INFO *)list_get_next(dev_rcb->dev_ip_port_list, dev_info)) //②遍历 全局装置IP、端口链表
	{
		//LOG_INFO("(报告触发)遍历dev_ip_port_list链表, dev_type_name= %s, ip= %s, port= %d", dev_rcb->dev_type_name, dev_info->IP, dev_info->Port); //仅调试使用！ zl 2019-12-24 00:16:31
		if ((strcmp(dev_info->IP, ip) == 0) && (dev_info->Port == port)) //匹配 装置IP && 端口号
		{
			bFindIpPort = 1; //查找到装置IP && 端口号
			//LOG_INFO("(报告触发)匹配到装置dev_type_name= %s, IP= %s， Port= %d", dev_rcb->dev_type_name, dev_info->IP, dev_info->Port); //仅调试使用！ zl 2019-12-24 00:23:55
			break;
		}
	}//遍历 装置IP、端口链表 结束
	if (!bFindIpPort) //未查找到装置IP、端口
	{
		dev_info = (Dev_IP_Port_INFO *)chk_calloc(1, sizeof(Dev_IP_Port_INFO)); //调用 申请连续内存空间，并初始化为0，返回首地址指针，相当于New

		strcpy(dev_info->IP, ip); //装置IP
		dev_info->Port = port; //端口号
		//LOG_INFO("(报告触发)新增装置dev_type_name= %s, IP= %s， Port= %d", dev_rcb->dev_type_name, dev_info->IP, dev_info->Port); //仅调试使用！ zl 2019-12-24 00:23:55
		list_add_last(&dev_rcb->dev_ip_port_list, dev_info); //添加 全局装置报告控制块链表->装置IP、端口链表
	}

	//三、装置报告控制块查找判断----------------------------------------------------------------------------------------------------------------------------------------------------------------------
	RCB_INFO *rcb_info = NULL; //报告控制块指针
	ST_BOOLEAN bFindRCB = 0; //是否在装置报告控制块链表中查找到
	for (rcb_info = dev_rcb->rcb_info_list; rcb_info != NULL; rcb_info = (RCB_INFO *)list_get_next(dev_rcb->rcb_info_list, rcb_info)) //③遍历 全局装置报告控制块链表->报告控制块信息链表
	{
		//LOG_INFO("(报告触发)遍历rcb_info_list链表, RptID= %s，dom_Name= %s，dom= %s，rcb_name= %s，rpt= %s", rcb_info->RptID, rcb_info->dom_Name, dom, rcb_info->rcb_name, rpt); //仅调试使用！ zl 2018-10-17 21:19:36
		if ((strcmp(rcb_info->rcb_name, rcb_name) == 0) && (strcmp(rcb_info->dom_Name, dom_name) == 0)) //匹配 装置报告控制块
		{
			bFindRCB = 1; //查找到装置报告控制块
			//LOG_INFO("(报告触发)匹配到报告控制块dev_type_name= %s, dom= %s，rcb= %s，已触发过！", dev_rcb->dev_type_name, dom, rpt); //仅调试使用！ zl 2019-3-13 14:24:43
			break; /* rcb_info now points to right structure */
		}
	}
	if (!bFindRCB) //未查找到装置报告控制块
	{
		rcb_info = rcb_info_create(clientNetInfo, dom_name, rcb_name, rpt_typeids, timeOut); //调用 创建装置报告控制块 —>> 注：该函数较为耗时，若本装置类型的报告控制块已创建过，则不需再进行创建！
		if (rcb_info == NULL) {
			//LOG_INFO("(报告触发)创建报告控制块失败！dev_type_name= %s, dom= %s，rcb= %s", dev_rcb->dev_type_name, dom, rpt); //仅调试使用！ zl 2019-3-13 14:24:43
			return SD_FAILURE;
		}
		else {
			//LOG_INFO("(报告触发)创建报告控制块成功，新增全局报告控制块链表！dev_type_name= %s, dom= %s，rcb= %s", dev_rcb->dev_type_name, dom, rpt); //仅调试使用！ zl 2019-3-13 14:24:43
			list_add_last(&dev_rcb->rcb_info_list, rcb_info); //添加 全局报告控制块链表->报告控制块信息链表
		}
		 //TRACE("(报告触发)创建报告控制块！dom= %s  rcb= %s，耗时:%0.6f，报告大小:%d", dom, rpt, dwEnd - dwStart, sizeof(RCB_INFO));
	}

	if (rcb_info)
	{
		//先注销报告先 WW 2023-08-29注释先注销报告的代码
		//ST_BOOLEAN RptDisEna = 0;		/* 0 = disable the report	*/
		//ST_CHAR varName[MAX_IDENT_LEN + 1];
		//apr_snprintf(varName, sizeof(varName), "%s$RptEna", rcb_name);
		//mms_named_var_write(clientNetInfo, varName, DOM_SPEC, dom_name, rpt_typeids->mmsbool, (ST_CHAR *)&RptDisEna, timeOut);
		//WW 2023-08-29 end

		printf("rcb_info->numDsVar = %d\n", rcb_info->numDsVar);
		/* Enable options we want to see in report.	*/
		//OptFlds [0] = 0;
		/* These options valid for IEC BRCB, IEC URCB, or UCA.	*/
		//BSTR_BIT_SET_ON(OptFlds, OPTFLD_BITNUM_SQNUM);
		//BSTR_BIT_SET_ON(OptFlds, OPTFLD_BITNUM_TIMESTAMP);
		//BSTR_BIT_SET_ON(OptFlds, OPTFLD_BITNUM_REASON);
		//BSTR_BIT_SET_ON(OptFlds, OPTFLD_BITNUM_DATSETNAME);
		/* NOTE: these options ONLY available for IEC-61850 (not UCA)
		 * SUBSEQNUM is only set by the server, so don't try to set it.
		 */
		if (rcb_info->rcb_type == RCB_TYPE_IEC_BRCB)
		{	/* These only valid for IEC BRCB	*/
  //      BSTR_BIT_SET_ON(OptFlds, OPTFLD_BITNUM_BUFOVFL);
			BSTR_BIT_SET_ON(OptFlds, OPTFLD_BITNUM_ENTRYID);
			if ((pEntryID[0] != 0xff) || (pEntryID[1] != 0xff) || (pEntryID[2] != 0xff) || (pEntryID[3] != 0xff)
				|| (pEntryID[4] != 0xff) || (pEntryID[5] != 0xff) || (pEntryID[6] != 0xff) || (pEntryID[7] != 0xff))
				mms_rcb_setEntryID(clientNetInfo, dom_name, rcb_name, rpt_typeids, timeOut, pEntryID);
		}
		//if (rcb_info->rcb_type != RCB_TYPE_UCA)
		//  {	/* These only valid for IEC BRCB or IEC URCB	*/
		//  BSTR_BIT_SET_ON(OptFlds, OPTFLD_BITNUM_DATAREF);
		//  BSTR_BIT_SET_ON(OptFlds, OPTFLD_BITNUM_CONFREV);
		//  }

		TrgOps[0] = trgops;
		ret = rcb_enable(clientNetInfo, dom_name, rcb_name, OptFlds, TrgOps,
			scanRate * 1000,		/* Integrity Period (ms)	*/
			rpt_typeids,
			timeOut);		/* timeout (s)	*/

		if (ret == SD_SUCCESS)
		{
			list_add_last(&((ALL_RCB_INFO *)clientNetInfo->user_info)->rcb_info_list, rcb_info);	/* add RCB to list*/
		}
		else {
			rcb_info_destroy(rcb_info);
			rcb_info = NULL;
			echo_warn2("rcb_enable error for dom='%s', rcb='%s'\n", dom_name, rcb_name);
		}
	}
	else
	{
		printf("rcb_info_create error for dom='%s', rcb='%s'\n", dom_name, rcb_name);
		rcb_info = NULL;
	}
	return (rcb_info);
}

ST_RET mms_unregister_iec_rpt_by_devtype(MVL_NET_INFO *clientNetInfo, RPT_TYPEIDS *rpt_typeids, ST_CHAR *dev_type, ST_CHAR *ip, int port,
	ST_CHAR *dom_name, ST_CHAR *rcb_name,
	ST_INT timeOut)
{

	ST_BOOLEAN RptEna = 0;
	ST_CHAR varName[MAX_IDENT_LEN + 1];
	ST_RET	ret; //函数执行返回值

	//一、装置类型查找判断---------------------------------------------------------------------------------------------------------------------------
	ST_BOOLEAN bFindDevType = 0; //是否在链表中查找到装置类型	
	Dev_RCB_INFO *dev_rcb = NULL;//装置报告结构指针
	for (dev_rcb = gDev_rcb_list.dev_rcb_info_Head; dev_rcb != NULL; dev_rcb = (Dev_RCB_INFO *)list_get_next(gDev_rcb_list.dev_rcb_info_Head, dev_rcb)) //①遍历 全局装置报告控制块链表
	{
		//LOG_INFO("(报告停止)遍历gDev_rcb_list链表, dev_type_name= %s", dev_rcb->dev_type_name); //仅调试使用！ zl 2019-12-24 00:16:31
		if ((strcmp(dev_rcb->dev_type_name, dev_type) == 0)) //匹配 装置类型
		{
			bFindDevType = 1; //查找到装置类型
			//LOG_INFO("(报告停止)匹配到装置类型，dev_type_name= %s", dev_rcb->dev_type_name); //仅调试使用！ zl 2019-12-24 00:23:55
			break;
		}
	} //遍历 装置类型链表 结束

	if (!bFindDevType) //未查找到装置类型
	{
		dev_rcb = (Dev_RCB_INFO *)chk_calloc(1, sizeof(Dev_RCB_INFO)); //调用 申请连续内存空间，并初始化为0，返回首地址指针，相当于New
		//memset(dev_rcb, 0, sizeof(Dev_RCB_INFO)); //指针 清0
		strcpy(dev_rcb->dev_type_name, dev_type); //装置类型
		list_add_last(&gDev_rcb_list.dev_rcb_info_Head, dev_rcb); //添加 全局装置报告控制块链表
	}

	//二、装置IP、端口号查找判断---------------------------------------------------------------------------------------------------------------------------------------------------
	ST_BOOLEAN bFindIpPort = 0; //是否在链表中查找到装置IP、端口号
	Dev_IP_Port_INFO *dev_info = NULL; //装置IP、端口号结构指针
	for (dev_info = dev_rcb->dev_ip_port_list; dev_info != NULL; dev_info = (Dev_IP_Port_INFO *)list_get_next(dev_rcb->dev_ip_port_list, dev_info)) //②遍历 全局装置IP、端口链表
	{
		//LOG_INFO("(报告停止)遍历dev_ip_port_list链表, dev_type_name= %s, ip= %s, port= %d", dev_rcb->dev_type_name, dev_info->IP, dev_info->Port); //仅调试使用！ zl 2019-12-24 00:16:31
		if ((strcmp(dev_info->IP, ip) == 0) && (dev_info->Port == port)) //匹配 装置IP && 端口号
		{
			bFindIpPort = 1; //查找到装置IP && 端口号
			//LOG_INFO("(报告停止)匹配到装置dev_type_name= %s, IP= %s， Port= %d", dev_rcb->dev_type_name, dev_info->IP, dev_info->Port); //仅调试使用！ zl 2019-12-24 00:23:55
			break;
		}
	} //遍历 装置IP、端口链表 结束
	if (!bFindIpPort) //未查找到装置IP、端口
	{
		dev_info = (Dev_IP_Port_INFO *)chk_calloc(1, sizeof(Dev_IP_Port_INFO)); //调用 申请连续内存空间，并初始化为0，返回首地址指针，相当于New
		//memset(dev_info, 0, sizeof(Dev_IP_Port_INFO)); //指针 清0
		strcpy(dev_info->IP, ip); //装置IP
		dev_info->Port = port; //端口号
		list_add_last(&dev_rcb->dev_ip_port_list, dev_info); //添加 全局装置报告控制块链表->装置IP、端口链表
	}

	//三、装置报告控制块查找判断----------------------------------------------------------------------------------------------------------------------------------------------------------------------
	RCB_INFO *rcb_info = NULL; //报告控制块指针
	ST_BOOLEAN bFindRCB = 0; //是否在装置报告控制块链表中查找到
	for (rcb_info = dev_rcb->rcb_info_list; rcb_info != NULL; rcb_info = (RCB_INFO *)list_get_next(dev_rcb->rcb_info_list, rcb_info)) //③遍历 全局装置报告控制块链表->报告控制块信息链表
	{
		//LOG_INFO("(报告停止)遍历rcb_info_list链表, RptID= %s，dom_Name= %s，dom= %s，rcb_name= %s，rpt= %s", rcb_info->RptID, rcb_info->dom_Name, dom, rcb_info->rcb_name, rpt); //仅调试使用！ zl 2018-10-17 21:19:36
		//if ((strcmp (rcb_info->rcb_name, rpt) == 0) && (strcmp(rcb_info->dom_Name, dom) == 0)) //匹配 装置报告控制块 —— 废弃！ zl 2019-12-26 17:27:29
		if ((strcmp(rcb_info->rcb_name, rcb_name) == 0)) //匹配 装置报告控制块
		{
			bFindRCB = 1; //查找到装置报告控制块
			//LOG_INFO("(报告停止)匹配到报告控制块dom= %s，rcb= %s，已触发过！", dom, rpt); //仅调试使用！ zl 2019-3-13 14:24:43
			break;	/* rcb_info now points to right structure */
		}
	}
	if (!bFindRCB) //未查找到装置报告控制块
	{
		rcb_info = rcb_info_create(clientNetInfo, dom_name, rcb_name, rpt_typeids, timeOut); //调用 创建装置报告控制块 —>> 注：该函数较为耗时，若本装置类型的报告控制块已创建过，则不需再进行创建！
		if (rcb_info == NULL) {
			//LOG_INFO("(报告停止)创建报告控制块失败！dom= %s，rcb= %s", dom, rpt); //仅调试使用！ zl 2019-3-13 14:24:43
			return SD_FAILURE;
		}
		else {
			//LOG_INFO("(报告停止)创建报告控制块成功，新增全局报告控制块链表！dom= %s，rcb= %s", dom, rpt); //仅调试使用！ zl 2019-3-13 14:24:43
			list_add_last(&dev_rcb->rcb_info_list, rcb_info); //添加 全局报告控制块链表->报告控制块信息链表
		}
	}

	sprintf(varName, "%s$RptEna", rcb_name); //报告停止  例：LLN0$BR$brcbStatisticData03$RptEna
	ret = mms_named_var_write(clientNetInfo, varName, DOM_SPEC, dom_name, rpt_typeids->mmsbool, (ST_CHAR *)&RptEna, timeOut); //调用 写入装置报告控制块(报告停止)

	return ret;

}
//WW 2023-08-29 end
////////////////////////////////////
ST_RET mms_unregister_iec_rpt (MVL_NET_INFO *clientNetInfo, RPT_TYPEIDS *rpt_typeids,
		ST_CHAR *dom_name,	ST_CHAR *rcb_name,
		ST_INT timeOut )
  {  
 	ST_RET ret;
	RCB_INFO *rcb_info;
	RCB_INFO* this_rcb_info;

	this_rcb_info = FindRcbInfo(clientNetInfo,dom_name,rcb_name );

	ret = rcb_disable(clientNetInfo, dom_name, rcb_name,rpt_typeids,timeOut);
	// remove the if condition
	ALL_RCB_INFO *all_rcb_info = (ALL_RCB_INFO *)clientNetInfo->user_info ;
	for (rcb_info = all_rcb_info->rcb_info_list;
		rcb_info != NULL;
		rcb_info = (RCB_INFO *) list_get_next (all_rcb_info->rcb_info_list, rcb_info))
	{
		if( rcb_info == this_rcb_info )
		{
			ret = list_unlink(&all_rcb_info->rcb_info_list, rcb_info);
			if(ret == SD_SUCCESS)
			{
				rcb_info_destroy(rcb_info);
				return ret;
			}
		}
	}
	
	return SD_FAILURE;
  } 
 
 
/************************************************************************/
/*				doCommService 				*/
/************************************************************************/

ST_VOID doCommService ()
  {
#if defined (MOSI)		/* If stack is MOSI, use events		*/
    wait_any_event (50);	/* Wait 50 milliseconds		*/
    while (mvl_comm_serve ())	/* Perform communications service 	*/
      {				/* Loop until "mvl_comm_serve" returns 0*/
      }
#else
    mvl_comm_serve ();		/* Perform communications service 	*/
#endif
  }


  

/************************************************************************/
/* 			connectToServer 				*/
/************************************************************************/

// 检查字符串是否为空、仅空格或不可见字符lnk20241119
int is_empty_or_whitespace(const char* str) {
    if (str == NULL) {
        return 1; // NULL 情况，视为"空"
    }
    while (*str) {
        if (!isspace((unsigned char)*str)) {
            return 0; // 有非空格字符，非"空"
        }
        str++;
    }
    return 1; // 全是空格或不可见字符，视为"空"
}

ST_RET mms_connectToServer (ST_CHAR * dev_key,ST_CHAR *dev_series, ST_CHAR *serverARName,
	 MVL_NET_INFO **clientNetInfo, MVL_REQ_PEND **reqCtrl)
  {

ST_RET ret;

#if defined(S_SEC_ENABLED) || defined(ACSE_AUTH_ENABLED)
ST_CHAR				ied_password[256];
ACSE_AUTH_INFO      authInfoStr = {0};
#endif
#if defined(S_SEC_ENABLED)
S_SEC_ENCRYPT_CTRL  encryptCtrlStr = {0};
S_SEC_LOC_AR       *loc_ar_sec = NULL;
S_SEC_REM_AR       *rem_ar_sec = NULL;
#endif
ACSE_AUTH_INFO     *authInfo = NULL;	/* conn authentication info	*/
S_SEC_ENCRYPT_CTRL *encryptCtrl = NULL;	/* conn enctryption info	*/

  callingInitInfo.mms_p_context_pres = SD_TRUE;
  callingInitInfo.max_segsize_pres = SD_TRUE;
  callingInitInfo.max_segsize = mvl_cfg_info->max_msg_size;
  callingInitInfo.maxreq_calling = 1;
  callingInitInfo.maxreq_called = 1;
  callingInitInfo.max_nest_pres = SD_TRUE;
  callingInitInfo.max_nest = 5;
  callingInitInfo.mms_detail_pres = SD_TRUE;
  callingInitInfo.version = 1;
  callingInitInfo.num_cs_init = 0;
  callingInitInfo.core_position = 0;
  callingInitInfo.param_supp[0] = m_param[0];
  callingInitInfo.param_supp[1] = m_param[1];
  memcpy (callingInitInfo.serv_supp, m_service_resp,11);

#if defined(S_SEC_ENABLED)
  authInfo = &authInfoStr;
  encryptCtrl = &encryptCtrlStr;
  /* set authentication and encryption infor for this connection */
  ret = ulSetSecurityCalling (clientARName, serverARName,
                             &loc_ar_sec, &rem_ar_sec,
                             authInfo, encryptCtrl);
  if (ret != SD_SUCCESS)9
    {
    printf ("\n Initialization of security info failed");
    return (NULL);	/* error	*/
    }
#elif defined(ACSE_AUTH_ENABLED)
  if (g_no_auth==0) {
	  /* Fill out an authentication structure */
	  //authInfo = &authInfoStr;
	  //authInfo->auth_pres = SD_TRUE;
	  //authInfo->mech_type = ACSE_AUTH_MECH_PASSWORD;
	  //memset(ied_password,0,sizeof(ied_password));

    //lnk20241119添加判断和打印，确保识别码秘钥为空时不调用加密
    if (!is_empty_or_whitespace(dev_series) || !is_empty_or_whitespace(dev_key)) {
        printf("dev_series= %s,dev_key= %s\n", dev_series,dev_key);
        /* Fill out an authentication structure */
	      authInfo = &authInfoStr;
	      authInfo->auth_pres = SD_TRUE;
	      authInfo->mech_type = ACSE_AUTH_MECH_PASSWORD;
	      memset(ied_password,0,sizeof(ied_password));

        //传进来的就是加密过的先解密成明文再传入
        const char* key = "11HDESaAhiHHug2z";//上层使用的写死的key
        char dev_series_plaintext[256] = {0};
        char dev_key_plaintext[256] = {0};

        int ret1 = sm4_ecb_decrypt_zeropad_base64(dev_series, key, dev_series_plaintext);
        int ret2 = sm4_ecb_decrypt_zeropad_base64(dev_key, key, dev_key_plaintext);
        if (ret1 == 0 && ret2 == 0) {
            printf("识别码解密成功\n");
            //printf("解密后的 dev_series: %s\n", dev_series_plaintext);
            //printf("解密后的 dev_key   : %s\n", dev_key_plaintext);
        } else {
            printf("识别码解密失败\n");
        }

        GetSM4Code((unsigned char*)dev_series_plaintext,dev_key_plaintext,ied_password);
	      strcpy (authInfo->u.pw_auth.password, ied_password);	
    } else {
        printf("dev_series or dev_key is null or space!\n");
    }

	  //GetSM4Code((unsigned char*)dev_series,dev_key,ied_password);
	  //strcpy (authInfo->u.pw_auth.password, ied_password);	

	  //cout<<"old password:"<< ied_password <<endl
	  //strcpy (authInfo->u.pw_auth.password, "7a88179802378762611182956231b315");	/* this is the password */

  }
#endif  /* defined(ACSE_AUTH_ENABLED) */

//  *reqCtrl = NULL;
  *clientNetInfo = NULL;
  ret = mvla_initiate_req_ex (serverARName, &callingInitInfo, 
			      &initRespInfo, clientNetInfo, reqCtrl,
			      authInfo, encryptCtrl);
  if (ret == SD_SUCCESS)
    {
#if defined(S_SEC_ENABLED)
    /* Save the AR Security pointers, for convenience when the confirm is rxd	*/
    clientNetInfo->loc_ar_sec = loc_ar_sec;
    clientNetInfo->rem_ar_sec = rem_ar_sec;
#endif
//    ret = waitReqDone (reqCtrl, 60);  /* need to wait longer on connect */
   
    }
  if (ret != SD_SUCCESS)    *clientNetInfo = NULL;

 //printf ("\n %s clientNetInfo = 0x%x ,ret =0x%X  ",serverARName,*clientNetInfo,ret);
#if defined(S_SEC_ENABLED)
  ulFreeAssocSecurity (authInfo); /* free local auth info, not needed anymore	*/
#endif

//  mvl_free_req_ctrl (reqCtrl);
  return ret;
  }

/************************************************************************/
/*			disconnectFromServer				*/
/************************************************************************/

ST_RET mms_disconnectFromServer (MVL_NET_INFO *clientNetInfo,MVL_REQ_PEND **reqCtrl)
  {	
  ST_RET ret;
  echo_warn1( "mms_disconnectFromServer clientNetInfo= %x \n",clientNetInfo);	
  ret = mvla_concl (clientNetInfo, reqCtrl);
  //waitReqDone (*reqCtrl, 0);
  //mvl_free_req_ctrl (*reqCtrl);
  return ret;
  }

ST_RET mms_release_connection (MVL_NET_INFO *clientNetInfo)
  {	  
  echo_warn1( "mms_release_connection NetInfo= %x \n",clientNetInfo);	  
  mvl_abort_req(clientNetInfo);
  return SD_SUCCESS;
  }


/************************************************************************/
/* 			u_mvl_info_rpt_ind				*/
/************************************************************************/


ST_VOID u_mvl_info_rpt_ind (MVL_COMM_EVENT *event)
  {
  //printf(" ST_VOID u_mvl_info_rpt_ind (MVL_COMM_EVENT *event) \n");
 //printf(" %d  \n",event->net_info);//->rem_ar_sec->arName);
INFO_REQ_INFO *info_ptr;
ST_INT j;
OBJECT_NAME *vobj;
VARIABLE_LIST *vl;
VAR_ACC_SPEC *va_spec;

//MVL_VAR_ASSOC *va;
ST_INT num_va;
ST_CHAR *name;

  /* Assume this is an IEC-61850/UCA report, and try to process it.
   * If processing successful, then skip other processing.
   */
//WW 2023-08-30
  if (u_iec_rpt_ind (event) == SD_SUCCESS)
//	  if (u_iec_rpt_ind_by_devtype(event) == SD_SUCCESS)
		  return;	/* this funct processed it. Skip other processing.	*/
//WW 2023-08-30 end
/* We want to get a table of pointers to VARIABLE_ASSOCIATIONS so we	*/
/* can convert the received data to local format.		 	*/

  info_ptr = (INFO_REQ_INFO *) event->u.mms.dec_rslt.data_ptr;
  va_spec = &info_ptr->va_spec;
  num_va = info_ptr->num_of_acc_result;
  
  if (va_spec->var_acc_tag == VAR_ACC_NAMEDLIST)
    {
    printf ("Unrecognized Report received with NamedVariableList='%s'. Ignored.\n",
          va_spec->vl_name.obj_name.vmd_spec);
    }
  else if (va_spec->var_acc_tag == VAR_ACC_VARLIST)
    {
    vl = (VARIABLE_LIST *) (info_ptr + 1);
    
    for (j = 0; j < num_va; ++j, ++vl)
      {
      printf ("	Recognized Report received with NamedVariable	");
      if (vl->var_spec.var_spec_tag == VA_SPEC_NAMED)
        {
        vobj = &vl->var_spec.vs.name;
        name = vobj->obj_name.vmd_spec;
        printf (" var name= %s	\n ",name);

        if (!strcmp (name, my_control_info.oper_name) && vobj->object_tag == DOM_SPEC)
          {
          /* This is IEC 61850 CommandTermination. Save num of vars.	*/
          my_control_info.cmd_term_num_va = num_va;
          }
        }
      else
        {
        printf ("InfoRpt Ind : VA Spec not named \n");
        }
      } 
    }	/* end if VAR_ACC_VARLIST	*/

  }


/************************************************************************/
/*			u_mvl_connect_ind_ex				*/
/*----------------------------------------------------------------------*/
/* This function is called when a remote node has connected to us. We	*/
/* can look at the assReqInfo to see who it is (assuming that the 	*/
/* AP-Titles are used), or at the cc->rem_init_info to see initiate 	*/
/* request parameters.							*/
/************************************************************************/
extern ST_ACSE_AUTH u_mvl_connect_ind_ex (MVL_NET_INFO *cc, INIT_INFO *init_info,
	ACSE_AUTH_INFO *req_auth_info, ACSE_AUTH_INFO *rsp_auth_info)
{
	ST_ACSE_AUTH ret = ACSE_AUTH_SUCCESS;

#if defined(S_SEC_ENABLED)
	/* this Client will reject the connection since there is no security	*/
	/* checking code in place (see Server for sample)			*/
	ret = ACSE_DIAG_NO_REASON;
#elif defined(ACSE_AUTH_ENABLED)
	/* this Client will reject the connection since there is no password	*/
	/* checking code in place (see Server for sample)			*/
	if (g_no_auth==0) 
		ret = ACSE_DIAG_NO_REASON;
	else
		return (ret);

#endif

	return (ret);
}

/************************************************************************/
/*			u_mvl_connect_cnf_ex   				*/
/*----------------------------------------------------------------------*/
/* This function is called when we have received an initiate response.	*/
/* Depending on the server's authentication scheme, we may have been 	*/
/* sent responding authentication that we can pull out of the response 	*/
/* PDU.									*/
/************************************************************************/

ST_ACSE_AUTH u_mvl_connect_cnf_ex (MVL_NET_INFO *cc, AARE_APDU *ass_rsp_info)
{
	ST_RET ret = ACSE_AUTH_SUCCESS;

#if defined(S_SEC_ENABLED)
	/* check security parameters of the Associate Confirm			*/
	ret = ulCheckSecureAssocConf (cc);
#elif defined(ACSE_AUTH_ENABLED)
	if (g_no_auth==0) 
	{
		ACSE_AUTH_INFO *rsp_auth_info;

		rsp_auth_info = &ass_rsp_info->auth_info;


		if(rsp_auth_info->auth_pres == SD_TRUE)
		{
			/* Looks like we have some authentication to look at, simply print  */
			/* the password and continue as normal.				*/
			if(rsp_auth_info->mech_type == ACSE_AUTH_MECH_PASSWORD)
			{
				//printf("\nPassword recieved from peer: %s\n", rsp_auth_info->u.pw_auth.password);
			}
			/* We were sent a mechanism we don't support, let's reject the 	*/
			/* the connection with the appropriate diagnostic.			*/
			else
			{
				ret = ACSE_DIAG_AUTH_MECH_NAME_NOT_RECOGNIZED;
			}
		}
		else
		{
			/* Hmm... looks like we weren't sent any authentication even though */
			/* we require it. Let's reject with the appropriate diagnostic.	*/
			ret = ACSE_AUTH_SUCCESS;//ww 2024-01-09 ACSE DIAG AUTH REQUIRED
		}
	}
#endif /* defined(ACSE_AUTH_ENABLED) */

	/* to accept the confirm ACSE_AUTH_SUCCESS need to be returned	*/
	return ((ST_ACSE_AUTH) ret);
}


/************************************************************************/
/*			disc_ind_fun					*/
/************************************************************************/
/* This function is called when connection is terminated.		*/

static ST_VOID disc_ind_fun (MVL_NET_INFO *cc, ST_INT discType)
  {

ALL_RCB_INFO *all_rcb_info;
RCB_INFO *rcb_info;

  printf("disc_ind_fun clientNetInfo= 0x%x  discType=%i\n",cc,discType);
  Callback_channel_disconnect_ind(cc, discType);	

  /* Free up rcb_info if necessary.	*/
  if (cc->user_info)
    {
    all_rcb_info = (ALL_RCB_INFO*)cc->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);
    cc->user_info=NULL;
    }

  cc->rem_vmd = NULL;
  
  
  }

#if (MMS_IDENT_EN & RESP_EN)
/************************************************************************/
/*			u_mvl_ident_ind					*/
/************************************************************************/
ST_VOID u_mvl_ident_ind (MVL_IND_PEND *indCtrl)
  {
//#if 0	/* enable this to print when Ind received		*/
  printf ("\n Identify Indication received.");
//#endif
  indCtrl->u.ident.resp_info = &identify_response_info;
  mplas_ident_resp (indCtrl);
  }
#endif	/* #if (MMS_IDENT_EN & RESP_EN)	*/

/************************************************************************/
/*			getFile						*/
/*
先向远端发 fopen，打开远程文件

拿到这个远程文件会话句柄 frsmid

循环发 fread，一块一块把远程文件内容读回来并写入本地文件

最后发 fclose，关闭远程文件

clientNetInfo：客户端和 MMS 服务器之间的网络连接信息

loc_file：本地保存的文件路径

rem_file：远端设备上的文件路径

iTimeout：每次请求等待响应的超时时间

返回值：

SD_SUCCESS：下载成功

其他：失败错误码
 */
/************************************************************************/
ST_RET mms_getFile (MVL_NET_INFO *clientNetInfo, ST_CHAR *loc_file, 
	ST_CHAR *rem_file, ST_INT iTimeout)
{
	FREAD_REQ_INFO   fread_req_info;
	FCLOSE_REQ_INFO  fclose_req_info;
	ST_RET ret;
	FILE *loc_fp;
	MVL_REQ_PEND *reqCtrl;
	ST_INT32 frsmid;
	ST_BOOLEAN more_follows;

	ret = mvla_fopen (clientNetInfo,
		rem_file,
		0,		/* init_pos: start at beginning of file	*/
		&reqCtrl);
	if (ret == SD_SUCCESS)
		ret = waitReqDone (reqCtrl, iTimeout);

	if (ret == SD_SUCCESS)
		frsmid = reqCtrl->u.fopen.resp_info->frsmid;	/* save frsmid	*/
	mvl_free_req_ctrl (reqCtrl);
	if (ret != SD_SUCCESS)
		return (ret);

	loc_fp = fopen (loc_file, "wb");
	if (loc_fp == NULL)
		goto ERR;

	fread_req_info.frsmid = frsmid;	/* "fread" request doesn't chg.	*/
	do
	{
		ret = mvla_fread (clientNetInfo, &fread_req_info, &reqCtrl);
		if (ret == SD_SUCCESS)
			ret = waitReqDone (reqCtrl, iTimeout);
		if (ret == SD_SUCCESS)
		{
			more_follows = reqCtrl->u.fread.resp_info->more_follows;
			fwrite (reqCtrl->u.fread.resp_info->filedata, 1,
				reqCtrl->u.fread.resp_info->fd_len, loc_fp);
		}
		mvl_free_req_ctrl (reqCtrl);
		if (ret != SD_SUCCESS)
			goto ERR;
	} while (more_follows);

	fclose_req_info.frsmid = frsmid;
	ret = mvla_fclose (clientNetInfo, &fclose_req_info, &reqCtrl);
	if (ret == SD_SUCCESS)
		ret = waitReqDone (reqCtrl, iTimeout);
	if (ret != SD_SUCCESS)
		printf ("\n mvl_fclose failed, ret = 0x%X", ret);
	mvl_free_req_ctrl (reqCtrl);

	fclose (loc_fp);
	return (SD_SUCCESS);

ERR:
	if (loc_fp == NULL) {
		return (SD_FAILURE);	/* Can't open local file.	*/
	} else {
		fclose(loc_fp);
		return (ret);
	}	
}

//***************************lnk20260309下发文件到装置*********************/
ST_RET mms_putFile(MVL_NET_INFO *clientNetInfo,
                   ST_CHAR *src_file,
                   ST_CHAR *dest_file,
                   ST_INT iTimeout)
{
    MVL_REQ_PEND *reqCtrl = NULL;
    ST_RET ret = SD_FAILURE;

    if (clientNetInfo == NULL)
    {
        printf("\n mms_putFile failed: clientNetInfo is NULL");
        return SD_FAILURE;
    }

    if (src_file == NULL || src_file[0] == '\0')
    {
        printf("\n mms_putFile failed: src_file is NULL or empty");
        return SD_FAILURE;
    }

    if (dest_file == NULL || dest_file[0] == '\0')
    {
        printf("\n mms_putFile failed: dest_file is NULL or empty");
        return SD_FAILURE;
    }

    ret = mvla_obtfile(clientNetInfo, src_file, dest_file, &reqCtrl);
    if (ret == SD_SUCCESS)
        ret = waitReqDone(reqCtrl, iTimeout);

    if (ret != SD_SUCCESS)
    {
        printf("\n mms_putFile failed, src='%s', dest='%s', ret=0x%X",
               src_file, dest_file, ret);
    }
    else
    {
        printf("\n mms_putFile OK, src='%s', dest='%s'",
               src_file, dest_file);
    }

    if (reqCtrl != NULL)
    {
        mvl_free_req_ctrl(reqCtrl);
        reqCtrl = NULL;
    }

    return ret;
}
//************************************************************************/
/*			putFile						*/


/************************************************************************/
/*				init_mem				*/
/************************************************************************/

static ST_VOID mem_chk_error_detected (ST_VOID);
static ST_VOID *my_malloc_err (ST_UINT size);
static ST_VOID *my_calloc_err (ST_UINT num, ST_UINT size);
static ST_VOID *my_realloc_err (ST_VOID *old, ST_UINT size);

static ST_CHAR *spareMem;

static ST_VOID init_mem ()
  {
#if defined(NO_GLB_VAR_INIT)
  mvl_init_glb_vars ();
#endif

/* Allocate spare memory to allow logging/printing memory errors	*/
  spareMem = (ST_CHAR *) malloc (500);

/* trap mem_chk errors							*/
  mem_chk_err = mem_chk_error_detected;
  m_memerr_fun = my_malloc_err;
  c_memerr_fun = my_calloc_err;
  r_memerr_fun = my_realloc_err;

#if 0
  m_heap_check_enable = SD_TRUE;
  m_check_list_enable = SD_TRUE;
  m_no_realloc_smaller = SD_TRUE;
  m_fill_en = SD_TRUE;
#endif
  m_mem_debug = SD_TRUE;
  }

/************************************************************************/
/* This function is called from the DEBUG version of the mem library	*/
/* when an error of any type is detected.				*/
/************************************************************************/

static ST_INT memErrDetected;

static ST_VOID mem_chk_error_detected (ST_VOID)
  {
  if (!memErrDetected)
    {
    free (spareMem);
    memErrDetected = SD_TRUE;
    printf ("\n Memory Error Detected!");
    dyn_mem_ptr_status ();
    }
  }

/************************************************************************/
/* Memory Allocation Error Handling Functions.				*/
/* These functions are called from mem_chk when it is unable to 	*/
/* perform the requested operation. These functions must either return 	*/
/* a valid buffer or not return at all.					*/
/************************************************************************/

static ST_VOID *my_malloc_err (ST_UINT size)
  {
  mem_chk_error_detected ();
  printf ("\n Malloc");
  exit (2);
  return (NULL);   
  }

static ST_VOID *my_calloc_err (ST_UINT num, ST_UINT size)
  {
  mem_chk_error_detected ();
  exit (3);
  return (NULL);   
  }

static ST_VOID *my_realloc_err (ST_VOID *old, ST_UINT size)
  {
  mem_chk_error_detected ();
  exit (4);
  return (NULL);   
  }

/////////////////////////////////////////////////////////////////////////
#define MAX_FILE_HANDLE_NUM (256)
static FILE	*fp_arr[MAX_FILE_HANDLE_NUM];
static ST_INT32 cur_handle = 0;
ST_INT32 set_file_pointer( FILE *fp)
{
	ST_INT32 the_handle = cur_handle;
	fp_arr[cur_handle++] = fp;
	cur_handle %= MAX_FILE_HANDLE_NUM;
	return the_handle;
}
FILE* get_file_pointer(ST_INT32 handle)
{
	return fp_arr[handle];
}
///////////////////////////////////////////////////////////////////////

#if (MMS_FOPEN_EN & RESP_EN)
/************************************************************************/
/*			u_mvl_fopen_ind					*/
/************************************************************************/
ST_VOID u_mvl_fopen_ind (MVL_IND_PEND *indCtrl)
  {
FILE *fp;
FOPEN_RESP_INFO resp_info;
struct stat  stat_buf;

  fp = fopen (indCtrl->u.fopen.filename, "rb");	/* CRITICAL: use "b" flag for binary transfer*/
  if (fp == NULL)
    {
    _mplas_err_resp (indCtrl,11,6);		/* File-access denied	*/
    return;
    }
  if (fseek (fp, indCtrl->u.fopen.init_pos, SEEK_SET))
    {
    _mplas_err_resp (indCtrl,11,5);		/* Position invalid	*/
    return;
    }

  /* WARNING: this only works if (FILE *) is a 32-bit pointer.		*/
  resp_info.frsmid = set_file_pointer(fp);  //(ST_INT32) fp;

  if (fstat (fileno (fp), &stat_buf))
    {					/* Can't get file size or time	*/
    _mplas_err_resp (indCtrl,11,0);	/* File Problem, Other		*/
    return;
    }
  else
    {
    resp_info.ent.fsize    = stat_buf.st_size;
    resp_info.ent.mtimpres = SD_TRUE;
    resp_info.ent.mtime    = stat_buf.st_mtime;
    }

  indCtrl->u.fopen.resp_info = &resp_info;
  mplas_fopen_resp (indCtrl);
  }
#endif 	/* MMS_FOPEN_EN & RESP_EN	*/


#if (MMS_FREAD_EN & RESP_EN)
/************************************************************************/
/*			u_mvl_fread_ind					*/
/************************************************************************/
ST_VOID u_mvl_fread_ind (MVL_IND_PEND *indCtrl)
  {
FILE *fp;
ST_UCHAR *tmp_buf;
MVLAS_FREAD_CTRL *fread_ctrl = &indCtrl->u.fread;
FREAD_RESP_INFO resp_info;

  fp = get_file_pointer(fread_ctrl->req_info->frsmid);// (FILE *) fread_ctrl->req_info->frsmid;
  /* Do NOT read more than "max_size".				*/
  tmp_buf = (ST_UCHAR *) chk_malloc (fread_ctrl->max_size);

  resp_info.fd_len = fread (tmp_buf, 1, fread_ctrl->max_size, fp);
  if (resp_info.fd_len == 0  &&  ferror (fp))
    {
    _mplas_err_resp (indCtrl, 3, 0);
    return;
    }

  resp_info.filedata = tmp_buf;
  if (resp_info.fd_len == fread_ctrl->max_size)
    resp_info.more_follows = SD_TRUE;
  else
    resp_info.more_follows = SD_FALSE;

  fread_ctrl->resp_info = &resp_info;
mplas_fread_resp (indCtrl);
  chk_free (tmp_buf);		/* Temporary buffer	*/
  }
#endif	/* #if (MMS_FREAD_EN & RESP_EN)	*/

#if (MMS_FCLOSE_EN & RESP_EN)
/************************************************************************/
/*			u_mvl_fclose_ind				*/
/************************************************************************/
ST_VOID u_mvl_fclose_ind (MVL_IND_PEND *indCtrl)
  {
FILE *fp;
MVLAS_FCLOSE_CTRL *fclose_ctrl = &indCtrl->u.fclose;

  fp = get_file_pointer(fclose_ctrl->req_info->frsmid);//(FILE *) fclose_ctrl->req_info->frsmid;

  if (fclose (fp))
    _mplas_err_resp (indCtrl, 11, 0);	/* File problem, other	*/
  else
    mplas_fclose_resp (indCtrl);
  }
#endif	/* #if (MMS_FCLOSE_EN & RESP_EN)	*/

/************************************************************************/
/*			waitReqDone					*/
/*----------------------------------------------------------------------*/
/* Wait for request to complete. Service communications while waiting.	*/
/* Check for timeout or Ctrl-C (i.e. doIt=SD_FALSE).			*/
/* NOTE: if other processing should be done while waiting, add it to	*/
/*       this function.							*/
/* Parameters:								*/
/*	req		request to wait for.				*/
/*	timeout		time to wait (seconds). 0 means wait forever.	*/
/* RETURNS: SD_SUCCESS, MVL_ERR_USR_TIMEOUT or other MVL error code.	*/
/************************************************************************/
ST_RET waitReqDone (MVL_REQ_PEND *req, ST_INT timeout)
{
	ST_DOUBLE stopTime = sGetMsTime() + (ST_DOUBLE) timeout * 1000.0;
	while (req->done == SD_FALSE)		/* wait until done	*/
	{
		if (timeout != 0  &&  sGetMsTime() > stopTime)
		{
			req->result = MVL_ERR_USR_TIMEOUT;
			MVL_LOG_ERR1 ("MMS request (opcode = %d) timed out", req->op); 
			break;
		}
		doCommService();	/* wait for events if possible & call mvl_comm_serve*/
	}	/* end loop	*/
	return (req->result);
}


void doCommForSecs (ST_INT timeout)
{
	ST_DOUBLE stopTime = sGetMsTime() + (ST_DOUBLE) timeout * 1000.0;
	while ( sGetMsTime() < stopTime)
	{	 
		doCommService();	/* wait for events if possible & call mvl_comm_serve*/	
	}
}

/************************************************************************/
/*                       named_var_read					*/
/* Read a single named variable.					*/
/*   scope = VMD_SPEC, DOM_SPEC, or AA_SPEC				*/
/* CRITICAL: the local variable "parse_info" is written indirectly 	*/
/*           from waitReqDone when the response is received. The call	*/
/*           to "waitReqDone" MUST NOT be moved outside this function.	*/
/************************************************************************/
ST_RET mms_named_var_read (MVL_NET_INFO *net_info, ST_CHAR *varName,
		ST_INT scope, ST_CHAR *domName,
		ST_INT type_id, ST_VOID *dataDest, ST_INT timeOut)
  {
ST_INT num_data;
ST_CHAR buffer[512];
MVL_READ_RESP_PARSE_INFO parse_info;
READ_REQ_INFO *req_info;
VARIABLE_LIST *vl;
ST_INT ret;
MVL_REQ_PEND *reqCtrl;

  /* Create a read request info struct  */
  req_info = (READ_REQ_INFO *) buffer;
  req_info->spec_in_result = SD_FALSE;
  req_info->va_spec.var_acc_tag = VAR_ACC_VARLIST;
  req_info->va_spec.num_of_variables = 1;
  vl = (VARIABLE_LIST *) (req_info + 1);

  vl->alt_access_pres = SD_FALSE;
  vl->var_spec.var_spec_tag = VA_SPEC_NAMED;
  vl->var_spec.vs.name.object_tag = scope;	/* set scope	*/
  if (scope == DOM_SPEC)
    vl->var_spec.vs.name.domain_id = domName;	/* set domain name	*/
  vl->var_spec.vs.name.obj_name.vmd_spec = varName;

  num_data = 1;     /* Number of named variables returned		*/
  
  /* IMPORTANT: start with clean structure, then set appropriate elements.*/
  memset (&parse_info, 0, sizeof (parse_info));
  parse_info.dest = dataDest;
  parse_info.type_id = type_id;
  parse_info.descr_arr = SD_FALSE;

  /* Send read request. */
  ret = mvla_read_variables (net_info,req_info,num_data,
			    &parse_info, &reqCtrl);

  /* If request sent successfully, wait for reply.	*/
  if (ret == SD_SUCCESS)
    ret = waitReqDone (reqCtrl, timeOut);

  mvl_free_req_ctrl (reqCtrl);
  if (ret != SD_SUCCESS)
    return (ret);

  return (parse_info.result);	/* Return the single variable result	*/
  }
/************************************************************************/
/*			mms_named_var_write					*/
/* Write a single named variable.					*/
/*   scope = VMD_SPEC, DOM_SPEC, or AA_SPEC				*/
/* CRITICAL: the local variable "wr_info" is written indirectly 	*/
/*           from waitReqDone when the response is received. The call	*/
/*           to "waitReqDone" MUST NOT be moved outside this function.	*/
/************************************************************************/
ST_RET mms_named_var_write (MVL_NET_INFO *netInfo, ST_CHAR *varName,
		ST_INT scope, ST_CHAR *domName,
		ST_INT type_id,	ST_VOID *dataSrc, ST_INT timeOut)
  {
ST_CHAR buffer[512];
MVL_WRITE_REQ_INFO *wr_info;
WRITE_REQ_INFO *req_info;
VARIABLE_LIST *vl;
ST_RET ret;
MVL_REQ_PEND *reqCtrl;

  req_info = (WRITE_REQ_INFO *) buffer;
  req_info->num_of_data = 1;
  req_info->va_spec.var_acc_tag = VAR_ACC_VARLIST;
  req_info->va_spec.num_of_variables = 1;
  vl = (VARIABLE_LIST *) (req_info + 1);

  vl->alt_access_pres = SD_FALSE;
  vl->var_spec.var_spec_tag = VA_SPEC_NAMED;
  vl->var_spec.vs.name.object_tag = scope;	/* set scope	*/
  if (scope == DOM_SPEC)
    vl->var_spec.vs.name.domain_id = domName;	/* set domain name	*/
  vl->var_spec.vs.name.obj_name.vmd_spec = varName;

  /* Alloc array of structs. Here only one var so alloc one struct.	*/
  /* To read multiple vars, allocate more.				*/
  /* CRITICAL: response code writes to this struct. Do not free until	*/
  /*           response received.					*/ 
  wr_info = (MVL_WRITE_REQ_INFO*)chk_calloc (1, sizeof (MVL_WRITE_REQ_INFO));

  wr_info->local_data = dataSrc;
  wr_info->type_id = type_id;
  wr_info->arr = SD_FALSE;

  /* Send write request.	*/
  ret = mvla_write_variables (netInfo, req_info, 1, wr_info, &reqCtrl);

  /* If request sent successfully, wait for response.	*/
  if (ret == SD_SUCCESS)
    ret = waitReqDone (reqCtrl, timeOut);

  mvl_free_req_ctrl (reqCtrl);
  if (ret == SD_SUCCESS)	/* response received OK.	*/
    ret = wr_info->result;	/* return single variable result	*/
  chk_free (wr_info);

  return (ret);
  }

//lnk20260508添加重启装置函数
int BuildResetDomName(ied_usr_t *ied_usr, char *domName, size_t domNameSize)
{
    if (ied_usr == NULL || domName == NULL || domNameSize == 0)
        return -1;

    domName[0] = '\0';

    XmlConfigC cfg1;
    memset(&cfg1, 0, sizeof(cfg1));

    if (get_xml_config_by_dev_type(ied_usr->dev_type, &cfg1))
    {
        printf("========== XmlConfigC dump ==========\n");
        printf("IEDControl      = '%s'\n", cfg1.IEDControl);
        printf("IEDname         = '%s'\n", cfg1.IEDname);
        printf("LDevicePrefix   = '%s'\n", cfg1.LDevicePrefix);
        printf("=====================================\n");

        if (cfg1.IEDControl[0] != '\0')
        {
            snprintf(domName, domNameSize, "%s", cfg1.IEDControl);
            printf("[RESET] use cfg1.IEDControl=%s\n", domName);
            return 0;
        }
    }

    if (ied_usr->LD_info && ied_usr->LD_info[0].LD_name)
    {
        snprintf(domName, domNameSize, "%s", ied_usr->LD_info[0].LD_name);

        int len = strlen(domName);
        if (len > 0 && isdigit(domName[len - 1]))
            domName[len - 1] = '0';

        printf("[RESET] use LD_name domName=%s\n", domName);

		DIY_WARNLOG_CODE(ied_usr->terminal_id, 1, LOG_CODE_FILE_CONTROL,
			"【WARN】未取到 IEDControl 信息，使用 LD_name=%s terminal_id=%s",
			domName, ied_usr->terminal_id);

        return -2;
    }

    snprintf(domName, domNameSize, "%s", "PQMonitorPQM0");
    printf("[RESET] use default domName=%s\n", domName);
	DIY_ERRORLOG_CODE(ied_usr->terminal_id, 1, LOG_CODE_FILE_CONTROL,
            "【ERROR】未取到 LD 信息，使用默认 domName=%s terminal_id=%s",
            domName, ied_usr->terminal_id);
    return -1;
}

static ST_INT ledrs_var_type_create(MVL_NET_INFO* net_info,
                                    OBJECT_NAME* varObj,
                                    ST_INT timeOut)
{
    MVL_REQ_PEND* reqCtrl;
    GETVAR_REQ_INFO getvar_req;
    VAR_ACC_TSPEC* type_spec;
    ST_INT type_id = -1;
    ST_RET ret;

    memset(&getvar_req, 0, sizeof(getvar_req));

    getvar_req.req_tag = GETVAR_NAME;
    getvar_req.name = *varObj;

    ret = mvla_getvar(net_info, &getvar_req, &reqCtrl);

    if (ret == SD_SUCCESS)
        ret = waitReqDone(reqCtrl, timeOut);

    if (ret == SD_SUCCESS)
    {
        type_spec = &reqCtrl->u.getvar.resp_info->type_spec;

        type_id = mvl_type_id_create(NULL,
                                     type_spec->data,
                                     type_spec->len);
    }

    if (reqCtrl)
        mvl_free_req_ctrl(reqCtrl);

    return type_id;
}
static ST_INT create_oper_type_id(MVL_NET_INFO *net_info,
                                  ST_CHAR *domName,
                                  const char *ctlName,
                                  ST_INT timeOut)
{
    OBJECT_NAME obj;
    ST_CHAR varName[MAX_IDENT_LEN + 1];

    if (net_info == NULL || domName == NULL || ctlName == NULL)
        return -1;

    memset(&obj, 0, sizeof(obj));

    apr_snprintf(varName,
                 sizeof(varName),
                 "LLN0$%s",
                 ctlName);

    obj.object_tag = DOM_SPEC;
    obj.domain_id = domName;
    obj.obj_name.vmd_spec = varName;

    printf("[CTRL_INIT] create type dom=%s var=%s\n",
           domName, varName);

    return ledrs_var_type_create(net_info, &obj, timeOut);
}
void InitLedrsOperTypeForChannel(chnl_usr_t *chnl_usr)
{
    printf("[CTRL_INIT] enter\n");

    if (chnl_usr == NULL || chnl_usr->chnl == NULL ||
        chnl_usr->chnl->ied == NULL || chnl_usr->net_info == NULL)
    {
        printf("[CTRL_INIT] invalid chnl_usr\n");
        return;
    }

    ied_t *ied = chnl_usr->chnl->ied;
    ied_usr_t *ied_usr = GET_IEDEXT_ADDR(ied);

    if (ied_usr == NULL || ied_usr->LD_info == NULL)
    {
        printf("[CTRL_INIT] invalid ied_usr or LD_info\n");
        return;
    }

    printf("[CTRL_INIT] current inited=%d ledrs=%d reboot=%d reset=%d\n",
           (int)ied_usr->oper_type_cache.inited,
           ied_usr->oper_type_cache.ledrs_oper_type_id,
           ied_usr->oper_type_cache.reboot_oper_type_id,
           ied_usr->oper_type_cache.reset_oper_type_id);

    if (ied_usr->oper_type_cache.inited == SD_TRUE)
    {
        printf("[CTRL_INIT] already inited\n");
        return;
    }

    char domName[256] = {0};

    BuildResetDomName(ied_usr,
                      domName,
                      sizeof(domName));

    printf("[CTRL_INIT] final dom=%s\n", domName);

    ied_usr->oper_type_cache.ledrs_oper_type_id =
        create_oper_type_id(chnl_usr->net_info,
                            domName,
                            "CO$LEDRs$Oper",
                            g_pt61850app->mmsOpTimeout);

    printf("[CTRL_INIT] LEDRs type_id=%d\n",
           ied_usr->oper_type_cache.ledrs_oper_type_id);

    ied_usr->oper_type_cache.reboot_oper_type_id =
        create_oper_type_id(chnl_usr->net_info,
                            domName,
                            "CO$Reboot$Oper",
                            g_pt61850app->mmsOpTimeout);

    printf("[CTRL_INIT] Reboot type_id=%d\n",
           ied_usr->oper_type_cache.reboot_oper_type_id);

    ied_usr->oper_type_cache.reset_oper_type_id =
        create_oper_type_id(chnl_usr->net_info,
                            domName,
                            "ST$Mod$stVal",
                            g_pt61850app->mmsOpTimeout);

    printf("[CTRL_INIT] Reset type_id=%d\n",
           ied_usr->oper_type_cache.reset_oper_type_id);

    /* 无论成功失败，都不再重复初始化 */
    ied_usr->oper_type_cache.inited = SD_TRUE;

    printf("[CTRL_INIT] finish inited=%d\n",
           (int)ied_usr->oper_type_cache.inited);
}


ST_RET write_common_oper(chnl_usr_t *chnl_usr,
                         ST_CHAR *domName,
                         const char *ctlName,
                         ST_INT oper_type_id,
                         ST_INT timeOut)
{
    if (chnl_usr == NULL ||
        chnl_usr->net_info == NULL ||
        domName == NULL ||
        ctlName == NULL ||
        oper_type_id < 0)
    {
        printf("[OPER_WRITE] invalid param\n");
        return SD_FAILURE;
    }

    ST_CHAR varName[MAX_IDENT_LEN + 1];
    apr_snprintf(varName,
                 sizeof(varName),
                 "LLN0$%s",
                 ctlName);

    Control_Oper_t oper;
    memset(&oper, 0, sizeof(oper));

    oper.ctlVal = SD_TRUE;
    oper.origin.orCat = 3;
    oper.origin.orIdent.len = 0;
    oper.ctlNum = 1;
    u_get_current_utc_time(&oper.T);
    oper.Test = SD_FALSE;
    oper.Check[0] = 0x00;
    oper.Check[1] = 0x00;

    if ((int)sizeof(Control_Oper_t) !=
        mvl_type_ctrl[oper_type_id].data_size)
    {
        printf("[OPER_WRITE] SIZE MISMATCH ctl=%s local=%d runtime=%d\n",
               ctlName,
               (int)sizeof(Control_Oper_t),
               mvl_type_ctrl[oper_type_id].data_size);
        return SD_FAILURE;
    }

    printf("[OPER_WRITE] dom=%s var=%s type_id=%d\n",
           domName, varName, oper_type_id);

    return mms_named_var_write(chnl_usr->net_info,
                               varName,
                               DOM_SPEC,
                               domName,
                               oper_type_id,
                               (ST_CHAR *)&oper,
                               timeOut);
}

ST_RET mms_conclude_disconnect(MVL_NET_INFO *net_info, ST_INT timeOut)
{
    MVL_REQ_PEND *reqCtrl = NULL;
    ST_RET ret;

    if (net_info == NULL)
        return SD_FAILURE;

    printf("[RESET] before mvl_concl\n");

    ret = mvl_concl(net_info, &reqCtrl);

    printf("[RESET] after mvl_concl ret=0x%X reqCtrl=%p\n",
           ret, reqCtrl);

    if (ret == SD_SUCCESS && reqCtrl != NULL)
    {
        ret = waitReqDone(reqCtrl, timeOut);

        printf("[RESET] conclude wait ret=0x%X\n", ret);
    }

    if (reqCtrl != NULL)
        mvl_free_req_ctrl(reqCtrl);

    return ret;
}

ST_RET write_mod_stval(chnl_usr_t *chnl_usr,
                       ST_CHAR *domName,
                       ST_INT timeOut)
{
    ST_CHAR varName[MAX_IDENT_LEN + 1];
    ST_INT16 value = 88;

    apr_snprintf(varName, sizeof(varName), "LLN0$ST$Mod$stVal");

    return mms_named_var_write(chnl_usr->net_info,
                               varName,
                               DOM_SPEC,
                               domName,
                               14,          //int
                               (ST_CHAR *)&value,
                               timeOut);
}

/************************************************************************/
/*				init_log_cfg 				*/
/************************************************************************/

#define HARD_CODED_CFG 
  
#ifdef DEBUG_SISCO
ST_VOID init_log_cfg (ST_VOID)
  {
   static char log_filename[256];
#if defined (HARD_CODED_CFG)
/* Use File logging							*/
  sLogCtrl->logCtrl = LOG_FILE_EN;

/* Use time/date time log						*/
  sLogCtrl->logCtrl |= (LOG_DIFFTIME_EN | LOG_TIME_EN);

/* File Logging Control defaults 					*/
#ifdef  _OS_UNIX_ 
	apr_snprintf(log_filename,sizeof(log_filename),"/usr/local/saslog/pt61850netd_pqfe_%d.log",g_node_id);
#else
	apr_snprintf(log_filename,sizeof(log_filename),"../etc/log/pt61850netd_pqfe_%d.log",g_node_id);
#endif 
  sLogCtrl->fc.fileName = log_filename;
 
  sLogCtrl->fc.maxSize = 50000000L;  // 50M
  sLogCtrl->fc.ctrl = (FIL_CTRL_WIPE_EN | 
                 FIL_CTRL_WRAP_EN | 
//		 FIL_CTRL_NO_APPEND |
                 FIL_CTRL_MSG_HDR_EN);

  mms_debug_sel |= MMS_LOG_ERR;
  mms_debug_sel |= MMS_LOG_NERR;
//  mms_debug_sel |= MMS_LOG_DEC;
//  mms_debug_sel |= MMS_LOG_ENC;

  mvl_debug_sel |= MVLLOG_ERR;
  mvl_debug_sel |= MVLLOG_NERR;

  mvl_debug_sel |= MVLLOG_ACSE;
#if 0  
  mvl_debug_sel |= MVLLOG_ACSEDATA;
  mvl_debug_sel |= MVLLOG_TIMING;
#endif

  acse_debug_sel |= ACSE_LOG_ERR;
#if 0
  acse_debug_sel |= ACSE_LOG_ENC;
  acse_debug_sel |= ACSE_LOG_DEC;
  acse_debug_sel |= COPP_LOG_ERR;
  acse_debug_sel |= COPP_LOG_DEC;
  acse_debug_sel |= COPP_LOG_DEC_HEX;
  acse_debug_sel |= COPP_LOG_ENC;
  acse_debug_sel |= COPP_LOG_ENC_HEX;
  acse_debug_sel |= COSP_LOG_ERR;
  acse_debug_sel |= COSP_LOG_DEC;
  acse_debug_sel |= COSP_LOG_DEC_HEX;
  acse_debug_sel |= COSP_LOG_ENC;
  acse_debug_sel |= COSP_LOG_ENC_HEX;
#endif


  tp4_debug_sel |= TP4_LOG_ERR;
#if 0
  tp4_debug_sel |= TP4_LOG_FLOWUP;
  tp4_debug_sel |= TP4_LOG_FLOWDOWN;
#endif

#if !defined(REDUCED_STACK)
  clnp_debug_sel |= CLNP_LOG_ERR;
  clnp_debug_sel |= CLNP_LOG_NERR;

#if 0  
  clnp_debug_sel |= CLNP_LOG_REQ;   
  clnp_debug_sel |= CLNP_LOG_IND;
  clnp_debug_sel |= CLNP_LOG_ENC_DEC;
  clnp_debug_sel |= CLNP_LOG_LLC_ENC_DEC;
  clnp_debug_sel |= CLSNS_LOG_REQ;
  clnp_debug_sel |= CLSNS_LOG_IND;
#endif
#endif

#else	/* !defined (HARD_CODED_CFG)	*/
#if defined(S_SEC_ENABLED)
  logCfgAddMaskGroup (&secLogMaskMapCtrl);
  logCfgAddMaskGroup (&ssleLogMaskMapCtrl);
#endif
  logCfgAddMaskGroup (&mvlLogMaskMapCtrl);
  logCfgAddMaskGroup (&mmsLogMaskMapCtrl);
  logCfgAddMaskGroup (&acseLogMaskMapCtrl);
  logCfgAddMaskGroup (&tp4LogMaskMapCtrl);
  logCfgAddMaskGroup (&clnpLogMaskMapCtrl);
  logCfgAddMaskGroup (&asn1LogMaskMapCtrl);
  logCfgAddMaskGroup (&sxLogMaskMapCtrl);
#if defined(S_MT_SUPPORT)
  logCfgAddMaskGroup (&gsLogMaskMapCtrl);
#endif
  logCfgAddMaskGroup (&sockLogMaskMapCtrl);
  logCfgAddMaskGroup (&memLogMaskMapCtrl);
  logCfgAddMaskGroup (&memDebugMapCtrl);

  if (logcfgx_ex (sLogCtrl, "logcfg.xml", NULL, SD_FALSE, SD_FALSE) != SD_SUCCESS)
    {
    printf ("\n Parsing of 'logging' configuration file failed.");
    if (sLogCtrl->fc.fileName)
      printf ("\n Check log file '%s'.", sLogCtrl->fc.fileName);
    exit (5);
    }
#endif	/* !defined (HARD_CODED_CFG)	*/
  }
#endif  /* DEBUG_SISCO */

