Files
microser/mmslib/mvlu/mvl61850_rpt.c
2026-06-15 15:48:16 +08:00

1368 lines
54 KiB
C
Raw Blame History

/************************************************************************/
/* 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 <20><>־
//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 <20><><EFBFBD><EFBFBD>*/
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<EFBFBD><EFBFBD>޸<EFBFBD>*/
/*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 <20><><EFBFBD><EFBFBD>*/
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);
}