/************************************************************************/ /* SISCO SOFTWARE MODULE HEADER *****************************************/ /************************************************************************/ /* (c) Copyright Systems Integration Specialists Company, Inc., */ /* 2003 - 2005, All Rights Reserved */ /* */ /* MODULE NAME : cli_rpt.c */ /* PRODUCT(S) : MMSEASE-LITE */ /* */ /* MODULE DESCRIPTION : */ /* Functions to perform "client" processing of IEC-61850 Reports */ /* and UCA Reports received from "servers". */ /* */ /* GLOBAL FUNCTIONS DEFINED IN THIS MODULE : */ /* rpt_typeids_find */ /* rcb_info_create */ /* rcb_info_destroy */ /* u_iec_rpt_ind */ /* u_iec_rpt_ind_data (user modify or replace) */ /* */ /* MODIFICATION LOG : */ /* Date Who Rev Comments */ /* -------- --- ------ ------------------------------------------- */ /* 07/22/05 JRB 08 Allow one conn to control multiple RCBs */ /* (user_info must point to ALL_RCB_INFO). */ /* Save varNameArray in rcb_info to use later. */ /* Chg u_iec_rpt_ind_data 4th arg to (RCB_INFO *).*/ /* 12/03/04 JRB 07 Extract domain name of NVL from dataSetName. */ /* Increase RptID length to vstring65. */ /* 08/04/04 EJV 06 rcb_info_create: added typecast for Inclusion*/ /* 06/29/04 JRB 05 Del mvl_tdl_to_type_id & instead use new */ /* mvl_type_id_create_from_tdl. */ /* 05/13/04 JRB 04 Chg SqNum to INT16U to match 61850-7-2. */ /* 01/23/04 JRB 03 Use ms_local_to_text. */ /* 12/17/03 JRB 02 61850-8-1 FDIS changes: */ /* Decode ConfRev in rpt if enabled by OptFlds. */ /* Move SubSeqNum, MoreSegmentsFollow to just */ /* before inclusion bitstring. */ /* Chg OptFlds from bvstring9 to bvstring10. */ /* 10/24/03 JRB 01 New */ /************************************************************************/ #include "glbtypes.h" #include "sysincs.h" #include "mvl_acse.h" #include "mvl_log.h" #include "mvl_uca.h" /* only need OPTFLD_* defines */ #include "client.h" /************************************************************************/ /* 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 /************************************************************************/ /* Global variables. */ /************************************************************************/ /* NONE */ /************************************************************************/ /* Static function prototypes. */ /************************************************************************/ static OBJECT_NAME **nvl_var_name_array_create (MVL_NET_INFO *net_info, ST_CHAR *domName, ST_CHAR *nvlName, ST_INT *numVarOut, ST_INT timeOut); static ST_VOID nvl_var_name_array_destroy (OBJECT_NAME **varNameArray, ST_INT numVar); static ST_INT var_type_create (MVL_NET_INFO *net_info, OBJECT_NAME *varObj, ST_INT timeOut); ST_RET rcb_info_var_create (RCB_INFO *rcb_info, RPT_TYPEIDS *rpt_typeids); static ST_VOID rcb_info_var_destroy (RCB_INFO *rcb_info); static ST_VOID log_var_data (MVL_VAR_ASSOC *var); /* DEBUG: move these functions to library modules? */ OBJECT_NAME *object_name_clone_create (OBJECT_NAME *srcobj); ST_VOID object_name_clone_destroy (OBJECT_NAME *obj); /************************************************************************/ /* rpt_typeids_find */ /* Find ALL types needed for controlling & decoding IEC/UCA reports. */ /* These types must be defined in the ODF file and created by Foundry. */ /* RETURN: SD_SUCCESS or SD_FAILURE (if ANY type is NOT found) */ /* NOTE: this function based on "mvlu_rpt_find_typeids" in "mvlu_rpt.c".*/ /************************************************************************/ ST_RET rpt_typeids_find (RPT_TYPEIDS *rpt_typeids) { ST_RET retCode = SD_FAILURE; /* assume FAILURE for now */ ST_CHAR *type_name; /* name of type to be found */ /* stop on any error */ do { /* "one-time" loop: just to have something to break out of */ if ((rpt_typeids->mmsbool = mvl_typename_to_typeid (type_name = "RTYP_BOOL")) < 0) break; if ((rpt_typeids->bstr6 = mvl_typename_to_typeid (type_name = "RTYP_BSTR6")) < 0) break; if ((rpt_typeids->bstr8 = mvl_typename_to_typeid (type_name = "RTYP_BSTR8")) < 0) break; if ((rpt_typeids->bstr9 = mvl_typename_to_typeid (type_name = "RTYP_BSTR9")) < 0) break; if ((rpt_typeids->bvstring6 = mvl_typename_to_typeid (type_name = "RTYP_BVSTR6")) < 0) break; if ((rpt_typeids->bvstring8 = mvl_typename_to_typeid (type_name = "RTYP_BVSTR8")) < 0) break; if ((rpt_typeids->bvstring10 = mvl_typename_to_typeid (type_name = "RTYP_BVSTR10")) < 0) break; if ((rpt_typeids->btime6 = mvl_typename_to_typeid (type_name = "RTYP_BTIME6")) < 0) break; if ((rpt_typeids->int8u = mvl_typename_to_typeid (type_name = "RTYP_INT8U")) < 0) break; if ((rpt_typeids->int16u = mvl_typename_to_typeid (type_name = "RTYP_INT16U")) < 0) break; if ((rpt_typeids->int32u = mvl_typename_to_typeid (type_name = "RTYP_INT32U")) < 0) break; if ((rpt_typeids->ostring8 = mvl_typename_to_typeid (type_name = "RTYP_OSTR8")) < 0) break; if ((rpt_typeids->vstring32 = mvl_typename_to_typeid (type_name = "RTYP_VSTR32")) < 0) break; if ((rpt_typeids->vstring65 = mvl_typename_to_typeid (type_name = "RTYP_VSTR65")) < 0) break; retCode = SD_SUCCESS; /* If we get here, all were successful */ } while (0); /* end of "one-time" loop */ if (retCode) MVL_LOG_ERR1 ("Can't find type '%s'", type_name); return (retCode); /* If ANY find failed, SD_FAILURE is returned */ } /************************************************************************/ /* rcb_info_create */ /************************************************************************/ RCB_INFO *rcb_info_create (MVL_NET_INFO *net_info, ST_CHAR *domName, MVL_VMD_CTRL *vmd_ctrl, ST_CHAR *rcbName, RPT_TYPEIDS *rpt_typeids, ST_INT timeOut) { RCB_INFO *rcb_info; ST_CHAR varName [MAX_IDENT_LEN+1]; ST_CHAR datSetName [65+1]; /* data set name (Vstring65) */ ST_CHAR RptID [65+1]; /* RptID (Vstring65) */ ST_CHAR *nvlName; ST_INT numDsVar; /* num variables in NVL */ OBJECT_NAME **varNameArray; /* array of variable names in NVL */ ST_INT j; size_t extended_size; ST_CHAR *extended_ptr; ST_CHAR InclusionTdl [30]; /* place to create TDL for inclusion bstr*/ ST_RET status = SD_SUCCESS; /* set to SD_FAILURE if anything fails. */ ST_CHAR *nvlDomName; ST_INT rcb_type; //支持快速报告控制块信息获取而添加的变量 liwei 2012.11.24 OBJECT_NAME nvl_oname; MVL_NVLIST_CTRL *nvl; OBJECT_NAME *dstobj; int i; assert (rpt_typeids->mmsbool || rpt_typeids->int8u); /* make sure global struct initialized*/ /* Read "RptID" from the server & save in "RptID" local variable. */ /* Copy to "rcb_info" struct later. */ strcpy (varName, rcbName); strcat (varName, "$RptID"); if (var_read (net_info, varName, DOM_SPEC, domName, rpt_typeids->vstring65, RptID, timeOut)) { SLOGALWAYS2 ("Error reading RptID '%s' in domain '%s'", varName, domName); return (NULL); } /* Figure out RCB type from 'rcbName'. */ if (strstr (rcbName, "$BR$") != NULL) rcb_type = RCB_TYPE_IEC_BRCB; else if (strstr (rcbName, "$RP$") != NULL) { /* This could be IEC URCB or UCA URCB. Only IEC contains "Resv" attr.*/ /* Try to read "Resv". If successful, RCB is IEC_URCB, else it is UCA.*/ /* NOTE: "RptID" read was successful, so we know this RCB exists. */ ST_BOOLEAN tmpResv; strcpy (varName, rcbName); strcat (varName, "$Resv"); if (var_read (net_info, varName, DOM_SPEC, domName, rpt_typeids->mmsbool, &tmpResv, timeOut)==SD_SUCCESS) rcb_type = RCB_TYPE_IEC_URCB; else rcb_type = RCB_TYPE_UCA; } else { SLOGALWAYS1 ("RCB name '%s' invalid. Must contain 'BR' or 'RP'.", rcbName); return (NULL); } /* Read "DatSet" from the server. */ strcpy (varName, rcbName); strcat (varName, "$DatSet"); if (var_read (net_info, varName, DOM_SPEC, domName, rpt_typeids->vstring65, datSetName, timeOut)) { /* can't read data set name */ SLOGALWAYS2 ("Error reading data set name ('%s' in domain '%s')", varName, domName); return (NULL); } /* datSetName should now be set */ nvlDomName = strtok (datSetName, "/");/* extract domain name of NVL */ nvlName = strtok (NULL, ""); /* extract NVL name */ if (nvlName == NULL) /* "/" not found */ { SLOGALWAYS1 ("datSetName '%s' invalid. Does not contain '/' character.", datSetName); return (NULL); } //通过获取的数据集名在当前vmd中查找其控制结构体指针 liwei 2012.11.24////////////// nvl_oname.object_tag = DOM_SPEC; nvl_oname.domain_id = domName; nvl_oname.obj_name.item_id = nvlName; nvl = mvl_vmd_find_nvl (vmd_ctrl, &nvl_oname, NULL); if (nvl == NULL) { SLOGALWAYS1 ("datSetName '%s' no find.liwei", datSetName); return (NULL); } numDsVar = nvl->num_of_entries;//获取数据集元素的数目 /////////////////////////////////////////////////////////////////////////////////// /* Get NVL Attributes from the server */ /* NOTE: Resources allocated by this funct are freed by calling */ /* nvl_var_name_array_destroy (called from rcb_info_destroy). */ /*varNameArray = nvl_var_name_array_create (net_info, nvlDomName, nvlName, &numDsVar, timeOut); if (varNameArray == NULL) { SLOGALWAYS2 ("GetNamedVariableListAttributes request failed for '%s' in domain '%s'", nvlName, domName); return (NULL); }*/ /* Allocate RCB_INFO structure plus extra room for additional data * that depends on "numDsVar". This effectively combines 8 allocations into one. * Set pointers in RCB_INFO structure to point into the extra space. * CRITICAL: use chk_calloc to start with clean structure */ extended_size = sizeof (RCB_INFO) + numDsVar * sizeof (ST_INT) /* rcb_info->typeIdArr */ + numDsVar * sizeof (MVL_VAR_ASSOC *) /* rcb_info->rcb_var.dataRefName*/ + numDsVar * sizeof (MVL_VAR_ASSOC *) /* rcb_info->rcb_var.dataValue */ + numDsVar * sizeof (MVL_VAR_ASSOC *) /* rcb_info->rcb_var.Reason */ + numDsVar * 66 /* rcb_info->rcb_data.dataRefName*/ + numDsVar * sizeof (MMS_BVSTRING) /* rcb_info->rcb_data.Reason */ + BSTR_NUMBITS_TO_NUMBYTES(numDsVar); /* rcb_info->rcb_data.Inclusion */ rcb_info = (RCB_INFO *) chk_calloc (1, (unsigned int)extended_size); SLOGALWAYS3 ("Client RCB info 0x%X created for '%s' in domain '%s'", rcb_info, rcbName, domName); extended_ptr = (ST_CHAR *)(rcb_info + 1); /* point after RCB_INFO struct */ rcb_info->typeIdArr = (ST_INT *) extended_ptr; extended_ptr += numDsVar * sizeof (ST_INT); /* point after rcb_info->typeIdArr*/ rcb_info->rcb_var.dataRefName = (MVL_VAR_ASSOC **) extended_ptr; extended_ptr += numDsVar * sizeof (MVL_VAR_ASSOC *); /* rcb_info->rcb_var.dataRefName*/ rcb_info->rcb_var.dataValue = (MVL_VAR_ASSOC **) extended_ptr; extended_ptr += numDsVar * sizeof (MVL_VAR_ASSOC *); /* rcb_info->rcb_var.dataValue */ rcb_info->rcb_var.Reason = (MVL_VAR_ASSOC **) extended_ptr; extended_ptr += numDsVar * sizeof (MVL_VAR_ASSOC *); /* rcb_info->rcb_var.Reason */ rcb_info->rcb_data.dataRefName = extended_ptr; extended_ptr += numDsVar * 66; /* rcb_info->rcb_data.dataRefName*/ rcb_info->rcb_data.Reason = (MMS_BVSTRING *) extended_ptr; extended_ptr += numDsVar * sizeof (MMS_BVSTRING); /* rcb_info->rcb_data.Reason */ rcb_info->rcb_data.Inclusion = (ST_UINT8 *) extended_ptr; extended_ptr += BSTR_NUMBITS_TO_NUMBYTES(numDsVar); /* rcb_info->rcb_data.Inclusion */ /* Make sure we computed sizes correctly. */ assert (extended_ptr == (ST_CHAR *) rcb_info + extended_size); /* Fill in other rcb_info structure members. */ rcb_info->rcb_type = rcb_type; strcpy (rcb_info->RptID, RptID); /* save RptID to compare when RPTs received*/ //rcb_info->varNameArray = varNameArray; rcb_info->numDsVar = numDsVar; //生成数据集元素变量名列表 liwei 2012.11.24//////////////////////////// varNameArray = chk_malloc (numDsVar * sizeof (OBJECT_NAME *)); for (i = 0; i < numDsVar; i++) { extended_size = sizeof (OBJECT_NAME) + MAX_IDENT_LEN + 1 + MAX_IDENT_LEN + 1; dstobj =(OBJECT_NAME *) chk_malloc ((unsigned int)extended_size); extended_ptr = (ST_CHAR *)(dstobj + 1); dstobj->obj_name.vmd_spec = extended_ptr; extended_ptr += (MAX_IDENT_LEN + 1); dstobj->domain_id = extended_ptr; strcpy(dstobj->domain_id, domName); strcpy(dstobj->obj_name.vmd_spec, nvl->entries[i]->name); dstobj->object_tag = DOM_SPEC; varNameArray[i] = dstobj; } rcb_info->varNameArray = varNameArray; ///////////////////////////////////////////////////////////////////////// /* Create all necessary types. */ /* Try to create all types even if one fails. */ /* This way, we can later destroy all without remembering which */ /* were successfully created. */ /* Create a Type for the Inclusion bitstring. Save it in "rcb_info->InclusionTypeid" */ sprintf (InclusionTdl, "Bstring%d", rcb_info->numDsVar); rcb_info->InclusionTypeid = mvl_type_id_create_from_tdl (NULL, InclusionTdl); if (rcb_info->InclusionTypeid < 0) { SLOGALWAYS0 ("Error - could not add type for 'Inclusion'."); status = SD_FAILURE; } /* Create types and fill in rcb_info->typeIdArr */ /* Start with all set to invalid, so if some fail, cleanup is easier. */ for (j=0; jtypeIdArr [j] = -1; /* invalid type_id */ /*for (j=0; jtypeIdArr [j] = var_type_create (net_info, varNameArray[j], timeOut)) < 0) { SLOGALWAYS1 ("Error creating type for variable '%s'", varNameArray[j]->obj_name.vmd_spec); status = SD_FAILURE; break; } }*/ //生成报告各元素类型 liwei 2012.11.24///////////////////////////////////////////////// for (i=0; itypeIdArr [i] = nvl->entries[i]->type_id; ////////////////////////////////////////////////////////////////////////////////////// if (status == SD_SUCCESS) { /* Types created OK, so create variables */ status = rcb_info_var_create (rcb_info, rpt_typeids); /* returns SD_SUCCESS or SD_FAILURE*/ } if (status != SD_SUCCESS) { /* something failed. Destroy anything created. */ rcb_info_destroy (rcb_info); rcb_info = NULL; } return (rcb_info); } /************************************************************************/ /* rcb_info_destroy */ /************************************************************************/ ST_VOID rcb_info_destroy (RCB_INFO *rcb_info) { ST_INT j; /* Free up array of variable names saved in rcb_info_create */ nvl_var_name_array_destroy (rcb_info->varNameArray, rcb_info->numDsVar); /* Destroy all variables. */ rcb_info_var_destroy (rcb_info); /* Destroy "Inclusion" Type (this should be AFTER destroying "Inclusion" Variable).*/ if (rcb_info->InclusionTypeid > 0) /* 0=never created. -1=error */ mvl_type_id_destroy (rcb_info->InclusionTypeid); //liwei 2012.07.13 if (rcb_info->rcb_create_type == 0) { for (j=0; jnumDsVar; j++) { if (rcb_info->typeIdArr [j] >= 0) mvl_type_id_destroy (rcb_info->typeIdArr [j]); } } chk_free (rcb_info); SLOGALWAYS1 ("Client RCB info 0x%X destroyed", rcb_info); } /************************************************************************/ /* nvl_var_name_array_create */ /* Send a GetNamedVariableListAttributes request to get a list of */ /* variable names in a NamedVariableList (NVL). */ /* Assume the NVL is domain-specific */ /* RETURN: ptr to array of ptrs to variable names (OBJECT_NAME structs).*/ /* Also the variable pointed to by numVarOut contains the number*/ /* of variables in the NVL. */ /* NOTE: this function allocates memory for the (OBJECT_NAME *) array. */ /* Call "nvl_var_name_array_destroy" to free the memory. */ /************************************************************************/ static OBJECT_NAME **nvl_var_name_array_create (MVL_NET_INFO *net_info, ST_CHAR *domName, ST_CHAR *nvlName, ST_INT *numVarOut, ST_INT timeOut) { MVL_REQ_PEND *reqCtrl; GETVLIST_REQ_INFO getvlist_req; GETVLIST_RESP_INFO *getvlist_resp; /* set to reqCtrl->u.getvlist_resp_info*/ VARIABLE_LIST *variable_list; OBJECT_NAME **varNameArray; /* Ptr to array of ptrs to var names */ ST_RET ret; /* general purpose return code */ ST_INT j; getvlist_req.vl_name.object_tag = DOM_SPEC; getvlist_req.vl_name.domain_id = domName; getvlist_req.vl_name.obj_name.vmd_spec = nvlName; ret = mvla_getvlist (net_info, &getvlist_req, &reqCtrl); if (ret == SD_SUCCESS) ret = waitReqDone (reqCtrl, timeOut); if (ret != SD_SUCCESS) { SLOGALWAYS1 ("mvla_getvlist Error = 0x%X.", ret); varNameArray = NULL; } else { getvlist_resp = reqCtrl->u.getvlist.resp_info; variable_list = (VARIABLE_LIST *) (getvlist_resp + 1); /* Allocate array of variable names. */ *numVarOut = getvlist_resp->num_of_variables; varNameArray = chk_malloc (getvlist_resp->num_of_variables * sizeof (OBJECT_NAME *)); /* Copy info from response to allocated array */ for (j = 0; j < getvlist_resp->num_of_variables; j++, variable_list++) { /* Assume all variables are named. */ assert (variable_list->var_spec.var_spec_tag == VA_SPEC_NAMED); varNameArray[j] = object_name_clone_create (&variable_list->var_spec.vs.name); } } mvl_free_req_ctrl (reqCtrl); /* CRITICAL: */ return (varNameArray); } /************************************************************************/ /* nvl_var_name_array_destroy */ /* Free up all resources allocated by "nvl_var_name_array_create". */ /************************************************************************/ static ST_VOID nvl_var_name_array_destroy (OBJECT_NAME **varNameArray, ST_INT numVar) { ST_INT j; for (j = 0; j < numVar; j++) object_name_clone_destroy (varNameArray[j]); chk_free (varNameArray); } /************************************************************************/ /* var_type_create */ /* Send GetVariableAccessAttributes request. Wait for response. When */ /* response received, create new type. Pass NULL as type_name arg to */ /* "mvl_type_id_create" (this avoids the problem of duplicate names). */ /* RETURN: Type ID */ /* NOTE: Call "mvl_type_id_destroy" to destroy the type created here. */ /************************************************************************/ static ST_INT 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; /* invalid. If anything fails, this value returned*/ ST_RET ret; getvar_req.req_tag = GETVAR_NAME; getvar_req.name = *varObj; /* Send GetVariableAccessAttributes request & wait for response. */ ret = mvla_getvar (net_info, &getvar_req, &reqCtrl); if (ret == SD_SUCCESS) ret = waitReqDone (reqCtrl, timeOut); if (ret != SD_SUCCESS) SLOGALWAYS1 ("GetVariableAccessAttributes error = 0x%x.", ret); else { type_spec = &reqCtrl->u.getvar.resp_info->type_spec; /* First arg (type_name) is NULL to avoid duplicate names. */ type_id = mvl_type_id_create (NULL, type_spec->data, type_spec->len); } mvl_free_req_ctrl (reqCtrl); return (type_id); } /************************************************************************/ /* rcb_info_var_create */ /* Create dummy variables to be used later when a report is received. */ /* These variables are needed to decode the data received in a report. */ /* This function fills in "rcb_info->rcb_data" and "rcb_info->rcb_var". */ /************************************************************************/ ST_RET rcb_info_var_create (RCB_INFO *rcb_info, RPT_TYPEIDS *rpt_typeids) { OBJECT_NAME dummyvar_objname; /* dummy variable object name */ ST_INT j; ST_INT incSize; /* num bytes for inclusion bitstring */ /* CRITICAL: "mvl_var_create" is used (NOT "mvl_var_add") to create * local variables to store data received later in IEC/UCA Reports. * These are NOT real variables, so remote MMS nodes cannot access them. * All variables use the same name (dummyvar): that's OK because * "mvl_var_create" does NOT add them to the database and nothing needs * to use the name. */ /* NOTE: last arg to mvl_var_create is always SD_FALSE, so the name is * NOT copied. This is OK because all names here are fixed strings. */ /* Set up one OBJECT_NAME to use for all variables. */ dummyvar_objname.object_tag = VMD_SPEC; dummyvar_objname.obj_name.vmd_spec = "dummyvar"; /* all vars use same name */ if ((rcb_info->rcb_var.RptID = mvl_var_create (&dummyvar_objname, rpt_typeids->vstring65, &rcb_info->rcb_data.RptID, NULL, SD_FALSE)) == NULL) return (SD_FAILURE); if ((rcb_info->rcb_var.OptFlds = mvl_var_create (&dummyvar_objname, rpt_typeids->bvstring10, &rcb_info->rcb_data.OptFlds, NULL, SD_FALSE)) == NULL) return (SD_FAILURE); if ((rcb_info->rcb_var.SqNum = mvl_var_create (&dummyvar_objname, rpt_typeids->int16u, &rcb_info->rcb_data.SqNum, NULL, SD_FALSE)) == NULL) return (SD_FAILURE); if ((rcb_info->rcb_var.TimeOfEntry = mvl_var_create (&dummyvar_objname, rpt_typeids->btime6, &rcb_info->rcb_data.TimeOfEntry, NULL, SD_FALSE)) == NULL) return (SD_FAILURE); if ((rcb_info->rcb_var.DatSetNa = mvl_var_create (&dummyvar_objname, rpt_typeids->vstring65, &rcb_info->rcb_data.DatSetNa, NULL, SD_FALSE)) == NULL) return (SD_FAILURE); if ((rcb_info->rcb_var.BufOvfl = mvl_var_create (&dummyvar_objname, rpt_typeids->mmsbool, &rcb_info->rcb_data.BufOvfl, NULL, SD_FALSE)) == NULL) return (SD_FAILURE); if ((rcb_info->rcb_var.SubSeqNum = mvl_var_create (&dummyvar_objname, rpt_typeids->int16u, &rcb_info->rcb_data.SubSeqNum, NULL, SD_FALSE)) == NULL) return (SD_FAILURE); if ((rcb_info->rcb_var.MoreSegmentsFollow = mvl_var_create (&dummyvar_objname, rpt_typeids->mmsbool, &rcb_info->rcb_data.MoreSegmentsFollow, NULL, SD_FALSE)) == NULL) return (SD_FAILURE); if ((rcb_info->rcb_var.EntryID = mvl_var_create (&dummyvar_objname, rpt_typeids->ostring8, &rcb_info->rcb_data.EntryID, NULL, SD_FALSE)) == NULL) return (SD_FAILURE); if ((rcb_info->rcb_var.ConfRev = mvl_var_create (&dummyvar_objname, rpt_typeids->int32u, &rcb_info->rcb_data.ConfRev, NULL, SD_FALSE)) == NULL) return (SD_FAILURE); incSize = BSTR_NUMBITS_TO_NUMBYTES(rcb_info->numDsVar); /* Note: rcb_info->rcb_data.Inclusion is a "ptr" to data, so don't add "&". */ if ((rcb_info->rcb_var.Inclusion = mvl_var_create (&dummyvar_objname, rcb_info->InclusionTypeid, rcb_info->rcb_data.Inclusion, NULL, SD_FALSE)) == NULL) return (SD_FAILURE); /* Allocate array of DataRefName (vstring65) */ /* & array of (MVL_VAR_ASSOC *). */ /* Can't alloc array of strings, so we just allocate one big buffer */ /* and use [j*66] to find postion for each string in the buffer. */ /* Create separate variable for each array entry. */ for (j=0; jnumDsVar; j++) { if ((rcb_info->rcb_var.dataRefName[j] = mvl_var_create (&dummyvar_objname, rpt_typeids->vstring65, &rcb_info->rcb_data.dataRefName[j*66], NULL, SD_FALSE)) == NULL) return (SD_FAILURE); } /* Create variable for each DataSet variable. */ for (j=0; jnumDsVar; j++) { ST_VOID *remote_data; /* buffer to store remote data */ MVL_TYPE_CTRL *type_ctrl; type_ctrl = mvl_type_ctrl_find (rcb_info->typeIdArr[j]); assert (type_ctrl); /* find should never fail */ /* Allocate buffer to store remote data. */ remote_data = chk_malloc (type_ctrl->data_size); if ((rcb_info->rcb_var.dataValue[j] = mvl_var_create (&dummyvar_objname, rcb_info->typeIdArr[j], /* Use type just created */ remote_data, /* buffer for data */ NULL, /* proc funs */ SD_FALSE)) /* DON'T copy name */ == NULL) { /* couldn't create this variable, so stop */ return (SD_FAILURE); } } /* Allocate array of Reason (bstr6)(one byte for each Reason) */ /* & array of (MVL_VAR_ASSOC *). */ /* Create separate variable for each array entry. */ for (j=0; jnumDsVar; j++) { /* Use bvstring8 because IEC sends bstr6 and UCA sends bstr8. */ if ((rcb_info->rcb_var.Reason[j] = mvl_var_create (&dummyvar_objname, rpt_typeids->bvstring8, &rcb_info->rcb_data.Reason[j], NULL, SD_FALSE)) == NULL) return (SD_FAILURE); } /* If we get this far, everything was successful. */ return (SD_SUCCESS); } /************************************************************************/ /* rcb_info_var_destroy */ /* Destroy everything created in "rcb_info_var_create". */ /* Check that each was successfully creating before destroying. Any one */ /* of the "mvl_var_create" calls in "rcb_info_var_create" may have failed.*/ /************************************************************************/ static ST_VOID rcb_info_var_destroy (RCB_INFO *rcb_info) { ST_INT j; if (rcb_info->rcb_var.RptID) mvl_var_destroy (rcb_info->rcb_var.RptID); if (rcb_info->rcb_var.OptFlds) mvl_var_destroy (rcb_info->rcb_var.OptFlds); if (rcb_info->rcb_var.SqNum) mvl_var_destroy (rcb_info->rcb_var.SqNum); if (rcb_info->rcb_var.TimeOfEntry) mvl_var_destroy (rcb_info->rcb_var.TimeOfEntry); if (rcb_info->rcb_var.DatSetNa) mvl_var_destroy (rcb_info->rcb_var.DatSetNa); if (rcb_info->rcb_var.BufOvfl) mvl_var_destroy (rcb_info->rcb_var.BufOvfl); if (rcb_info->rcb_var.SubSeqNum) mvl_var_destroy (rcb_info->rcb_var.SubSeqNum); if (rcb_info->rcb_var.MoreSegmentsFollow) mvl_var_destroy (rcb_info->rcb_var.MoreSegmentsFollow); if (rcb_info->rcb_var.EntryID) mvl_var_destroy (rcb_info->rcb_var.EntryID); if (rcb_info->rcb_var.ConfRev) mvl_var_destroy (rcb_info->rcb_var.ConfRev); if (rcb_info->rcb_var.Inclusion) mvl_var_destroy (rcb_info->rcb_var.Inclusion); /* Destroy "dataRefName" variables. */ for (j=0; jnumDsVar; j++) { if (rcb_info->rcb_var.dataRefName[j]) mvl_var_destroy (rcb_info->rcb_var.dataRefName[j]); } /* Destroy "dataValue" variables. */ for (j=0; jnumDsVar; j++) { if (rcb_info->rcb_var.dataValue[j]!=NULL) { chk_free (rcb_info->rcb_var.dataValue[j]->data); mvl_var_destroy (rcb_info->rcb_var.dataValue[j]); } } /* Destroy "Reason" variables. */ for (j=0; jnumDsVar; j++) { if (rcb_info->rcb_var.Reason[j]) mvl_var_destroy (rcb_info->rcb_var.Reason[j]); } } /************************************************************************/ /* This sample function sets some options and enables the RCB. */ /* If any write fails, stop. */ /* If more options needed, add more arguments to this function. */ /************************************************************************/ ST_RET rcb_enable (MVL_NET_INFO *netInfo, ST_CHAR *domName, ST_CHAR *rcbName, ST_UCHAR *OptFlds, ST_UCHAR *TrgOps, ST_UINT32 IntgPd, RPT_TYPEIDS *rpt_typeids, ST_INT timeOut) { ST_BOOLEAN RptEna = 1; /* 1 = enable the report */ ST_RET ret = SD_SUCCESS; ST_CHAR varName [MAX_IDENT_LEN + 1]; #if 0 /* Add something like this if other options needed. */ if (ret == SD_SUCCESS) { sprintf (varName, "%s$IntgPd", rcbName); ret = var_write (netInfo, varName, DOM_SPEC, domName, rpt_typeids->int32u, (ST_CHAR *) &IntgPd, timeOut); } if (ret == SD_SUCCESS) { /* NOTE: only write 9 bits. 10th bit (segmentation) is read-only. */ sprintf (varName, "%s$OptFlds", rcbName); ret = var_write (netInfo, varName, DOM_SPEC, domName, rpt_typeids->bstr9, OptFlds, timeOut); } if (ret == SD_SUCCESS) { sprintf (varName, "%s$TrgOps", rcbName); ret = var_write (netInfo, varName, DOM_SPEC, domName, rpt_typeids->bstr8, TrgOps, timeOut); } if (ret == SD_SUCCESS) { sprintf (varName, "%s$Trgs", rcbName); ret = named_var_write (netInfo, varName, DOM_SPEC, domName, rpt_typeids->int16u, (ST_CHAR *) &Trgs, timeOut); } #endif if (ret == SD_SUCCESS) { sprintf (varName, "%s$RptEna", rcbName); ret = var_write (netInfo, varName, DOM_SPEC, domName, rpt_typeids->mmsbool, (ST_CHAR *) &RptEna, timeOut); } return (ret); } /************************************************************************/ /************************************************************************/ static ST_VOID log_var_data (MVL_VAR_ASSOC *var) { ST_CHAR tdl_buf [500]; /* increase size if complex TDL expected*/ MVL_TYPE_CTRL *type_ctrl; ST_CHAR *data_text; /* var data converted to text */ ST_CHAR text_buf [30000]; /* increase size if ms_local_to_text fails */ type_ctrl = mvl_type_ctrl_find (var->type_id); if (type_ctrl) { /* If the TDL produced is longer than max_tdl_len, this function */ /* "gracefully" fails (i.e. returns 0). */ if (ms_runtime_to_tdl (type_ctrl->rt, type_ctrl->num_rt, tdl_buf, sizeof(tdl_buf))>0) SLOGCALWAYS1 (" TYPE: %s", tdl_buf); else SLOGCALWAYS0 (" TYPE: unknown"); data_text = ms_local_to_text (var->data, type_ctrl->rt, type_ctrl->num_rt, text_buf, sizeof (text_buf)); if (data_text) SLOGCALWAYS1 (" DATA: %s", data_text); else SLOGCALWAYS0 (" DATA: cannot be converted to text"); } else SLOGCALWAYS0 (" ERR: type_id is invalid"); } /************************************************************************/ /* u_iec_rpt_ind_data */ /* User function to process the received report data. This example */ /* function simply writes the data to the log file. */ /* */ /* It should be easy to modify or rewrite this function to do whatever */ /* is appropriate for your application. */ /************************************************************************/ ST_VOID u_iec_rpt_ind_data (MVL_VAR_ASSOC **info_va, ST_UINT8 *OptFldsData, /* ptr to data part of OptFlds bvstring */ ST_UINT8 *InclusionData, /* ptr to Inclusion bstring */ RCB_INFO *rcb_info, ST_INT va_total, RPT_INFO *rpt_info) { ST_INT va_num=0; ST_INT j; rpt_info->RptID = (char *) info_va[va_num]->data; va_num++; rpt_info->OptFlds = info_va[va_num]->data; va_num++; /* NOTE: Don't change initial entries in "info_va". Add these right after "OptFlds".*/ if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_SQNUM)) { rpt_info->SqNum = (ST_UINT16 *) info_va[va_num]->data; va_num++; } else rpt_info->SqNum = NULL; if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_TIMESTAMP)) { rpt_info->TimeOfEntry = (MMS_BTIME6 *) info_va[va_num]->data; va_num++; } else rpt_info->TimeOfEntry = NULL; if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_DATSETNAME)) { rpt_info->DatSetNa = (char *) info_va[va_num]->data; va_num++; } else rpt_info->DatSetNa = NULL; if (BSTR_BIT_GET (OptFldsData, OPTFLD_BITNUM_BUFOVFL)) { rpt_info->BufOvfl = (ST_BOOLEAN *) info_va[va_num]->data; va_num++; } else rpt_info->BufOvfl = NULL; if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_ENTRYID)) { rpt_info->EntryID = (ST_UINT8 *) info_va[va_num]->data; va_num++; } else rpt_info->EntryID = NULL; if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_CONFREV)) { rpt_info->ConfRev = (ST_UINT32 *) info_va[va_num]->data; va_num++; } else rpt_info->ConfRev = NULL; if (BSTR_BIT_GET (OptFldsData,OPTFLD_BITNUM_SUBSEQNUM)) { rpt_info->SubSeqNum = (ST_UINT16 *) info_va[va_num]->data; va_num++; rpt_info->MoreSegmentsFollow = (ST_BOOLEAN *) info_va[va_num]->data; va_num++; } else { rpt_info->SubSeqNum = NULL; rpt_info->MoreSegmentsFollow = NULL; } rpt_info->Inclusion = (ST_UINT8 *) info_va[va_num]->data; va_num++; /* If data-Ref enabled, check "Inclusion" to figure out what is being received.*/ if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_DATAREF)) { for (j = 0; j < rcb_info->numDsVar; ++j) { if (BSTR_BIT_GET (InclusionData, j)) { rpt_info->dataRefName[j] = (char *) info_va[va_num]->data; va_num++; } else rpt_info->dataRefName[j] = NULL; } } /* HERE'S THE DATA. Check "Inclusion" to figure out what is being received.*/ for (j = 0; j < rcb_info->numDsVar; ++j) { if (BSTR_BIT_GET (InclusionData, j)) { rpt_info->Value[j] = info_va[va_num]->data; va_num++; } else rpt_info->Value[j] = NULL; } /* If "reason" enabled, check "Inclusion" to figure out what is being received.*/ if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_REASON)) { for (j = 0; j < rcb_info->numDsVar; ++j) { if (BSTR_BIT_GET (InclusionData, j)) { rpt_info->Reason[j] = (MMS_BVSTRING *)info_va[va_num]->data; va_num++; } else rpt_info->Reason[j] = NULL; } } rpt_info->num_data = rcb_info->numDsVar; assert (va_num == va_total); /* Did we count right? */ } /************************************************************************/ /* object_name_clone_create */ /* Create clone of OBJECT_NAME struct. Can't just copy struct because */ /* struct contains pointers to strings. */ /* The function "object_name_clone_destroy" should be called to destroy */ /* the clone. */ /************************************************************************/ OBJECT_NAME *object_name_clone_create (OBJECT_NAME *srcobj) { OBJECT_NAME *dstobj; size_t extended_size; ST_CHAR *extended_ptr; /* Allocate OBJECT_NAME structure plus extra room for additional data. * This effectively combines 3 allocations into one. * Set pointers in OBJECT_NAME structure to point into the extra space. */ extended_size = sizeof (OBJECT_NAME) + MAX_IDENT_LEN + 1 /* dstobj->obj_name.vmd_spec */ + MAX_IDENT_LEN + 1; /* dstobj->domain_id */ dstobj =(OBJECT_NAME *) chk_malloc ((unsigned int)extended_size); /* Copy old struct to new struct (before setting ptrs in new struct). */ memcpy (dstobj, srcobj, sizeof (OBJECT_NAME)); /* Fix ptrs to strings in new struct */ extended_ptr = (ST_CHAR *)(dstobj + 1); /* point after dstobj struct */ dstobj->obj_name.vmd_spec = extended_ptr; extended_ptr += (MAX_IDENT_LEN + 1); /* point after dstobj->obj_name.vmd_spec*/ dstobj->domain_id = extended_ptr; /* Copy strings to the new struct. */ strcpy (dstobj->obj_name.vmd_spec, srcobj->obj_name.vmd_spec); if (dstobj->object_tag == DOM_SPEC) strcpy (dstobj->domain_id, srcobj->domain_id); return (dstobj); } /************************************************************************/ /* object_name_clone_destroy */ /* Destroy OBJECT_NAME clone created by "object_name_clone_create". */ /************************************************************************/ ST_VOID object_name_clone_destroy (OBJECT_NAME *obj) { /* allocated by object_name_clone_create using chk_malloc, so use chk_free.*/ chk_free (obj); } /************************************************************************/ /* u_iec_rpt_ind */ /* This function processes ONLY IEC-61850 and UCA Reports. If any */ /* other InformationReport is received, it logs an error message and */ /* ignores it. */ /* CRITICAL: this function assumes that a pointer to an ALL_RCB_INFO */ /* structure has been saved in the */ /* user_info member of the MVL_NET_INFO structure for this conn. */ /************************************************************************/ ST_RET u_iec_rpt_ind (MVL_COMM_EVENT *event) { INFO_REQ_INFO *info_ptr; ST_INT j, va_num, va_total; VAR_ACC_SPEC *va_spec; MVL_VAR_ASSOC **info_va; RCB_INFO *rcb_info; ST_UINT8 *OptFldsData; /* ptr to data part of OptFlds bvstring */ ST_UINT8 *InclusionData; /* ptr to Inclusion bstring */ ST_CHAR saveRptID [66]; ALL_RCB_INFO *all_rcb_info; ST_RET retcode = SD_SUCCESS; RPT_INFO rpt_info; CLIENT_CTRL *client_ctrl;// info_ptr = (INFO_REQ_INFO *) event->u.mms.dec_rslt.data_ptr; va_spec = &info_ptr->va_spec; va_total = info_ptr->num_of_acc_result; /* An IEC-61850 or UCA report must be a NamedVariableList named "RPT". * Ignore any other InformationReport. */ if (va_spec->var_acc_tag != VAR_ACC_NAMEDLIST || strcmp (va_spec->vl_name.obj_name.vmd_spec, "RPT") != 0) { SLOGALWAYS0 ("Received InformationReport is not a IEC-61850 Report or UCA Report. Ignored."); return (SD_FAILURE); } /* Get "all_rcb_info" from "user_info". User must set "user_info" when conn established.*/ all_rcb_info = event->net_info->user_info; /* Check "all_rcb_info" to see if any RCB has been enabled. */ if (all_rcb_info == NULL || all_rcb_info->rcb_info_list == NULL) { SLOGALWAYS0 ("Received InformationReport. No IEC-61850 or UCA RCB enabled. Ignored."); return (SD_FAILURE); } /* Create array of (MVL_VAR_ASSOC *) needed for converting the received * data to local format. * Use variables created earlier to fill in the array. These variables will * hold the decoded data. * NOTE: any return after this must free info_va (see CLEANUP label). */ info_va = (MVL_VAR_ASSOC **) chk_calloc (va_total, sizeof (MVL_VAR_ASSOC *)); /* Must decode the RptID first to find the correct RCB_INFO. Could decode into * any "vstring65" variable. We just use the RptID variable from the first * rcb_info on the list. */ rcb_info = all_rcb_info->rcb_info_list; /* use first rcb_info on list*/ va_num=0; info_va[va_num++] = rcb_info->rcb_var.RptID; rcb_info->rcb_data.RptID[0] = '\0'; /* start with empty string */ mvl_info_data_to_local (event, va_num, info_va); /* NOTE: decoded RptID is now in "rcb_info->rcb_data.RptID". Save it.*/ strcpy (saveRptID, rcb_info->rcb_data.RptID); /* Search list of "rcb_info" to find one with matching RptID. */ /* If not found, rcb_info will be == NULL. */ 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 (strcmp (rcb_info->RptID, saveRptID) == 0) break; /* rcb_info now points to right structure */ } if (!rcb_info) { SLOGALWAYS1 ("RptID '%s' not recognized on this connection. Received report ignored.", saveRptID); retcode = SD_FAILURE; goto CLEANUP; } va_num=0; info_va[va_num++] = rcb_info->rcb_var.RptID; info_va[va_num++] = rcb_info->rcb_var.OptFlds; /* Perform 1st decode (up through "OptFlds"). Need "OptFlds" to figure * out what comes next. */ mvl_info_data_to_local (event, va_num, info_va); /* Examine "OptFlds", and set up 2nd decode to decode all options */ OptFldsData = rcb_info->rcb_data.OptFlds.data_1; /* use local var */ /* NOTE: Don't change initial entries in "info_va". Add these right after "OptFlds".*/ if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_SQNUM)) info_va[va_num++] = rcb_info->rcb_var.SqNum; if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_TIMESTAMP)) info_va[va_num++] = rcb_info->rcb_var.TimeOfEntry; if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_DATSETNAME)) info_va[va_num++] = rcb_info->rcb_var.DatSetNa; /* The following optional vars are supported by IEC-61850 but they are * NOT supported by UCA. This client must NOT set these OptFlds bits * when connected to a UCA server, and these variables will NOT be * included in a report received from a UCA server. */ if (BSTR_BIT_GET (OptFldsData, OPTFLD_BITNUM_BUFOVFL)) info_va[va_num++] = rcb_info->rcb_var.BufOvfl; if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_ENTRYID)) info_va[va_num++] = rcb_info->rcb_var.EntryID; if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_CONFREV)) info_va[va_num++] = rcb_info->rcb_var.ConfRev; if (BSTR_BIT_GET (OptFldsData,OPTFLD_BITNUM_SUBSEQNUM)) { info_va[va_num++] = rcb_info->rcb_var.SubSeqNum; info_va[va_num++] = rcb_info->rcb_var.MoreSegmentsFollow; } info_va[va_num++] = rcb_info->rcb_var.Inclusion; assert (va_num < va_total); /* Perform 2nd decode (up through "Inclusion"). */ mvl_info_data_to_local (event, va_num, info_va); /* Examine "Inclusion", and set up 3rd decode to decode all data. */ InclusionData = rcb_info->rcb_data.Inclusion; /* use local var */ /* NOTE: Don't change initial entries in "info_va". Add these right after "Inclusion".*/ /* If "data-Ref" enabled, check "Inclusion" to figure out what is being received.*/ if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_DATAREF)) { for (j = 0; j < rcb_info->numDsVar; ++j) { if (BSTR_BIT_GET (InclusionData, j)) info_va[va_num++] = rcb_info->rcb_var.dataRefName[j]; } } /* HERE'S THE DATA. Check "Inclusion" to figure out what is being received.*/ for (j = 0; j < rcb_info->numDsVar; ++j) { if (BSTR_BIT_GET (InclusionData, j)) info_va[va_num++] = rcb_info->rcb_var.dataValue[j]; } /* If "reason" enabled, check "Inclusion" to figure out what is being received.*/ if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_REASON)) { for (j = 0; j < rcb_info->numDsVar; ++j) { if (BSTR_BIT_GET (InclusionData, j)) info_va[va_num++] = rcb_info->rcb_var.Reason[j]; } } /* Does num of variables received match expected num.*/ if (va_num!=va_total) SLOGALWAYS2 ("Num of var received in RPT (%d) does not match expected (%d). Possibly incorrect OptFlds or inclusion bitstring", va_total, va_num); else { /* Perform 3rd decode (everything). */ rpt_info.dataRefName = (ST_CHAR **) chk_calloc(rcb_info->numDsVar, sizeof(ST_CHAR *)); rpt_info.Value = (void **)chk_calloc(rcb_info->numDsVar, sizeof(void *)); rpt_info.Reason = (MMS_BVSTRING **)chk_calloc(rcb_info->numDsVar, sizeof(MMS_BVSTRING *)); mvl_info_data_to_local (event, va_num, info_va); u_iec_rpt_ind_data (info_va, OptFldsData, InclusionData, rcb_info, va_total, &rpt_info); //liwei 调用报告控制回调函数 client_ctrl = FindConnectServer(event->net_info); if (client_ctrl != NULL) { if (client_ctrl->rpt_callback_fun != NULL) client_ctrl->rpt_callback_fun(client_ctrl->sever_name, &rpt_info); } chk_free(rpt_info.Value); chk_free(rpt_info.Reason); chk_free(rpt_info.dataRefName); } CLEANUP: chk_free (info_va); return (retcode); }