/************************************************************************/ /* SISCO SOFTWARE MODULE HEADER *****************************************/ /************************************************************************/ /* (c) Copyright Systems Integration Specialists Company, Inc., */ /* 2006-2006 All Rights Reserved */ /* */ /* MODULE NAME : mvl61850_rpt.c */ /* PRODUCT(S) : MMSEASE-LITE */ /* */ /* MODULE DESCRIPTION : */ /* Code to support IEC 61850 Reporting as a Server. */ /* */ /* BUFFERING OF REPORTS: */ /* For Buffered Report Controls, buffering is automatic (no user */ /* intervention). For each BRCB, all reports are stored in a linked */ /* list "rpt_list". Also, "rpt_list_next" points to the next report to */ /* send. If all reports have been sent, rpt_list_next==NULL. */ /* */ /* The maximum number of bytes to store in the buffer is a */ /* user-configurable parameter. When the maximum number of bytes is */ /* exceeded, the oldest report in the buffer is discarded. */ /* */ /* The standard SISCO linked list functions are used for buffering. */ /* */ /* GLOBAL FUNCTIONS DEFINED IN THIS MODULE : */ /* mvl61850_brcb_rpt_save */ /* mvl61850_brcb_rpt_send */ /* mvl61850_brcb_rpt_lists_clean */ /* mvl61850_brcb_client_service */ /* mvl61850_urcb_client_service */ /* */ /* MODIFICATION LOG : */ /* Date Who Rev Comments */ /* -------- --- ------ ------------------------------------------- */ /* 06/30/08 JRB 10 For Ed2, save lastSentTimeOfEntry for BRCB. */ /* Resync to oldest if all 0 written to EntryID.*/ /* 06/04/08 JRB 09 Start buffering on power-up if DatSet attr */ /* references existing DataSet (for 61850-7-2 Ed2)*/ /* 05/15/08 JRB 08 Add mvl61850_integrity_timeout for 61850 only*/ /* (does NOT scan data, just sends current data)*/ /* 05/06/08 JRB 07 Fix memory leak after "PDU size" error. */ /* 02/29/08 JRB 06 Create correct AA_SPEC or VMD_SPEC DatSet. */ /* Reset cur_va_index, SubSeqNum when all_seg_done*/ /* so retransmission starts with first va. */ /* If dsNvl==NULL, do not service RCB. */ /* 02/13/07 JRB 05 mvl61850_get_rcb: del unused net_info arg. */ /* Use scan_rate to control scan timing. */ /* Save TimeofEntry in "basrcb". */ /* 11/30/06 JRB 04 Fix *_brcb_entryid_init so user can call it. */ /* 10/10/06 JRB 03 Add mvl61850_rpt_ctrl_destroy_all. */ /* 08/18/06 JRB 02 Disable debug printing. Clean up comments. */ /* 08/09/06 JRB 01 New. Moved most 61850 Rpt code to here */ /* with following changes: */ /* New RPT functions just for 61850 (not UCA): */ /* mvl61850_create_rpt_ctrl, mvl61850_free_rpt_ctrl,*/ /* mvl61850_rpt_service. */ /* Chg BRCB code to save "raw" data in buffer */ /* and encode rpt only when sending. */ /* Chg to one rpt list with ptr to next to send.*/ /* ..urcb_client_service: First chk if connected.*/ /* Put start time in first 4 bytes of EntryID. */ /* When GI rpt buffered, discard old GI rpt. */ /************************************************************************/ #include "glbtypes.h" #include "sysincs.h" #include "mvl_acse.h" /* need mvl_cfg_info */ #include "mvl_uca.h" #include "mvl_log.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 /* DEBUG: IEC 61850 is not clear about exactly how to handle "BufOvfl", * so the code in this module implements the following: * 1. If client writes EntryID & the value can't be found in the buffer, * BufOvfl is set, an error is returned, and the oldest buffer entry will * be sent next (see mvl61850_brcb_rpt_set_entryid). * 2. If entry found but discarded before RptEna, * BufOvfl is set, and the oldest buffer entry will * be sent next (see buflist_discard_old). * 3. If entries lost while connected, set BufOvfl (see buflist_discard_old). * 4. After one report sent with BufOvfl, clear it * (see mvl61850_brcb_client_service). */ /************************************************************************/ /* print macros for debugging */ /************************************************************************/ #if 0 /* DEBUG: enable to see queue activity. */ /* Use function here (too complex for macro). */ ST_VOID print_queues(MVL61850_BRCB_CTRL *brcbCtrl) { BUFLIST_ENTRY *entry; printf ("REPORT BUFFER ('.'=rpt, '+'=next_to_send)"); for (entry = brcbCtrl->rpt_list; entry != NULL; entry = (BUFLIST_ENTRY *) list_get_next (brcbCtrl->rpt_list, entry)) { if (entry == brcbCtrl->rpt_list_next) putchar('+'); else putchar('.'); } putchar('\n'); } #define print_msg0(fmt) printf (fmt) #define print_msg1(fmt,a) printf (fmt,a) #else #define print_queues(brcbCtrl) #define print_msg0(fmt) #define print_msg1(fmt,a) #endif /************************************************************************/ /* mvl61850_mk_rptid */ /* Construct RptID from info in rptCtrl. */ /* RETURNS: SD_SUCCESS or SD_FAILURE */ /* Also on error, put a NULL string in RptID & log error. */ /************************************************************************/ ST_RET mvl61850_mk_rptid (MVLU_RPT_CTRL *rptCtrl, ST_CHAR *RptID, size_t max_len) { ST_RET retcode; /* If domname + '/' + basrcb_name will fit in RptID, then write it. */ if (strlen (rptCtrl->dsNvl->nvl_scope.dom->name) + strlen (rptCtrl->basrcb_name) + 1 <= max_len) { sprintf (RptID, "%s/%s", rptCtrl->dsNvl->nvl_scope.dom->name, rptCtrl->basrcb_name); retcode = SD_SUCCESS; } else { MVL_LOG_ERR0 ("Cannot construct RptID (too long)"); /* should NEVER happen*/ RptID [0] = '\0'; /* return empty RptID */ retcode = SD_FAILURE; } return (retcode); } /************************************************************************/ /* mvl61850_mk_dataref */ /************************************************************************/ ST_VOID mvl61850_mk_dataref (MVL_VAR_ASSOC *va, MVL_SCOPE *va_scope, ST_CHAR *databuf, ST_INT maxlen) { if (va_scope->scope == DOM_SPEC) { strcpy (databuf, va_scope->dom->name); strcat (databuf, "/"); } else if (va_scope->scope == VMD_SPEC) strcpy (databuf, "/"); else /* must be AA_SPEC */ strcpy (databuf, "@"); strncat (databuf, va->name, maxlen - strlen (databuf)); } /************************************************************************/ /* mvl61850_rcb_cleanup */ /* Cleanup RPT info to prepare for next report. */ /************************************************************************/ ST_VOID mvl61850_rcb_cleanup (MVLU_RPT_CLIENT *rptClient) { MVL_NVLIST_CTRL *dsNvl; ST_INT incSize; /* num bytes for inclusion bitstring */ dsNvl = rptClient->rpt_ctrl->dsNvl; incSize = BSTR_NUMBITS_TO_NUMBYTES(dsNvl->num_of_entries); /* Reset reasons, etc. to prepare for new report triggers. */ memset (rptClient->reasons_data, 0, dsNvl->num_of_entries); memset (rptClient->changed_flags, 0, incSize); rptClient->numTrgs = 0; } /************************************************************************/ /* mvl61850_brcb_rpt_set_entryid */ /************************************************************************/ ST_RET mvl61850_brcb_rpt_set_entryid (MVLU_RPT_CLIENT *rptClient, ST_UCHAR *EntryID) { BUFLIST_ENTRY *entry; BUFLIST_ENTRY *foundEntry = NULL; /* matching entry, assume NULL (not found)*/ MVL61850_BRCB_CTRL *brcbCtrl; ST_RET retcode = SD_FAILURE; /* assume failure */ ST_UCHAR all_zero[8]={0,0,0,0,0,0,0,0}; /* Assume this is a BRCB. Should never get here otherwise. */ assert (rptClient->rpt_ctrl->rcb_type == RCB_TYPE_IEC_BRCB); brcbCtrl = &rptClient->rpt_ctrl->brcbCtrl; /* Is client writing all 0? */ if (memcmp (all_zero, EntryID, 8) == 0) { /* special case. Just resync to oldest rpt in buffer */ brcbCtrl->rpt_list_next = brcbCtrl->rpt_list; /* oldest */ return (SD_SUCCESS); /* this can never fail*/ } /* Search "sent" list for this ID. */ /* If "rpt_list_next != NULL" start search there. */ /* If "rpt_list_next == NULL" start search at end of list. */ if (brcbCtrl->rpt_list_next) entry = brcbCtrl->rpt_list_next; else entry = (BUFLIST_ENTRY *) list_find_last ((DBL_LNK *) brcbCtrl->rpt_list); for ( ; entry != NULL; entry = (BUFLIST_ENTRY *) list_find_prev ((DBL_LNK *) brcbCtrl->rpt_list, (DBL_LNK *) entry)) { if (memcmp (entry->EntryID, EntryID, 8) == 0) { foundEntry = entry; break; } } /* If matching entry found, just point to next one. */ if (foundEntry) { brcbCtrl->rpt_list_next = list_get_next (brcbCtrl->rpt_list, entry); retcode = SD_SUCCESS; } else { /* not found, point to "oldest" rpt */ if((entry = brcbCtrl->rpt_list_next)) printf("next_entry =%02x%02x%02x%02x%02x%02x%02x%02x\n\r",entry->EntryID[0],entry->EntryID[1],entry->EntryID[2], entry->EntryID[3],entry->EntryID[4],entry->EntryID[5],entry->EntryID[6],entry->EntryID[7]); if((entry = (BUFLIST_ENTRY *) list_find_last ((DBL_LNK *) brcbCtrl->rpt_list))) printf("last_entry=%02x%02x%02x%02x%02x%02x%02x%02x\n\r",entry->EntryID[0],entry->EntryID[1],entry->EntryID[2], entry->EntryID[3],entry->EntryID[4],entry->EntryID[5],entry->EntryID[6],entry->EntryID[7]); printf("find_entry=%02x%02x%02x%02x%02x%02x%02x%02x\n\r",EntryID[0],EntryID[1],EntryID[2],EntryID[3], EntryID[4],EntryID[5],EntryID[6],EntryID[7]); brcbCtrl->rpt_list_next = brcbCtrl->rpt_list; /* oldest */ brcbCtrl->BufOvfl = SD_TRUE; /* overflow occurred */ } return (retcode); } /************************************************************************/ /* fill_asn1_len_array */ /* Compute the ASN.1 encoded len for each var in a rpt dataset, and */ /* fill in "bufEntry->asn1_len_array". */ /************************************************************************/ static ST_RET fill_asn1_len_array ( BUFLIST_ENTRY *bufEntry, MVL_NVLIST_CTRL *dsNvl) /* rpt dataset */ { ST_UCHAR *tmp_asn1_buf; /* temporary ASN.1 buffer (allocated) */ ST_INT tmp_asn1_buf_size = mvl_cfg_info->max_msg_size; /* reasonable size*/ ST_INT j; ST_RET retCode=SD_SUCCESS; /* assume success */ tmp_asn1_buf = chk_malloc (tmp_asn1_buf_size); for (j = 0; j < dsNvl->num_of_entries; ++j) { if (bufEntry->var_data[j].reason_for_incl != 0) { /* this var should be included in rpt */ ST_UCHAR *asn1_ptr; MVL_TYPE_CTRL *type_ctrl = dsNvl->entries[j]->type_ctrl; retCode = ms_local_to_asn1_2 (type_ctrl->rt, type_ctrl->num_rt, bufEntry->var_data[j].data_ptr, /* CRITICAL: use data from buffer entry*/ tmp_asn1_buf, tmp_asn1_buf_size, &asn1_ptr, /* function sets asn1_ptr (not needed here)*/ &bufEntry->asn1_len_array[j]); /* function sets len at this ptr*/ if (retCode) { /* Should NEVER fail, but if so, don't continue with other vars.*/ break; } } } /* end loop */ chk_free (tmp_asn1_buf); return (retCode); } /************************************************************************/ /* buflist_entry_create */ /* Allocate and initialize BUFLIST_ENTRY and add it to list. */ /* Also update brcbCtrl->cur_bufsize & brcbCtrl->rpt_count. */ /************************************************************************/ BUFLIST_ENTRY *buflist_entry_create (MVLU_RPT_CLIENT *rptClient) { BUFLIST_ENTRY *bufEntry; MVL_NVLIST_CTRL *dsNvl = rptClient->rpt_ctrl->dsNvl; ST_INT j; int num; MVL61850_BRCB_CTRL *brcbCtrl = &rptClient->rpt_ctrl->brcbCtrl; bufEntry = (BUFLIST_ENTRY *) chk_calloc (1, sizeof (BUFLIST_ENTRY)); bufEntry->num_var = dsNvl->num_of_entries; /* Use calloc so start out with all data_ptr=NULL & all reason_for_incl=0.*/ bufEntry->var_data = (VAR_DATA *) chk_calloc (bufEntry->num_var, sizeof (VAR_DATA)); bufEntry->asn1_len_array = chk_calloc (dsNvl->num_of_entries, sizeof(ST_INT)); for (j = 0; j < bufEntry->num_var; j++) { /* Check "reason for inclusion". */ if (rptClient->reasons_data[j] != 0) { /* this var must be in the report */ ST_INT data_len; MVL_VAR_ASSOC *var_assoc = dsNvl->entries[j]; MVL_TYPE_CTRL *type_ctrl = dsNvl->entries[j]->type_ctrl; VAR_DATA *var_data = &bufEntry->var_data[j]; /* Find data_len */ data_len = type_ctrl->data_size; brcbCtrl->cur_bufsize += data_len; /* update buffer size*/ var_data->data_len = data_len; var_data->data_ptr = chk_malloc (data_len); memcpy (var_data->data_ptr, var_assoc->data, data_len); /* Save "reason for inclusion". */ var_data->reason_for_incl = rptClient->reasons_data[j]; } } /* Fill in other entry data. */ /* Point to last 4 bytes of Ostring, cast to INT32 & increment.*/ #ifdef WIN32 num = ((int)rptClient->basrcb.EntryID[7]<<0 )+ ((int)rptClient->basrcb.EntryID[6]<<8 )+ ((int)rptClient->basrcb.EntryID[5]<<16)+ ((int)rptClient->basrcb.EntryID[4]<<24); num++; rptClient->basrcb.EntryID[7] = (ST_UCHAR)(num>>0); rptClient->basrcb.EntryID[6] = (ST_UCHAR)(num>>8); rptClient->basrcb.EntryID[5] = (ST_UCHAR)(num>>16); rptClient->basrcb.EntryID[4] = (ST_UCHAR)(num>>24); #else ++(*(ST_INT32 *)(&rptClient->basrcb.EntryID[4])); #endif /* Then copy to bufEntry. */ memcpy (bufEntry->EntryID, rptClient->basrcb.EntryID, 8); /* Call user function to get report time. */ u_mvlu_rpt_time_get (&bufEntry->TimeOfEntry); /* Copy report time to RCB. */ memcpy (&rptClient->basrcb.TimeofEntry, &bufEntry->TimeOfEntry, sizeof (MMS_BTIME6)); /* Fill in "bufEntry->asn1_len_array", needed later by "chk_seg_needed". */ if (fill_asn1_len_array (bufEntry, dsNvl)!=SD_SUCCESS) { /* NOTE: This should NEVER fail, but if it does, just log it. */ /* Real encode will almost certainly fail later. */ MVL_LOG_ERR0 ("Cannot calculate ASN.1 length for Buffered Report."); } /* Add entry to list. */ list_add_last (&brcbCtrl->rpt_list, bufEntry); brcbCtrl->rpt_count++; /* Update list count */ /* If next ptr is NULL (all old rpts sent), point to this new entry.*/ if (brcbCtrl->rpt_list_next == NULL) brcbCtrl->rpt_list_next = bufEntry; return (bufEntry); } /************************************************************************/ /* buflist_entry_destroy */ /* Remove BUFLIST_ENTRY from list and free it. */ /* Also update brcbCtrl->cur_bufsize & brcbCtrl->rpt_count. */ /************************************************************************/ ST_VOID buflist_entry_destroy (MVL61850_BRCB_CTRL *brcbCtrl, BUFLIST_ENTRY *entry) { ST_INT j; if (entry == brcbCtrl->rpt_list_next) { /* Deleting next rpt to send. */ /* CRITICAL: must fix up next to send. */ brcbCtrl->rpt_list_next = list_get_next (brcbCtrl->rpt_list, entry); } list_unlink (&brcbCtrl->rpt_list, entry); /* remove rpt from list */ brcbCtrl->rpt_count--; /* update list count */ /* Free all in reverse order. */ for (j = 0; j < entry->num_var; j++) { VAR_DATA *var_data = &entry->var_data[j]; if (var_data->data_ptr) { chk_free (var_data->data_ptr); brcbCtrl->cur_bufsize -= var_data->data_len; /* update buffer size*/ } } chk_free (entry->asn1_len_array); chk_free (entry->var_data); chk_free (entry); } /************************************************************************/ /* buflist_discard_old_gi */ /* Search for old GI report in buffer, and delete it. */ /************************************************************************/ ST_VOID buflist_discard_old_gi (MVL61850_BRCB_CTRL *brcbCtrl) { BUFLIST_ENTRY *entry; for (entry = brcbCtrl->rpt_list; entry != NULL; entry = list_get_next (brcbCtrl->rpt_list, entry)) { /* For GI rpt, "reason_for_incl" for all vars is MVLU_TRGOPS_GI, */ /* so just check reason for first var. */ if (entry->var_data[0].reason_for_incl == MVLU_TRGOPS_GI) { /* Remove entry from list and free it. */ buflist_entry_destroy (brcbCtrl, entry); break; /* never more than one GI, so stop looping now */ } } } /************************************************************************/ /* buflist_discard_old */ /* If memory usage exceeds buffer size, remove oldest reports from list */ /* until memory usage is reduced. */ /************************************************************************/ ST_VOID buflist_discard_old (MVL61850_BRCB_CTRL *brcbCtrl) { BUFLIST_ENTRY *entry; while (brcbCtrl->cur_bufsize > brcbCtrl->brcb_bufsize) { entry = brcbCtrl->rpt_list; /* find first on list */ if (entry != NULL) { if (entry == brcbCtrl->rpt_list_next) { /* Deleting next rpt to send (i.e. rpts are being lost). */ /* Set BufOvfl flag. */ brcbCtrl->BufOvfl = SD_TRUE; brcbCtrl->rpts_lost_count++; /* keep a count of lost reports */ } /* Remove entry from list and free it. */ buflist_entry_destroy (brcbCtrl, entry); } else { /* This error should never happen. Buffer is much too small.*/ MVL_LOG_ERR0 ("Report buffer size too small to store any reports."); } } /* end loop */ } /************************************************************************/ /* mvl61850_brcb_rpt_lists_clean */ /* Delete and destroy all entries on the buffered report linked list */ /* for this BRCB. */ /************************************************************************/ ST_VOID mvl61850_brcb_rpt_lists_clean (MVL61850_BRCB_CTRL *brcbCtrl) { BUFLIST_ENTRY *entry; /* Loop removing first entry from list. */ while ((entry = brcbCtrl->rpt_list) != NULL) buflist_entry_destroy (brcbCtrl, entry); /* removes it from list*/ assert (brcbCtrl->rpt_list_next == NULL); /* if list empty, this must be NULL*/ if (brcbCtrl->rpt_count != 0 || brcbCtrl->cur_bufsize != 0) { MVL_LOG_ERR2 ("ERROR removing buffered reports from list (count=%d, bufsize=%d). Possible memory leak.", brcbCtrl->rpt_count, brcbCtrl->cur_bufsize); brcbCtrl->rpt_count = 0; /* reset count */ brcbCtrl->cur_bufsize = 0; /* reset buffer size */ } brcbCtrl->BufOvfl = SD_FALSE; /* Clear BufOvfl flag */ brcbCtrl->rpt_list_next = NULL; /* CRITICAL: must not point to deleted entry*/ } /************************************************************************/ /* mvl61850_brcb_rpt_save */ /* For BRCB, just buffer the raw data in a linked list to be encoded */ /* and sent later (see mvl61850_brcb_rpt_send). */ /************************************************************************/ ST_RET mvl61850_brcb_rpt_save (MVLU_RPT_CLIENT *rptClient) { /* If this is GI rpt, discard previous GI rpt from buffer before */ /* saving new one in buffer. */ if (rptClient->reasons_data[0] == MVLU_TRGOPS_GI) buflist_discard_old_gi (&rptClient->rpt_ctrl->brcbCtrl); buflist_entry_create (rptClient); /* add rpt to buffer */ /* If buffer is full, discard oldest reports until not full. */ buflist_discard_old (&rptClient->rpt_ctrl->brcbCtrl); mvl61850_rcb_cleanup (rptClient); /* prepare for next RPT. */ return (SD_SUCCESS); } /************************************************************************/ /* mvl61850_brcb_rpt_send */ /* Encode and send a Buffered Report (possibly in multiple segments). */ /* NOTE: This is similar to mvl61850_urcb_rpt_send. The main difference */ /* is that it gets most data from the BUFLIST_ENTRY struct. */ /* NOTE: DOES NOT use rptClient->reasons_data, rptClient->changed_flags,*/ /* rptClient->numTrgs. Use "reason_for_incl" from BUFLIST_ENTRY */ /* to construct inclusion bitstring. */ /************************************************************************/ ST_RET mvl61850_brcb_rpt_send (MVLU_RPT_CTRL *rptCtrl, MVLU_RPT_CLIENT *rptClient, BUFLIST_ENTRY *bufEntry, ST_BOOLEAN *all_seg_done) { MVL_NVLIST_CTRL *dsNvl; MVL_NVLIST_CTRL *rptNvl; MVL_VAR_ASSOC *va; ST_INT j; /* loop counter */ ST_RET retCode; ST_INT sendIndex; ST_INT sendIndexSave; /* save index before data, compare after data */ ST_UINT8 *optFlds; MVLU_BASRCB *basrcb; ST_INT incSize; /* num bytes for inclusion bitstring */ MVL_VAR_ASSOC *tmp_va_arr; /* alloc array of structs */ ST_INT tmp_va_arr_size; /* num of entries in tmp_va_arr */ MVL_VAR_ASSOC *tmp_va; /* current entry in tmp_va_arr */ /* For Segmented reports, all segments are generated by this function, * so these parameters can be local variables. */ ST_INT next_va_index; /* index into dsNvl va array */ ST_BOOLEAN segNeeded; /* If TRUE, segmenting needed */ ST_BOOLEAN MoreSegmentsFollow; ST_CHAR *tmp_dataref_buf; ST_CHAR tmpRptID [MVL61850_MAX_RPTID_LEN+1]; retCode = SD_SUCCESS; /* assume success. Change if error detected.*/ *all_seg_done = SD_FALSE; /* initialize flag */ basrcb = &rptClient->basrcb; optFlds = basrcb->OptFlds.data_1; dsNvl = rptCtrl->dsNvl; rptNvl = &rptCtrl->rptNvl; incSize = BSTR_NUMBITS_TO_NUMBYTES(dsNvl->num_of_entries); MVLU_LOG_FLOW1 ("Building IEC-61850 BRCB Report, MVL_NET_INFO %08lx", rptClient->netInfo); /* Need tmp va's for options, array of data_refs, array of dataset vars,*/ /* array of reason_for_incl. */ tmp_va_arr_size = MVLU_MAX_RPT_OPTS + (dsNvl->num_of_entries * 3); tmp_va_arr = M_CALLOC (MSMEM_GEN, sizeof (MVL_VAR_ASSOC), tmp_va_arr_size); /* Need tmp buffer for datarefs. One buffer for all. */ /* Allow max len plus NULL for each dataref. */ tmp_dataref_buf = M_CALLOC (MSMEM_GEN, (MVL61850_MAX_OBJREF_LEN+1), dsNvl->num_of_entries); assert (rptCtrl->dsNvl->num_of_entries); /* must be >0 entries */ /* MAIN LOOP */ /* Start with "bufEntry->cur_va_index" and "bufEntry->SubSeqNum".*/ /* Usually they are both 0, but if segmenting occurs and buffers were */ /* not available to send all segments, they indicate what is next. */ for (next_va_index = bufEntry->cur_va_index; next_va_index < rptCtrl->dsNvl->num_of_entries; bufEntry->cur_va_index = next_va_index, bufEntry->SubSeqNum++) { /* BEGIN MAIN LOOP */ if (retCode) break; /* something failed in this loop or earlier loop, so stop now.*/ /* If buffers not available, break out of loop. */ /* Use cur_va_index, SubSeqNum in bufEntry as starting point */ /* next time this function is called. */ if (mvl_ureq_bufs_avail (rptClient->netInfo) <= 0) /* stack bufs avail to send rpt */ break; tmp_va = tmp_va_arr; /* start loop pointing to first entry */ sendIndex = 0; /* start with first rptNvl->entries. */ /* Prepare a Report NVL to be sent, based on the */ /* options, data, and inclusion bitstring in the rptCtrl. */ /* CRITICAL: asn1_len_array must be filled in before call to chk_seg_needed.*/ segNeeded = chk_seg_needed (rptClient, bufEntry->asn1_len_array, bufEntry->cur_va_index, &next_va_index); if (next_va_index - bufEntry->cur_va_index == 0) { MVL_LOG_ERR0 ("MMS PDU size too small to fit ANY variables in IEC-61850 Report. Report not sent."); retCode = SD_FAILURE; break; /* break out of loop so normal cleanup occurs. */ } /* We will create a NVL to send, using the dsNvl as a model ... */ /* RptID and OptFlds are always the first 2 entries in the Rpt NVL. */ /* Get RptID from RCB (OK because changing it causes PurgeBuf). */ tmp_va->type_id = rptCtrl->rpt_typeids.vstring65; if (basrcb->RptID [0] == '\0') { /* RptID not set in RCB, so construct it. */ mvl61850_mk_rptid (rptCtrl, tmpRptID, MVL61850_MAX_RPTID_LEN); tmp_va->data = tmpRptID; } else tmp_va->data = basrcb->RptID; MVLU_LOG_CFLOW1 (" RptID='%s", tmp_va->data); /* log before tmp_va++*/ rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/ /* Get OptFlds from RCB (OK because changing it causes PurgeBuf). */ tmp_va->type_id = rptCtrl->rpt_typeids.bvstring10; tmp_va->data = &basrcb->OptFlds; rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/ MVLU_LOG_CFLOW2 (" OptFld = 0x%02x 0x%02x", basrcb->OptFlds.data_1[0], basrcb->OptFlds.data_1[1]); /* 10 bit bstr (2 bytes) */ /* Add optional RCB vars to NVL, depending on OptFlds. */ /* Get SqNum from RCB. */ if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_SQNUM)) { assert (rptCtrl->rcb_type == RCB_TYPE_IEC_BRCB); /* SqNum in BRCB is INT16U */ tmp_va->type_id = rptCtrl->rpt_typeids.int16u; tmp_va->data = &basrcb->SqNumInt16u; rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/ MVLU_LOG_CFLOW1 (" SqNum : %u", (unsigned) basrcb->SqNumInt16u); } /* Get TimeofEntry from BUFLIST_ENTRY. */ if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_TIMESTAMP)) { MVLU_LOG_CFLOW2 (" RptTim : %lums, %lu days", bufEntry->TimeOfEntry.ms, bufEntry->TimeOfEntry.day); tmp_va->type_id = rptCtrl->rpt_typeids.btime6; tmp_va->data = &bufEntry->TimeOfEntry; /* use time saved in buffer*/ rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/ } /* Get DatSet from RCB (OK because changing it causes PurgeBuf). */ if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_DATSETNAME)) { /* UCA called it OutDat instead of DatSet. Value is same. */ MVLU_LOG_CFLOW1 (" DatSet : %s", basrcb->DatSetNa); tmp_va->type_id = rptCtrl->rpt_typeids.vstring129; /*ObjectReference*/ tmp_va->data = basrcb->DatSetNa; rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/ } /* Get BufOvfl. */ if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_BUFOVFL)) { tmp_va->type_id = rptCtrl->rpt_typeids.mmsbool; tmp_va->data = &rptCtrl->brcbCtrl.BufOvfl; rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/ } /* Get EntryID from BUFLIST_ENTRY. */ if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_ENTRYID)) { /* NOTE: EntryID only incremented when RPT buffered. */ /* Copied to bufEntry->EntryID after incrementing. */ tmp_va->type_id = rptCtrl->rpt_typeids.ostring8; tmp_va->data = bufEntry->EntryID; rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/ } /* Add "ConfRev" if enabled by "OptFlds". */ if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_CONFREV)) { tmp_va->type_id = rptCtrl->rpt_typeids.int32u; tmp_va->data = &basrcb->ConfRev; rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/ } if(segNeeded) printf("brcb:segNeeded=%d num=%d va_index = %d->%d\r\n", segNeeded,rptCtrl->dsNvl->num_of_entries,bufEntry->cur_va_index, next_va_index); if (segNeeded) { /* Set appropriate bit in OptFlds. */ /* NOTE: this bit only for output. Never used as input. */ BSTR_BIT_SET_ON (optFlds,OPTFLD_BITNUM_SUBSEQNUM); tmp_va->type_id = rptCtrl->rpt_typeids.int16u; tmp_va->data = &bufEntry->SubSeqNum; rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/ if (next_va_index < rptCtrl->dsNvl->num_of_entries) MoreSegmentsFollow = SD_TRUE; else MoreSegmentsFollow = SD_FALSE; tmp_va->type_id = rptCtrl->rpt_typeids.mmsbool; tmp_va->data = &MoreSegmentsFollow; rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/ } else { BSTR_BIT_SET_OFF (optFlds,OPTFLD_BITNUM_SUBSEQNUM); } /* Construct inclusion bitstring using "reason_for_incl" in BUFLIST_ENTRY. */ tmp_va->type_id = rptCtrl->inclusion_typeid; /* just set the bits included in this segment. */ memset (rptClient->segmented_inclusion, 0, incSize); /* start clean */ for (j = bufEntry->cur_va_index; j < next_va_index; ++j) { if (bufEntry->var_data[j].reason_for_incl) BSTR_BIT_SET_ON (rptClient->segmented_inclusion, j); } tmp_va->data = rptClient->segmented_inclusion; rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/ /* If data-Ref enabled, go through inclusion_data to decide what to send*/ if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_DATAREF)) { for (j = bufEntry->cur_va_index; j < next_va_index; ++j) { if (bufEntry->var_data[j].reason_for_incl) { /* include dataRef for this variable */ tmp_va->type_id = rptCtrl->rpt_typeids.vstring129; /*ObjectReference*/ /* point to right part of tmp buffer & construct dataref */ tmp_va->data = &tmp_dataref_buf [j * (MVL61850_MAX_OBJREF_LEN+1)]; mvl61850_mk_dataref (dsNvl->entries[j], &dsNvl->va_scope[j], tmp_va->data, MVL61850_MAX_OBJREF_LEN); rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/ } } } /* HERE'S DATA: Go through inclusion_data to decide what to send */ sendIndexSave = sendIndex; /* save index before data included */ for (j = bufEntry->cur_va_index; j < next_va_index; ++j) { if (bufEntry->var_data[j].reason_for_incl) { /* include this variable */ va = dsNvl->entries[j]; tmp_va->data = bufEntry->var_data[j].data_ptr; /* buffered data*/ tmp_va->type_id = va->type_id; MVLU_LOG_CFLOW3 (" Including variable %d ('%s'), reason_for_incl=0x%02X", j, va->name, bufEntry->var_data[j].reason_for_incl); rptNvl->entries[sendIndex++] = tmp_va++; } } assert (sendIndex>sendIndexSave); /* make sure SOME data included */ /* If "reason" enabled, go through inclusion_data to decide what to send*/ if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_REASON)) { for (j = bufEntry->cur_va_index; j < next_va_index; ++j) { if (bufEntry->var_data[j].reason_for_incl) { /* include reason for this variable */ /* Don't need BVSTR here, because size is fixed. */ tmp_va->type_id = rptCtrl->rpt_typeids.bstr6; tmp_va->data = &bufEntry->var_data[j].reason_for_incl; rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/ } } } rptNvl->num_of_entries = sendIndex; assert (rptNvl->num_of_entries <= rptCtrl->maxNumRptVars); /* past end of array?*/ assert ((tmp_va-tmp_va_arr) <= tmp_va_arr_size); /* past end of array?*/ /* Encode the InformationReport and send it. */ retCode = mvl_info_variables (rptClient->netInfo, rptNvl, SD_FALSE); /* FALSE means "send as NVL" */ } /* END MAIN LOOP */ /* Did we send all segments of this report? */ if (retCode == SD_SUCCESS && bufEntry->cur_va_index == rptCtrl->dsNvl->num_of_entries) { *all_seg_done = SD_TRUE; /* YES: Set flag */ /* CRITICAL: Reset these to be ready for retransmit later. */ bufEntry->cur_va_index = 0; bufEntry->SubSeqNum = 0; } M_FREE (MSMEM_GEN, tmp_va_arr); M_FREE (MSMEM_GEN, tmp_dataref_buf); return (retCode); } /************************************************************************/ /* mvl61850_brcb_enable_all */ /* Start buffering all buffered reports. */ /************************************************************************/ ST_VOID mvl61850_brcb_enable_all () { MVLU_RPT_CTRL *rptCtrl; for (rptCtrl = mvl61850_rpt_ctrl_list; rptCtrl != NULL; rptCtrl = (MVLU_RPT_CTRL *) list_get_next (mvl61850_rpt_ctrl_list, rptCtrl)) { if (rptCtrl->rcb_type == RCB_TYPE_IEC_BRCB) rptCtrl->brcbCtrl.enabled_once = SD_TRUE; } } /************************************************************************/ /* mvl61850_brcb_entryid_init */ /* Set initial value of EntryID for this BRCB. EntryID must point to */ /* an 8 byte octet string. */ /************************************************************************/ ST_VOID mvl61850_brcb_entryid_init (MVLU_RPT_CTRL *rptCtrl, ST_UINT8 *EntryID) { memcpy (rptCtrl->only_client.basrcb.EntryID, EntryID, 8); } /************************************************************************/ /* mvl61850_rpt_change_scan_va_done */ /* Used by code that scans for dchg, qchg, dupd changes. */ /* This function is called by 'mvlu_rd_prim_done' when all "leaf" */ /* functions for a VA have been completed (i.e. data ready for this VA).*/ /************************************************************************/ void mvlu_log_va_change (MVL_VAR_ASSOC *va, ST_UCHAR reason, ST_VOID *new_data); ST_VOID mvl61850_rpt_change_scan_va_done (MVL_IND_PEND *indCtrl, MVL_VAR_ASSOC *va) { /* If "rpt_reason" set by ANY leaf function, call mvlu_rpt_va_change. */ if (va->rpt_reason) { mvlu_rpt_va_change (va, va->rpt_reason, NULL); /*renxiaobao 日志 //mvlu_log_va_change (va, va->rpt_reason, NULL);*/ } } /************************************************************************/ /* mvl61850_rpt_change_scan_done */ /* Used by code that scans for dchg, qchg, dupd changes. */ /* This function is called when the scan is complete. */ /* (i.e. 'mvlu_rd_prim_done' has been called for all "leafs" of all */ /* variables in the report). Everything is ready to build a report, */ /* so build it now. */ /* CRITICAL: when all done, this function must free the temporary */ /* MVL_IND_PEND struct by calling "mvlu_integrity_scan_destroy". */ /************************************************************************/ ST_RET mvl61850_rpt_change_scan_done (MVL_IND_PEND *indCtrl) { ST_RET retCode = SD_SUCCESS; /* nothing to fail here */ /* NOTE: not much to do here. DO NOT send reports. */ /* mvlu_rpt_service chks numTrgs & BufTim & sends when needed. */ /* CRITICAL: free the temporary MVL_IND_PEND struct. */ mvlu_integrity_scan_destroy (indCtrl); return (retCode); } /************************************************************************/ /* mvl61850_rpt_change_scan */ /* Scan all IEC 61850 Report Datasets for dchg, qchg, dupd. */ /************************************************************************/ /*renxiaobao 报告*/ ST_VOID mvl61850_rpt_change_scan (char *dom,char CHG) /*ST_VOID mvl61850_rpt_change_scan (ST_VOID)*/ { MVLU_RPT_CTRL *rptCtrl; MVL_NVLIST_CTRL *dsNvl; ST_INT j; ST_DOUBLE timeNow; short rpt_num=0; /* Reset "already_scanned=SD_FALSE" for each NVL. */ /* Reset it for every NVL, even if not enabled (faster). */ for(rptCtrl = mvl61850_rpt_ctrl_list; rptCtrl!= NULL; rptCtrl = (MVLU_RPT_CTRL *) list_get_next (mvl61850_rpt_ctrl_list, rptCtrl)) { if(dom&&strcmp(rptCtrl->dsNvl->va_scope->dom->name,dom)) continue; dsNvl = rptCtrl->dsNvl; if (dsNvl) /* dsNvl might be NULL */ { dsNvl->already_scanned = SD_FALSE; } rpt_num++; } if(!rpt_num) return; timeNow = sGetMsTime (); /* get time just once. Use it in loop. */ /* Loop through all Report NVLs to scan for data changes. */ /* Set already_scanned=SD_TRUE for each NVL. If same NVL used in another */ /* Report, do NOT rescan it. */ for(rptCtrl = mvl61850_rpt_ctrl_list; rptCtrl!= NULL; rptCtrl = (MVLU_RPT_CTRL *) list_get_next (mvl61850_rpt_ctrl_list, rptCtrl)) { if(dom&&strcmp(rptCtrl->dsNvl->va_scope->dom->name,dom)) continue; /* If scan_rate > 0, check timer. If not, go ahead and scan now. */ /* NOTE: if scan_rate is too small, there may be extra delay between scans.*/ if(CHG) { if (timeNow >= rptCtrl->next_scan_start) rptCtrl->next_scan_start = timeNow + rptCtrl->scan_rate; } else if (rptCtrl->scan_rate > 0) { /* check timer to see if it's time to scan */ if (timeNow < rptCtrl->next_scan_start) continue; /* DO NOT scan now. Skip to next rptCtrl */ else rptCtrl->next_scan_start = timeNow + rptCtrl->scan_rate; } dsNvl = rptCtrl->dsNvl; if (dsNvl!=NULL && dsNvl->already_scanned==SD_FALSE) /* dsNvl might be NULL*/ { /* This NVL not yet scanned. */ /* If URCB enabled or BRCB enabled ONCE, start scan.*/ if( rptCtrl->only_client.basrcb.RptEna||(rptCtrl->rcb_type == RCB_TYPE_IEC_BRCB&& rptCtrl->brcbCtrl.enabled_once)) { /* Initialize "rpt_reason=0" for all variables. May be changed */ /* in leaf functions. Checked in mvl61850_rpt_change_scan_va_done. */ for (j = 0; j < dsNvl->num_of_entries; j++) dsNvl->entries[j]->rpt_reason = 0; /* This is NOT an Integrity scan, but mvlu_integrity_scan_read */ /* does what we need if the right callback functions passed to it. */ /* Scan for dchg, qchg, dupd. */ /* This function "begins" the scan. When the scan is */ /* complete, "mvl61850_rpt_change_scan_done" is called to build the */ /* the report. If ALL "leaf" functions are synchronous, */ /* "mvl61850_rpt_change_scan_done" is called BEFORE this function returns.*/ /* CRITICAL: this function allocates a temporary MVL_IND_PEND */ /* which must be freed by mvl61850_rpt_change_scan_done. */ mvlu_integrity_scan_read (&rptCtrl->only_client, mvl61850_rpt_change_scan_va_done, mvl61850_rpt_change_scan_done); dsNvl->already_scanned = SD_TRUE; /* this NVL scanned. Don't scan again for another RCB*/ } } } } /************************************************************************/ /* mvl61850_get_rcb */ /* Find IEC 61850 RCB (URCB or BRCB). */ /* NOTE: Caller can access MVLU_RPT_CTRL, if needed, by using "rpt_ctrl"*/ /* member of MVLU_RPT_CLIENT. */ /************************************************************************/ MVLU_BASRCB *mvl61850_get_rcb (MVL_VAR_ASSOC *baseVa, RUNTIME_TYPE *rt, MVLU_RPT_CLIENT **rptClientOut) { MVLU_RPT_CTRL *rptCtrl; MVLU_BASRCB *rcb = NULL; /* ptr to be returned */ /* Find the associated Report Control, using this RT as a key */ for (rptCtrl = mvl61850_rpt_ctrl_list; rptCtrl != NULL; rptCtrl = (MVLU_RPT_CTRL *) list_get_next (mvl61850_rpt_ctrl_list, rptCtrl)) { if (rptCtrl->base_va == baseVa && (rt > rptCtrl->rcbRtHead) && (rt <= rptCtrl->rcbRtHead + rptCtrl->rcbRtHead->u.str.num_rt_blks)) { if (rptCtrl->rcb_type != RCB_TYPE_UCA) { rcb = &rptCtrl->only_client.basrcb; if (rptClientOut != NULL) /* set optional ptr */ *rptClientOut = &rptCtrl->only_client; /* always EXACTLY one client*/ } break; /* rptCtrl is set. Just need to return it. */ } } /* rcb will be NULL if rptCtrl NOT found or NOT 61850 */ return (rcb); } /************************************************************************/ /* mvl61850_rpt_dataset_create */ /* Create IEC 61850 Report Control Block resources for a dataset. */ /* Assumes EXACTLY one client per rptCtrl (see rptCtrl->only_client). */ /************************************************************************/ ST_RET mvl61850_rpt_dataset_create (MVLU_RPT_CTRL *rptCtrl, MVL_NVLIST_CTRL *dsNvl) { RUNTIME_TYPE *incRt; ST_RET rc; ST_INT numDsVars; ST_INT changed_flags_size; ST_INT allocSize; if (rptCtrl->dsNvl) { /* dsNvl already set. Must call mvl61850_rpt_dataset_destroy first*/ MVL_LOG_ERR0 ("Report Dataset already exists. Must destroy it before creating new one."); return (SD_FAILURE); } if (dsNvl == NULL) return (SD_SUCCESS); /* this is allowed, but no need to do anything*/ numDsVars = dsNvl->num_of_entries; /* NOTE: RptID (in MVLU_BASRCB struct) might be empty (0 length string).*/ /* If so, a string will be constructed each time a report is built */ /* (see "mvlu_mk_rptid" function). */ /* Alloc the entries table; allow for RptID, OptFlds, SqNum, RptTim, */ /* DatSet, BufOvfl, EntryID, ConfRev, */ /* SubSeqNum, MoreSegmentsFollow, & InclusionBitstring plus ARRAYS of */ /* dataRefs, values, and Reasons. */ /* "assert" later if "maxNumRptVars" exceeded. */ /* Allow (3 * numDsVars) to handle arrays of dataRef, value, and reason.*/ rptCtrl->maxNumRptVars = MVLU_MAX_RPT_OPTS + (3 * numDsVars); rptCtrl->rptNvl.entries = (MVL_VAR_ASSOC **) M_CALLOC (MSMEM_GEN, rptCtrl->maxNumRptVars, sizeof (MVL_VAR_ASSOC *)); /* NOTE: The "rptNvl->entries" array is not filled in and * and "rptNvl->num_of_entries" is not set * until a Report is being built. */ /* We need to create a Runtime Type for the inclusion bitstring */ incRt = &rptCtrl->incRt; incRt->el_tag = RT_BIT_STRING; incRt->el_size = BSTR_NUMBITS_TO_NUMBYTES(numDsVars); incRt->offset_to_last = incRt->el_size; /*renxiaobao 5.3版本修改*/ /*incRt->u.p.el_len = (ST_RTINT) numDsVars;*/ incRt->u.p.el_len = numDsVars; rc = mvlu_add_rt_type (&rptCtrl->incRt, 1, &rptCtrl->inclusion_typeid); if (rc != SD_SUCCESS) { MVL_LOG_NERR0 ("Error - could not add temp RT type"); return (SD_FAILURE); } /* That's all we can do ahead of time for the RPT variable associations */ /* Set a bit of the data for the BASRCB */ if (dsNvl->nvl_scope.scope == AA_SPEC) sprintf (rptCtrl->only_client.basrcb.DatSetNa, "@%s", dsNvl->name); else if (dsNvl->nvl_scope.scope == VMD_SPEC) sprintf (rptCtrl->only_client.basrcb.DatSetNa, "/%s", dsNvl->name); else sprintf (rptCtrl->only_client.basrcb.DatSetNa, "%s/%s", dsNvl->nvl_scope.dom->name, dsNvl->name); /* Alloc one buf for reasons_data, changed_flags, segmented_inclusion.*/ /* Free this buf in mvl61850_rpt_dataset_destroy. */ changed_flags_size = BSTR_NUMBITS_TO_NUMBYTES(numDsVars); allocSize = (numDsVars * sizeof (ST_UINT8)) + /* reasons_data */ (2 * changed_flags_size); /* changed_flags & segmented_inclusion*/ rptCtrl->only_client.reasons_data = chk_calloc (1, allocSize); rptCtrl->only_client.changed_flags = rptCtrl->only_client.reasons_data + numDsVars; rptCtrl->only_client.segmented_inclusion = rptCtrl->only_client.changed_flags + changed_flags_size; rptCtrl->dsNvl = dsNvl; /* now save new dataset */ return (SD_SUCCESS); } /************************************************************************/ /* mvl61850_rpt_dataset_destroy */ /* Destroy resources created by mvl61850_rpt_dataset_create. */ /************************************************************************/ ST_VOID mvl61850_rpt_dataset_destroy (MVLU_RPT_CTRL *rptCtrl) { if (rptCtrl->dsNvl != NULL) { /* if dsNvl is already NULL, do nothing */ chk_free (rptCtrl->only_client.reasons_data); rptCtrl->only_client.basrcb.DatSetNa[0] = '\0'; /* clear DatSet */ rptCtrl->maxNumRptVars = 0; M_FREE (MSMEM_GEN, rptCtrl->rptNvl.entries); /* Free the Runtime Type for the inclusion bitstring */ mvlu_free_rt_type (rptCtrl->inclusion_typeid); rptCtrl->dsNvl = NULL; } return; } /************************************************************************/ /* mvl61850_create_rpt_ctrl */ /* Create IEC 61850 Report Control Block. */ /* Similar to UCA function "mvlu_create_rpt_ctrl", but just for 61850. */ /* Only one client per RCB (see "only_client" param). */ /* The "dsNvl" arg may be NULL. If so, the RCB may not be enabled until */ /* it is set. */ /* NOTE: Does not support UCA Reports (see assert checking "rcb_type"). */ /************************************************************************/ MVLU_RPT_CTRL *mvl61850_create_rpt_ctrl (ST_CHAR *basrcbName, MVL_NVLIST_CTRL *dsNvl, /* may be NULL */ MVL_VAR_ASSOC *base_va, ST_INT rcb_type, ST_INT buftim_action, /* MVLU_RPT_BUFTIM_* */ ST_INT brcb_bufsize, /* for BRCB only */ ST_UINT32 ConfRev) /* for BRCB/URCB only */ { MVLU_RPT_CTRL *rptCtrl; ST_INT rptCtrlSize; ST_INT numRt; ST_RET status; /* SD_SUCCESS if everything worked */ assert (rcb_type != RCB_TYPE_UCA); /* DO NOT use this funct for UCA rpts*/ /* If any rpt_ctrl created, disconnect function ptr must be set. */ _mvlu_rpt_disconnect_rcvd_fun = _mvlu_rpt_disconnect_rcvd; /* Allocate MVLU_RPT_CTRL struct. Allow extra space after struct to */ /* store basrcbName. */ rptCtrlSize = sizeof (MVLU_RPT_CTRL) + strlen (basrcbName) + 1; rptCtrl = (MVLU_RPT_CTRL *) M_CALLOC (MSMEM_GEN, 1, rptCtrlSize); rptCtrl->basrcb_name = (ST_CHAR *) (rptCtrl + 1); /*point after struct*/ strcpy (rptCtrl->basrcb_name, basrcbName); /* Initialize structure members. */ rptCtrl->base_va = base_va; rptCtrl->rcb_type = rcb_type; rptCtrl->buftim_action = buftim_action; rptCtrl->brcbCtrl.brcb_bufsize = brcb_bufsize; /* ONLY for IEC BRCB*/ rptCtrl->rptNvl.name = "RPT"; /* Report NVL name ALWAYS = "RPT"*/ rptCtrl->only_client.basrcb.ConfRev = ConfRev; rptCtrl->only_client.basrcb.OptFlds.len_1 = 10; /* bitstring len*/ rptCtrl->only_client.basrcb.TrgOps.len = 6; /* bitstring len*/ rptCtrl->only_client.rpt_ctrl = rptCtrl; /* client struct needs ptr back to rptCtrl*/ /* For BRCB, set the initial "EntryID" value. */ /* Put current time in first 4 bytes, 0 in last 4 bytes. */ if (rcb_type == RCB_TYPE_IEC_BRCB) { ST_INT32 tmp[2]; tmp [0] = (ST_INT32) time (NULL); /*renxiaobao 报告*/ tmp [0] = 0; tmp [1] = 0; mvl61850_brcb_entryid_init (rptCtrl, (ST_UINT8 *) tmp); } /* The following initializations could fail, so we set & check "status".*/ /* Find the runtime type of the RCB and save it in rptCtrl->rcbRtHead.*/ /* Used by leaf functions to find the correct rptCtrl. */ /* If successful, this function sets "rptCtrl->rcbRtHead" & "numRt". */ status = mvlu_rpt_rcb_type_find (base_va->type_id, basrcbName, &rptCtrl->rcbRtHead, &numRt); if (status == SD_SUCCESS) { if (mvlu_rpt_find_typeids (&rptCtrl->rpt_typeids)) { MVL_LOG_ERR0 ("Cannot find Report Control Block types"); status = SD_FAILURE; } } /* mvl61850_rpt_dataset_create initializes everything related to dsNvl. */ if (status == SD_SUCCESS) status = mvl61850_rpt_dataset_create (rptCtrl, dsNvl); if (status == SD_SUCCESS) { #if 1 /* IEC 61850-7-2 Ed2 says to start buffering on power-up if */ /* DatSet attribute references an existing DataSet. */ if (dsNvl != NULL && rptCtrl->rcb_type == RCB_TYPE_IEC_BRCB) rptCtrl->brcbCtrl.enabled_once = SD_TRUE; /* starts buffering */ #endif list_add_last((ST_VOID **) &mvl61850_rpt_ctrl_list, (ST_VOID *) rptCtrl); } else { M_FREE (MSMEM_GEN, rptCtrl); /* clean up */ rptCtrl = NULL; /* set error return value */ } return (rptCtrl); } /************************************************************************/ /* mvl61850_free_rpt_ctrl */ /* Free a MVLU_RPT_CTRL allocated by 'mvl61850_create_rpt_ctrl'. */ /************************************************************************/ ST_VOID mvl61850_free_rpt_ctrl (MVLU_RPT_CTRL *rptCtrl) { assert (rptCtrl->rcb_type != RCB_TYPE_UCA); /* Take it off the report control list */ list_unlink ((ST_VOID **) &mvl61850_rpt_ctrl_list, (ST_VOID *) rptCtrl); mvl61850_rpt_dataset_destroy (rptCtrl); /* BRCB may contain large lists of reports sent or queued. */ if (rptCtrl->rcb_type == RCB_TYPE_IEC_BRCB) mvl61850_brcb_rpt_lists_clean (&rptCtrl->brcbCtrl); M_FREE (MSMEM_GEN, rptCtrl); } /************************************************************************/ /* mvl61850_brcb_client_service */ /* Check the BRCB state and perform appropriate actions. */ /************************************************************************/ ST_VOID mvl61850_brcb_client_service (MVLU_RPT_CTRL *rptCtrl, MVLU_RPT_CLIENT *rptClient, ST_DOUBLE timeNow) { BUFLIST_ENTRY *entry; ST_BOOLEAN all_seg_done; /* SD_TRUE if done sending all segments */ ST_RET retCode; /* Check BRBC state and, if necessary, encode AND queue up report. */ mvl61850_rcb_chk_state (rptCtrl, rptClient, timeNow); /* Now, examine BRCB state and send report if necessary. * If connected, reports are enabled, reports are queued, AND stack buffers * are available, send a report. */ if (rptClient->netInfo && /* connected */ rptClient->basrcb.RptEna && /* rpts enabled */ rptCtrl->brcbCtrl.rpt_list_next) /* rpts queued & not all sent*/ { if (mvl_ureq_bufs_avail (rptClient->netInfo) > 0) /* stack bufs avail to send rpt */ { /* Encode and send next buffered report. */ entry = rptCtrl->brcbCtrl.rpt_list_next; /* this is next one to send*/ retCode = mvl61850_brcb_rpt_send (rptCtrl, rptClient, entry, &all_seg_done); if (retCode != SD_SUCCESS) MVL_LOG_ERR1 ("Sending IEC-61850 Buffered Report failed: err=0x%X", retCode); /* If error OR done sending all segments of this rpt, */ /* point to next rpt on list (NULL if all sent) & update SqNum */ if (retCode != SD_SUCCESS || all_seg_done) /* done sending all segments of this report. */ { /* point to next rpt on the list (NULL if all sent) */ rptCtrl->brcbCtrl.rpt_list_next = list_get_next (rptCtrl->brcbCtrl.rpt_list, entry); memcpy (rptCtrl->brcbCtrl.lastSentEntryID, entry->EntryID, 8); /* save last sent EntryID*/ memcpy (&rptCtrl->brcbCtrl.lastSentTimeOfEntry, &entry->TimeOfEntry, sizeof (MMS_BTIME6)); print_queues (&rptCtrl->brcbCtrl); /* just for debug */ /* Increment seq number for next rpt. */ rptClient->basrcb.SqNumInt16u++; /* used only for BRCB */ /* Only one report should be sent with BufOvfl TRUE; clear it now.*/ rptCtrl->brcbCtrl.BufOvfl = SD_FALSE; } } else print_msg0 ("STACK BUFFERS NOT AVAILABLE TO SEND REPORT\n"); } return; } /************************************************************************/ /* mvl61850_urcb_client_service */ /* Check the URCB state and perform appropriate actions. */ /************************************************************************/ int urcb_netInfo_pri=0; ST_VOID mvl61850_urcb_client_service (MVLU_RPT_CTRL *rptCtrl, MVLU_RPT_CLIENT *rptClient, ST_DOUBLE timeNow) { /* Must be connected to send Unbuffered reports. */ if (rptClient->netInfo!=NULL) { /* if(urcb_netInfo_pri) printf("rptClient->netInfo=%d\r\n ",rptClient->netInfo);*/ /* If no buffers are available to send unbuffered reports, don't waste time * checking timers, scanning, etc. */ if (mvl_ureq_bufs_avail (rptClient->netInfo) <= 0) /* stack bufs avail to send rpt */ print_msg0 ("STACK BUFFERS NOT AVAILABLE TO SEND REPORT\n"); else /* Check URBC state and, if necessary, encode AND send report. */ mvl61850_rcb_chk_state (rptCtrl, rptClient, timeNow); } return; } /************************************************************************/ /* mvl61850_rpt_service */ /* Perform servicing for all IEC 61850 Report Control Blocks. */ /* Scanning for changes (dchg, qchg, etc) is automatic */ /* (see mvl61850_rpt_change_scan). */ /************************************************************************/ ST_VOID mvl61850_rpt_service (char *dom,char CHG) { MVLU_RPT_CTRL *rptCtrl; ST_DOUBLE timeNow; /* Scan all report data for changes (dchg, qchg, dupd). */ mvl61850_rpt_change_scan (dom,CHG); /* See if it is time to send reports */ timeNow = sGetMsTime (); for (rptCtrl = mvl61850_rpt_ctrl_list; rptCtrl != NULL; rptCtrl = (MVLU_RPT_CTRL *) list_get_next (mvl61850_rpt_ctrl_list, rptCtrl)) { if(dom&&strcmp(rptCtrl->dsNvl->va_scope->dom->name,dom)) continue; /* NOTE: if DatSet is NULL, don't need to do anything for this rptCtrl.*/ if (rptCtrl->dsNvl==NULL) continue; /* Only one client (rptCtrl->only_client). */ if (rptCtrl->rcb_type == RCB_TYPE_IEC_BRCB) mvl61850_brcb_client_service (rptCtrl, &rptCtrl->only_client, timeNow); else { /*assert (rptCtrl->rcb_type == RCB_TYPE_IEC_URCB);*/ /* must be IEC_URCB*/ mvl61850_urcb_client_service (rptCtrl, &rptCtrl->only_client, timeNow); } } } /************************************************************************/ /* mvl61850_rpt_ctrl_destroy_all */ /* Destroy all 61850 report controls. */ /************************************************************************/ ST_VOID mvl61850_rpt_ctrl_destroy_all () { MVLU_RPT_CTRL *rptCtrl; /* Use list_find_last to avoid calling list_unlink twice. */ while ((rptCtrl = (MVLU_RPT_CTRL *) list_find_last ((DBL_LNK *)mvl61850_rpt_ctrl_list)) != NULL) { mvl61850_free_rpt_ctrl (rptCtrl); /* this also unlinks it from list*/ } } /************************************************************************/ /* mvl61850_integrity_timeout */ /* NOTE: For IEC 61850 only. For UCA, use mvlu_integrity_timeout. */ /* If Integrity period timeout occurred, send Integrity Report. */ /* NOTE: data scan already done at start of mvl61850_rpt_service. */ /* No need to rescan data. Just send the data from the data scan. */ /* RETURNS: SD_TRUE if IntgPd is set and timeout occurred */ /* SD_FALSE otherwise */ /************************************************************************/ ST_RET mvl61850_integrity_timeout (MVLU_RPT_CLIENT *rptClient, ST_DOUBLE timeNow) { ST_RET retVal; ST_INT j; ST_RET readyRet; /* return code from mvlu_rpt_ready */ if (rptClient->basrcb.IntgPd != 0 && BSTR_BIT_GET (rptClient->basrcb.TrgOps.data, TRGOPS_BITNUM_INTEGRITY) /* enabled*/ && timeNow > rptClient->next_integ_rpt_time) /* timeout occurred*/ { MVLU_LOG_FLOW1 ("IEC 61850 Report Integrity Period timeout for client %08lx",rptClient); /* Send last buffered entry (due to BufTm buffering) BEFORE this integrity report.*/ if (rptClient->numTrgs > 0) { /* send or queue report */ mvlu_rpt_ready (rptClient, MVLU_RPT_TYPE_RBE); } /* Set "reason" for each variable of Dataset now. */ for (j = 0; j < rptClient->rpt_ctrl->dsNvl->num_of_entries; j++) rptClient->reasons_data[j] = MVLU_TRGOPS_INTEGRITY; /* send or queue rpt */ readyRet = mvlu_rpt_ready (rptClient, MVLU_RPT_TYPE_INTEGRITY_OR_GI); if (readyRet != SD_SUCCESS) MVLU_LOG_FLOW1 ("IEC 61850 Integrity report could not be sent or queued: err=0x%X", readyRet); /* set time for next integrity report */ rptClient->next_integ_rpt_time = sGetMsTime () + (ST_DOUBLE) rptClient->basrcb.IntgPd; retVal = SD_TRUE; } else retVal = SD_FALSE; return (retVal); }