1178 lines
43 KiB
C
1178 lines
43 KiB
C
/************************************************************************/
|
||
/* SISCO SOFTWARE MODULE HEADER *****************************************/
|
||
/************************************************************************/
|
||
/* (c) Copyright Systems Integration Specialists Company, Inc., */
|
||
/* 2002-2006 All Rights Reserved */
|
||
/* */
|
||
/* MODULE NAME : mvl61850.c */
|
||
/* PRODUCT(S) : MMSEASE-LITE */
|
||
/* */
|
||
/* MODULE DESCRIPTION : */
|
||
/* Code to support IEC-61850 Reporting as a Server. */
|
||
/* */
|
||
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE : */
|
||
/* mvl61850_rcb_chk_state */
|
||
/* mvl61850_urcb_rpt_send */
|
||
/* */
|
||
/* MODIFICATION LOG : */
|
||
/* Date Who Rev Comments */
|
||
/* -------- --- ------ ------------------------------------------- */
|
||
/* 08/05/08 JRB 31 Increment ConfRev if DatSet is written. */
|
||
/* 07/22/08 JRB 30 Add mvl61850_objref_create. */
|
||
/* 06/30/08 JRB 29 For Ed2, return different EntryID/TimeOfEntry*/
|
||
/* values if RptEna=FALSE or TRUE. */
|
||
/* 05/15/08 JRB 28 Use NEW mvl61850_integrity_timeout. */
|
||
/* 05/06/08 JRB 27 Fix memory leak after "PDU size" error. */
|
||
/* 04/15/08 JRB 26 Don't call mvlu_rpt_va_change for GI */
|
||
/* (save "reason" later when scan completes). */
|
||
/* 02/29/08 JRB 25 Allow VMD_SPEC DatSet for URCB or BRCB. */
|
||
/* Allow AA_SPEC DatSet for URCB, but ONLY if */
|
||
/* RCB is reserved (Resv=TRUE). */
|
||
/* Set changed_flags for Integrity BEFORE loop. */
|
||
/* chk_seg_needed: allow longer (129) RptID, etc*/
|
||
/* 03/16/07 JRB 24 mvlu_gi_wr_ind: Chk that this client reserved URCB.*/
|
||
/* 03/07/07 JRB 23 Use mvlu_get_leaf_val_int_any for Beh$stVal. */
|
||
/* 02/28/07 JRB 22 mvlu_gi_wr_ind: allow write only if RCB is */
|
||
/* enabled; if TrgOps.GI=FALSE, allow write but */
|
||
/* DO NOT generate a report. */
|
||
/* 02/13/07 JRB 21 mvl61850_get_rcb: del unused net_info arg. */
|
||
/* Use new _rcb_writable. */
|
||
/* 12/20/06 JRB 20 PurgeBuf only if DatSet val changed (tissue 322)*/
|
||
/* Allow Beh$stVal to be INT8 (Enum) or INT32 */
|
||
/* (for Tissues 120, 146, 171, 234). */
|
||
/* 10/30/06 JRB 19 Use new mvl_vmd_* object handling functions. */
|
||
/* 08/09/06 JRB 18 Moved most 61850 Rpt code to mvl61850_rpt.c */
|
||
/* Chg mvl61850_rcb_build to mvl61850_urcb_rpt_send*/
|
||
/* & use only for URCB (new functs handle BRCB).*/
|
||
/* Use new mvlu_rpt_ready, mvl61850_mk_dataref, */
|
||
/* mvl61850_rcb_cleanup. */
|
||
/* Use new mvl61850_mk_rptid to generate RptID */
|
||
/* in RPT if RptID is NULL in RCB. */
|
||
/* Allow GI write only if TrgOp GI bit=1. */
|
||
/* Del mvl_enc_va_data, use new ms_local_to_asn1_2.*/
|
||
/* Add mvl61850_datset_wr_ind to allow DatSet write.*/
|
||
/* Store EntryID everywhere as Ostring8. */
|
||
/* Fix type for all ObjectReference (Vstring129).*/
|
||
/* Use mvl61850_get_rcb to find the RCB. */
|
||
/* 07/11/05 JRB 17 BUG FIX: Set cur_bufsize=0 after PurgeBuf write.*/
|
||
/* Set BufOvfl=SD_FALSE after PurgeBuf write */
|
||
/* (not clear in 61850, but passes KEMA test). */
|
||
/* 07/08/05 JRB 16 For URCB, ignore BUFOVFL, ENTRYID bits in */
|
||
/* OptFlds to conform to 61850-8-1. */
|
||
/* 06/27/05 JRB 15 mvlu_entryid_rd_ind: return lastSentEntryID. */
|
||
/* Old code returned "last queued" EntryID. */
|
||
/* 06/27/05 JRB 14 _mvlu_get_rd_rcb: add (MVLU_RPT_CLIENT **) arg*/
|
||
/* 05/16/05 JRB 13 Set failure=ARE_TEMP_UNAVAIL when trying to */
|
||
/* write RCB elements while RptEna=TRUE. */
|
||
/* Del mvlu_sqnum_int16u_wr.. (not writable). */
|
||
/* 05/04/05 JRB 12 Call u_mvlu_rpt_time_get just once BEFORE */
|
||
/* main loop in ..rcb_build so that each */
|
||
/* segmented report has the same time stamp. */
|
||
/* 01/19/05 JRB 11 Add mvl61850_beh_stval_rd_ind. */
|
||
/* 08/20/04 JRB 10 Increment EntryID before encoding rpt, */
|
||
/* so EntryID encoded in rpt matches EntryID */
|
||
/* stored in BUFLIST_ENTRY struct. */
|
||
/* 08/06/04 EJV 09 mvlu_entryid_wr_ind: add typecast. */
|
||
/* 06/30/04 JRB 08 Add mvlu_sqnum_int16u_rd(wr)_ind_fun & use */
|
||
/* SqNumInt16u instead of SqNum for BRCB. */
|
||
/* Chg RptID type to vstring65. */
|
||
/* Del SCL parsing functions: */
|
||
/* mvl61850_ln_create_start, mvl61850_do_create,*/
|
||
/* mvl61850_da_create, mvl61850_ln_create_finish,*/
|
||
/* & mvl61850_ln_destroy. New SCL standard */
|
||
/* makes these obsolete. */
|
||
/* 03/18/04 JRB 07 Fix dataref in reports by adding scope. */
|
||
/* 12/17/03 JRB 06 61850-8-1 FDIS changes: */
|
||
/* Add ConfRev to rpt if enabled by OptFlds. */
|
||
/* Move SubSeqNum, MoreSegmentsFollow to just */
|
||
/* before inclusion bitstring. */
|
||
/* Save TimeofEntry in "basrcb". */
|
||
/* Add mvlu_timeofentry_rd_ind. */
|
||
/* 07/09/03 JRB 05 Del assert in mvlu_gi_scan_done so GI works */
|
||
/* for URCB too. */
|
||
/* 04/14/03 JRB 04 Eliminate compiler warnings. */
|
||
/* 04/04/03 JRB 03 Fix integrity/GI scan code so multiple */
|
||
/* concurrent scans don't corrupt one another. */
|
||
/* 03/13/03 JRB 02 Add URCB support: rename/rearrange some things*/
|
||
/* Add code for 61850 dynamic type creation. */
|
||
/* Trigger Intg Rpt ONLY by setting IntgPd!=0. */
|
||
/* Trigger GI ONLY by writing GI!=0. */
|
||
/* Move bstrcpy, bvstrcpy to "asn1r.c". */
|
||
/* Fix report segmenting (see chk_seg_needed). */
|
||
/* 12/15/02 JRB 01 Created */
|
||
/************************************************************************/
|
||
#include "glbtypes.h"
|
||
#include "sysincs.h"
|
||
#include "mvl_uca.h"
|
||
#include "mvl_log.h"
|
||
#include "mvl_acse.h" /* need mvl_cfg_info */
|
||
|
||
/************************************************************************/
|
||
/* 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
|
||
|
||
static ST_RET fill_asn1_len_array (MVLU_RPT_CLIENT *rptClient,
|
||
MVL_NVLIST_CTRL *dsNvl,
|
||
ST_INT *asn1_len_array);
|
||
|
||
/******
|
||
* mvl61850_rcb_chk_state
|
||
* Check RCB state and build/send report if necessary. This function works for
|
||
* BRCB or URCB.
|
||
*/
|
||
ST_VOID mvl61850_rcb_chk_state (MVLU_RPT_CTRL *rptCtrl,
|
||
MVLU_RPT_CLIENT *rptClient,
|
||
ST_DOUBLE timeNow)
|
||
{
|
||
/* If URCB enabled or BRCB enabled ONCE, chk Integrity period & triggers.*/
|
||
if (rptClient->basrcb.RptEna
|
||
|| (rptClient->rpt_ctrl->rcb_type == RCB_TYPE_IEC_BRCB
|
||
&& rptClient->rpt_ctrl->brcbCtrl.enabled_once))
|
||
{
|
||
/* Chk for Integrity Period timeout. */
|
||
/* NOTE: use mvl61850_integrity_timeout (mvlu_integrity_timeout is */
|
||
/* only for UCA). */
|
||
mvl61850_integrity_timeout (rptClient, timeNow);
|
||
|
||
/* Chk for other triggers. */
|
||
/* NOTE: numTrgs ONLY incremented if changes matched the TrgOps. */
|
||
if (rptClient->numTrgs > 0)
|
||
{
|
||
/* If BufTim disabled (=0) OR BufTim expired, send report now. */
|
||
if (rptClient->basrcb.BufTim == 0 || timeNow > rptClient->buf_time_done)
|
||
{
|
||
MVLU_LOG_FLOW1 ("Non-Integrity report for client %08lx", rptClient);
|
||
mvlu_rpt_ready (rptClient, MVLU_RPT_TYPE_RBE); /* send or queue rpt*/
|
||
}
|
||
}
|
||
} /* end if rpt enabled at least once */
|
||
return;
|
||
}
|
||
|
||
/************************************************************************/
|
||
/* mvlu_gi_scan_va_done */
|
||
/* 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).*/
|
||
/************************************************************************/
|
||
|
||
ST_VOID mvlu_gi_scan_va_done (MVL_IND_PEND *indCtrl,
|
||
MVL_VAR_ASSOC *va)
|
||
{
|
||
ST_INT data_size;
|
||
/* CRITICAL: DO NOT call mvlu_rpt_va_change. rptClient->reasons_data */
|
||
/* will be set in mvlu_gi_scan_done when scan completes. */
|
||
data_size = mvl_type_ctrl[va->type_id].data_size;
|
||
memcpy (va->last_data, va->data, data_size);
|
||
}
|
||
|
||
/************************************************************************/
|
||
/* mvlu_gi_scan_done */
|
||
/* Same as "_mvlu_rpt_scan_done" except arg is (MVL_IND_PEND *) */
|
||
/* instead of (MVLU_RPT_SCAN_CTRL *). */
|
||
/* */
|
||
/* This function is called when the gi 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. */
|
||
/************************************************************************/
|
||
|
||
ST_RET mvlu_gi_scan_done (MVL_IND_PEND *indCtrl)
|
||
{
|
||
ST_RET retCode;
|
||
MVLU_RPT_CLIENT *rptClient;
|
||
ST_INT j;
|
||
|
||
/* Get "rptClient", saved in "indCtrl" when scan initialized. */
|
||
rptClient = (MVLU_RPT_CLIENT *) indCtrl->usr_ind_ctrl;
|
||
|
||
/* 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_GI;
|
||
|
||
/* Assume this is a BRCB or URCB. Should never get here otherwise. */
|
||
|
||
/* Build the report. */
|
||
retCode = mvlu_rpt_ready (rptClient, MVLU_RPT_TYPE_INTEGRITY_OR_GI);
|
||
|
||
if (retCode != SD_SUCCESS)
|
||
MVLU_LOG_FLOW1 ("GI report send failed: err=0x%X", retCode);
|
||
|
||
mvlu_integrity_scan_destroy (indCtrl); /* destroy temporary struct*/
|
||
return (retCode);
|
||
}
|
||
|
||
/************************************************************************/
|
||
/* mvl61850_urcb_rpt_send */
|
||
/* Set up all info needed to build an Information Report for a URCB, */
|
||
/* then call "mvl_info_variables" to encode/send it. */
|
||
/************************************************************************/
|
||
ST_RET mvl61850_urcb_rpt_send (MVLU_RPT_CTRL *rptCtrl,
|
||
MVLU_RPT_CLIENT *rptClient,
|
||
ST_INT rpt_type)
|
||
{
|
||
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 cur_va_index, next_va_index; /* indices into dsNvl va array */
|
||
ST_BOOLEAN segNeeded; /* If TRUE, segmenting needed */
|
||
ST_UINT16 SubSeqNum = 0;
|
||
ST_BOOLEAN MoreSegmentsFollow;
|
||
ST_CHAR *tmp_dataref_buf;
|
||
ST_CHAR tmpRptID [MVL61850_MAX_RPTID_LEN+1];
|
||
ST_INT *asn1_len_array; /* array of ASN.1 lengths, one for each var (allocated)*/
|
||
|
||
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 URCB Report, MVL_NET_INFO %08lx", rptClient->netInfo);
|
||
|
||
/* Need tmp va's for options, array of data_refs, array of reasons_data.*/
|
||
tmp_va_arr_size = MVLU_MAX_RPT_OPTS + (dsNvl->num_of_entries * 2);
|
||
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);
|
||
|
||
asn1_len_array = M_CALLOC (MSMEM_GEN, rptCtrl->dsNvl->num_of_entries, sizeof(ST_INT));
|
||
|
||
assert (rptCtrl->dsNvl->num_of_entries); /* must be >0 entries */
|
||
|
||
/* Call user function to get report time. Called here before loop */
|
||
/* so that all segmented reports get the same time stamp. */
|
||
u_mvlu_rpt_time_get (&basrcb->TimeofEntry);
|
||
|
||
/* Fill in "asn1_len_array", needed later by "chk_seg_needed". */
|
||
if (fill_asn1_len_array (rptClient, rptCtrl->dsNvl, asn1_len_array)!=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 Unbuffered Report.");
|
||
}
|
||
|
||
/* If this is Integrity (or GI) report, just set all inclusion bits.*/
|
||
if (rpt_type == MVLU_RPT_TYPE_INTEGRITY_OR_GI)
|
||
memset (rptClient->changed_flags, 0xff, incSize);
|
||
|
||
for (cur_va_index = 0, next_va_index = 0;
|
||
next_va_index < rptCtrl->dsNvl->num_of_entries;
|
||
cur_va_index = next_va_index, SubSeqNum++)
|
||
{ /* BEGIN MAIN LOOP */
|
||
tmp_va = tmp_va_arr; /* start loop pointing to first entry */
|
||
sendIndex = 0; /* start with first rptNvl->entries. */
|
||
|
||
/* Prepares a UCA Report NVL to be sent, based on the */
|
||
/* options, data, and inclusion bitstring in the rptCtrl. After this*/
|
||
/* function completes, the rptCtrl->rptNvl is ready to be sent. */
|
||
|
||
/* CRITICAL: asn1_len_array must be filled in before call to chk_seg_needed.*/
|
||
segNeeded = chk_seg_needed (rptClient, asn1_len_array, cur_va_index, &next_va_index);
|
||
|
||
|
||
/*
|
||
printf("urcb:segNeeded=%d num=%d va_index = %d->%d\r\n",
|
||
segNeeded,rptCtrl->dsNvl->num_of_entries,cur_va_index, next_va_index);
|
||
*/
|
||
|
||
if (next_va_index - 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. */
|
||
|
||
/* Add RptID to Report. */
|
||
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*/
|
||
|
||
/* Add OptFlds to Report. */
|
||
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. */
|
||
|
||
/* The following optional vars are controlled by the same OptFlds bits,
|
||
* are of the same type, and are in the same order as in UCA, so that
|
||
* UCA clients can decode reports if only these options are used.
|
||
*/
|
||
|
||
if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_SQNUM))
|
||
{
|
||
/* NOTE: 61850-7-2 says SqNum in Report is INT16U, but we encode
|
||
* INT8U from URCB. ASN.1 encodes same either way, so client
|
||
* may decode as INT16U if needed.
|
||
*/
|
||
tmp_va->type_id = rptCtrl->rpt_typeids.int8u;
|
||
tmp_va->data = &basrcb->SqNum;
|
||
rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/
|
||
MVLU_LOG_CFLOW1 (" SqNum : %u", (unsigned) basrcb->SqNum);
|
||
}
|
||
if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_TIMESTAMP))
|
||
{
|
||
MVLU_LOG_CFLOW2 (" RptTim : %lums, %lu days",
|
||
basrcb->TimeofEntry.ms, basrcb->TimeofEntry.day);
|
||
|
||
tmp_va->type_id = rptCtrl->rpt_typeids.btime6;
|
||
tmp_va->data = &basrcb->TimeofEntry;
|
||
rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/
|
||
}
|
||
if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_DATSETNAME))
|
||
{
|
||
/* UCA called it OutDat instead of DataSetName. Value is same. */
|
||
MVLU_LOG_CFLOW1 (" DataSetName : %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*/
|
||
}
|
||
|
||
/* The following optional vars are NOT supported by UCA. The OptFlds bits
|
||
* must never be set by UCA clients, and the corresponding data
|
||
* must NOT be sent to UCA clients.
|
||
*/
|
||
|
||
/* For URCB, ignore BUFOVFL bit in OptFlds. */
|
||
/* For URCB, ignore ENTRYID bit in OptFlds. */
|
||
|
||
/* 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)
|
||
{
|
||
/* 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 = &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);
|
||
}
|
||
|
||
/* Add inclusion bitstring. */
|
||
tmp_va->type_id = rptCtrl->inclusion_typeid;
|
||
if (segNeeded)
|
||
{
|
||
/* just set the bits included in this segment. */
|
||
memset (rptClient->segmented_inclusion, 0, incSize); /* start clean */
|
||
for (j = cur_va_index; j < next_va_index; ++j)
|
||
{
|
||
if (BSTR_BIT_GET (rptClient->changed_flags, j))
|
||
BSTR_BIT_SET_ON (rptClient->segmented_inclusion, j);
|
||
}
|
||
tmp_va->data = rptClient->segmented_inclusion;
|
||
}
|
||
else
|
||
tmp_va->data = rptClient->changed_flags;
|
||
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 = cur_va_index; j < next_va_index; ++j)
|
||
{
|
||
if (BSTR_BIT_GET (rptClient->changed_flags, j))
|
||
{
|
||
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 = cur_va_index; j < next_va_index; ++j)
|
||
{
|
||
if (BSTR_BIT_GET (rptClient->changed_flags, j))
|
||
{
|
||
va = dsNvl->entries[j];
|
||
MVLU_LOG_CFLOW3 (" Including variable %d ('%s'), reason=0x%02X",
|
||
j, va->name, rptClient->reasons_data[j]);
|
||
rptNvl->entries[sendIndex++] = va;
|
||
/* va->data already points to latest data. No need to copy data.*/
|
||
}
|
||
}
|
||
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 = cur_va_index; j < next_va_index; ++j)
|
||
{
|
||
if (BSTR_BIT_GET (rptClient->changed_flags, j))
|
||
{
|
||
/* Don't need BVSTR here, because size is fixed. */
|
||
tmp_va->type_id = rptCtrl->rpt_typeids.bstr6;
|
||
tmp_va->data = &rptClient->reasons_data[j];
|
||
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, /* URCB: encode & send*/
|
||
&rptClient->rpt_ctrl->rptNvl,
|
||
SD_FALSE); /* FALSE means "send as NVL" */
|
||
/* If send fails, just log it. Try to send other segments anyway.*/
|
||
if (retCode)
|
||
MVL_LOG_ERR1 ("Sending IEC-61850 Unbuffered Report failed: err=0x%X", retCode);
|
||
} /* END MAIN LOOP */
|
||
|
||
/* Increment seq number for next rpt. */
|
||
basrcb->SqNum++; /* used only for URCB */
|
||
|
||
/* Reset reasons, etc. to prepare for new report triggers. */
|
||
mvl61850_rcb_cleanup (rptClient); /* prepare for next RPT. */
|
||
if (retCode != SD_SUCCESS)
|
||
MVLU_LOG_FLOW1 ("Report Build failed: err=0x%X", retCode);
|
||
M_FREE (MSMEM_GEN, tmp_va_arr);
|
||
M_FREE (MSMEM_GEN, tmp_dataref_buf);
|
||
M_FREE (MSMEM_GEN, asn1_len_array);
|
||
return (retCode);
|
||
}
|
||
|
||
/******
|
||
* chk_seg_needed
|
||
* Check if segmenting is needed.
|
||
* Set (*next_va_index) = first va to send in "next" report.
|
||
* RETURN: SD_FALSE if segmenting NOT needed.
|
||
* SD_TRUE if segmenting IS needed.
|
||
*/
|
||
ST_BOOLEAN chk_seg_needed (MVLU_RPT_CLIENT *rptClient,
|
||
ST_INT *asn1_len_array,
|
||
ST_INT cur_va_index, /* input arg */
|
||
ST_INT *next_va_index) /* output arg */
|
||
{
|
||
ST_INT negotiatedPduSize;
|
||
ST_INT unusedSize;
|
||
ST_INT num_entries;
|
||
ST_INT loopIndex;
|
||
ST_BOOLEAN segNeeded;
|
||
|
||
negotiatedPduSize = rptClient->netInfo->max_pdu_size;
|
||
|
||
/*negotiatedPduSize = 2*1024;*/
|
||
|
||
num_entries = rptClient->rpt_ctrl->dsNvl->num_of_entries;
|
||
|
||
/* Subtract overhead from max "negotiated" PDU size. */
|
||
/* Very verbose, but most data are constants so compiler should simplify.*/
|
||
unusedSize = negotiatedPduSize
|
||
- 32 /* InfoRpt overhead (ROUGH estimate) */
|
||
- 131 /* RptID (Vstring129) */
|
||
- 5 /* OptFlds (Bstring10) */
|
||
- 4 /* SqNum (Int16) */
|
||
- 131 /* DataSetNam (Vstring129) */
|
||
- 3 /* BufOvfl (Boolean) */
|
||
- 8 /* TimeOfEntry (Btime6) */
|
||
- 4 /* SubSeqNum (Int16) */
|
||
- 3 /* MoreSegFoll (Boolean) */
|
||
-10 /* EntryID (Ostring8) */
|
||
-(3+num_entries/8) /* Inclusion Bitstring */
|
||
;
|
||
|
||
/* In a loop, check if each var fits in the Report. */
|
||
for (loopIndex = cur_va_index; loopIndex < num_entries; ++loopIndex)
|
||
{
|
||
/* Subtract space needed for this var. */
|
||
/* If len==0, this var must not be included in the report, so skip it.*/
|
||
if (asn1_len_array[loopIndex])
|
||
{ /* this var should be included in rpt. Check for space. */
|
||
unusedSize -= 131; /* DataRef (Vstring129) */
|
||
unusedSize -= 4; /* ReasonForInclusion (Bstring4?) */
|
||
unusedSize -= asn1_len_array[loopIndex]; /* Data */
|
||
}
|
||
if (unusedSize < 0)
|
||
break; /* cannot fit this va. Break out of loop. */
|
||
}
|
||
/* When loop ends, "loopIndex" is index to first variable of "next" report.*/
|
||
|
||
*next_va_index = loopIndex;
|
||
if (cur_va_index == 0 && (*next_va_index) == num_entries)
|
||
segNeeded = SD_FALSE; /* All fit in one PDU (no segmenting) */
|
||
else
|
||
segNeeded = SD_TRUE;
|
||
return (segNeeded);
|
||
}
|
||
|
||
/************************************************************************/
|
||
/* mvlu_confrev_rd_ind */
|
||
/* NOTE: confrev write funct should be something like u_no_write_allowed*/
|
||
/************************************************************************/
|
||
ST_VOID mvlu_confrev_rd_ind (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
|
||
{
|
||
ST_UINT32 *dest;
|
||
MVLU_BASRCB *rcb;
|
||
ST_RET rc;
|
||
|
||
rc = SD_FAILURE;
|
||
/* Only for 61850, so use mvl61850_get_rcb to find the RCB. */
|
||
rcb = mvl61850_get_rcb (mvluRdVaCtrl->rdVaCtrl->va->base_va,
|
||
mvluRdVaCtrl->rt,
|
||
NULL);
|
||
if (rcb != NULL)
|
||
{
|
||
dest = (ST_UINT32 *) mvluRdVaCtrl->primData;
|
||
*dest = rcb->ConfRev;
|
||
rc = SD_SUCCESS;
|
||
}
|
||
mvlu_rd_prim_done (mvluRdVaCtrl, rc);
|
||
}
|
||
|
||
/************************************************************************/
|
||
/* mvlu_purgebuf_rd_ind */
|
||
/************************************************************************/
|
||
ST_VOID mvlu_purgebuf_rd_ind (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
|
||
{
|
||
ST_BOOLEAN *dest;
|
||
MVLU_BASRCB *rcb;
|
||
ST_RET rc;
|
||
|
||
rc = SD_FAILURE;
|
||
/* Only for 61850, so use mvl61850_get_rcb to find the RCB. */
|
||
rcb = mvl61850_get_rcb (mvluRdVaCtrl->rdVaCtrl->va->base_va,
|
||
mvluRdVaCtrl->rt,
|
||
NULL);
|
||
if (rcb != NULL)
|
||
{
|
||
dest = (ST_BOOLEAN *) mvluRdVaCtrl->primData;
|
||
*dest = 0; /* no data stored. ALWAYS return 0 */
|
||
rc = SD_SUCCESS;
|
||
}
|
||
mvlu_rd_prim_done (mvluRdVaCtrl, rc);
|
||
}
|
||
|
||
/************************************************************************/
|
||
/* mvlu_gi_rd_ind */
|
||
/************************************************************************/
|
||
ST_VOID mvlu_gi_rd_ind (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
|
||
{
|
||
ST_BOOLEAN *dest;
|
||
MVLU_BASRCB *rcb;
|
||
ST_RET rc;
|
||
|
||
rc = SD_FAILURE;
|
||
/* Only for 61850, so use mvl61850_get_rcb to find the RCB. */
|
||
rcb = mvl61850_get_rcb (mvluRdVaCtrl->rdVaCtrl->va->base_va,
|
||
mvluRdVaCtrl->rt,
|
||
NULL);
|
||
if (rcb != NULL)
|
||
{
|
||
dest = (ST_BOOLEAN *) mvluRdVaCtrl->primData;
|
||
*dest = 0; /* no data stored. ALWAYS return 0 */
|
||
rc = SD_SUCCESS;
|
||
}
|
||
mvlu_rd_prim_done (mvluRdVaCtrl, rc);
|
||
}
|
||
|
||
|
||
/************************************************************************/
|
||
/* mvlu_entryid_rd_ind */
|
||
/************************************************************************/
|
||
ST_VOID mvlu_entryid_rd_ind (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
|
||
{
|
||
MVLU_BASRCB *rcb;
|
||
ST_RET rc;
|
||
MVLU_RPT_CLIENT *rptClient; /* can access all rpt info from this struct.*/
|
||
BUFLIST_ENTRY *entry;
|
||
|
||
rc = SD_FAILURE;
|
||
|
||
/* Only for 61850, so use mvl61850_get_rcb to find the RCB. */
|
||
rcb = mvl61850_get_rcb (mvluRdVaCtrl->rdVaCtrl->va->base_va,
|
||
mvluRdVaCtrl->rt,
|
||
&rptClient);
|
||
if (rcb != NULL)
|
||
{
|
||
#if 1 /* NOTE: Ed2 says to return different value if RptEna=FALSE or TRUE.*/
|
||
if (rptClient->basrcb.RptEna==0)
|
||
{
|
||
entry = (BUFLIST_ENTRY *) list_find_last ((DBL_LNK *) rptClient->rpt_ctrl->brcbCtrl.rpt_list);
|
||
if (entry == NULL) /* empty list, return all 0 */
|
||
memset (mvluRdVaCtrl->primData, 0, 8);
|
||
else
|
||
memcpy (mvluRdVaCtrl->primData, entry->EntryID, 8);
|
||
}
|
||
else
|
||
memcpy (mvluRdVaCtrl->primData, rptClient->rpt_ctrl->brcbCtrl.lastSentEntryID, 8);
|
||
#else /* old way (before Ed2) */
|
||
memcpy (mvluRdVaCtrl->primData, rptClient->rpt_ctrl->brcbCtrl.lastSentEntryID, 8);
|
||
#endif
|
||
rc = SD_SUCCESS;
|
||
}
|
||
mvlu_rd_prim_done (mvluRdVaCtrl, rc);
|
||
}
|
||
|
||
/************************************************************************/
|
||
/* mvlu_timeofentry_rd_ind */
|
||
/************************************************************************/
|
||
|
||
ST_VOID mvlu_timeofentry_rd_ind (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
|
||
{
|
||
MVLU_BASRCB *rcb;
|
||
ST_RET rc;
|
||
MVLU_RPT_CLIENT *rptClient; /* can access all rpt info from this struct.*/
|
||
BUFLIST_ENTRY *entry;
|
||
|
||
rc = SD_FAILURE;
|
||
/* Only for 61850, so use mvl61850_get_rcb to find the RCB. */
|
||
rcb = mvl61850_get_rcb (mvluRdVaCtrl->rdVaCtrl->va->base_va,
|
||
mvluRdVaCtrl->rt,
|
||
&rptClient);
|
||
if (rcb != NULL)
|
||
{
|
||
#if 1 /* NOTE: Ed2 says to return different value if RptEna=FALSE or TRUE.*/
|
||
if (rptClient->basrcb.RptEna==0)
|
||
{
|
||
entry = (BUFLIST_ENTRY *) list_find_last ((DBL_LNK *) rptClient->rpt_ctrl->brcbCtrl.rpt_list);
|
||
if (entry == NULL) /* empty list */
|
||
{ /* Empty buffer. Return all 0. */
|
||
MMS_BTIME6 *btime = (MMS_BTIME6 *)mvluRdVaCtrl->primData;
|
||
btime->ms = 0;
|
||
btime->day = 0;
|
||
}
|
||
else
|
||
memcpy (mvluRdVaCtrl->primData, &entry->TimeOfEntry, sizeof (MMS_BTIME6));
|
||
}
|
||
else
|
||
memcpy (mvluRdVaCtrl->primData, &rptClient->rpt_ctrl->brcbCtrl.lastSentTimeOfEntry, sizeof (MMS_BTIME6));
|
||
#else /* old way (before Ed2) */
|
||
memcpy (mvluRdVaCtrl->primData, &rcb->TimeofEntry, sizeof (MMS_BTIME6));
|
||
#endif
|
||
rc = SD_SUCCESS;
|
||
}
|
||
mvlu_rd_prim_done (mvluRdVaCtrl, rc);
|
||
}
|
||
|
||
/************************************************************************/
|
||
/* mvlu_purgebuf_wr_ind */
|
||
/************************************************************************/
|
||
ST_VOID mvlu_purgebuf_wr_ind (MVLU_WR_VA_CTRL *mvluWrVaCtrl)
|
||
{
|
||
ST_BOOLEAN newval; /* val to write to RptEna */
|
||
MVLU_BASRCB *rcb;
|
||
MVLU_RPT_CLIENT *rptClient;
|
||
ST_RET rc;
|
||
|
||
rc = SD_FAILURE;
|
||
/* Only for 61850, so use mvl61850_get_rcb to find the RCB. */
|
||
rcb = mvl61850_get_rcb (mvluWrVaCtrl->wrVaCtrl->va->base_va,
|
||
mvluWrVaCtrl->rt,
|
||
&rptClient);
|
||
if (_rcb_writable (rcb, rptClient, mvluWrVaCtrl))
|
||
{ /* rcb found and NOT enabled */
|
||
rc = SD_SUCCESS; /* allow write */
|
||
newval = *(ST_BOOLEAN *) mvluWrVaCtrl->primData;
|
||
|
||
/* If the client is enabling the Purge, dump rpts_sent & rpts_queued */
|
||
if (newval != 0) /* any non-zero val means "enable" */
|
||
{
|
||
/* this function does just what we want. */
|
||
mvl61850_brcb_rpt_lists_clean (&rptClient->rpt_ctrl->brcbCtrl);
|
||
/*renxiaobao 61850 <20>ڶ<EFBFBD><DAB6><EFBFBD><EFBFBD><EFBFBD>*/
|
||
memset(rcb->EntryID,0,8);
|
||
}
|
||
|
||
/* NOTE: don't actually change any value because PurgeBuf is */
|
||
/* automatically set back to 0 when Purge is complete. */
|
||
}
|
||
else
|
||
mvluWrVaCtrl->wrVaCtrl->failure = ARE_TEMP_UNAVAIL;
|
||
mvlu_wr_prim_done (mvluWrVaCtrl, rc);
|
||
}
|
||
|
||
/************************************************************************/
|
||
/* mvlu_gi_wr_ind */
|
||
/************************************************************************/
|
||
ST_VOID mvlu_gi_wr_ind (MVLU_WR_VA_CTRL *mvluWrVaCtrl)
|
||
{
|
||
ST_BOOLEAN newval; /* val to write to RptEna */
|
||
MVLU_BASRCB *rcb;
|
||
MVLU_RPT_CLIENT *rptClient;
|
||
ST_RET rc;
|
||
|
||
rc = SD_FAILURE;
|
||
/* Only for 61850, so use mvl61850_get_rcb to find the RCB. */
|
||
rcb = mvl61850_get_rcb (mvluWrVaCtrl->wrVaCtrl->va->base_va,
|
||
mvluWrVaCtrl->rt,
|
||
&rptClient);
|
||
|
||
/* Check all condtions. If all conditions right, set rc=SD_SUCCESS to allow write.*/
|
||
if (rcb != NULL)
|
||
{
|
||
/* RCB must be enabled. */
|
||
/* NOTE: this is only RCB attribute that is writable when RCB enabled.*/
|
||
if (rptClient->basrcb.RptEna)
|
||
{
|
||
if (rptClient->rpt_ctrl->rcb_type == RCB_TYPE_IEC_URCB)
|
||
{
|
||
/* URCB must be reserved by this clint. RCB is enabled, so it */
|
||
/* must already be reserved. Just make sure reserved by this client.*/
|
||
if (rptClient->netInfo == mvluWrVaCtrl->indCtrl->event->net_info)
|
||
rc = SD_SUCCESS; /* allow write */
|
||
else
|
||
MVLU_LOG_FLOW1 ("Write of GI for RptID='%s' FAILED because RCB reserved by another client",
|
||
rcb->RptID);
|
||
}
|
||
else /* RCB_TYPE_IEC_BRCB */
|
||
rc = SD_SUCCESS; /* allow write */
|
||
}
|
||
else
|
||
MVLU_LOG_FLOW1 ("Write of GI for RptID='%s' FAILED because RCB not enabled",
|
||
rcb->RptID);
|
||
}
|
||
|
||
if (rc == SD_SUCCESS)
|
||
{
|
||
newval = *(ST_BOOLEAN *) mvluWrVaCtrl->primData;
|
||
|
||
/* If client is enabling the GI & TrgOps.GI=TRUE, start GI scan.*/
|
||
if (newval != 0 /* any non-zero val means "enable" */
|
||
&& BSTR_BIT_GET(rcb->TrgOps.data, TRGOPS_BITNUM_GENERAL_INTERROGATION) != 0)
|
||
mvlu_integrity_scan_read (rptClient,
|
||
mvlu_gi_scan_va_done,
|
||
mvlu_gi_scan_done); /* begin GI scan*/
|
||
else if(newval != 0) rc = SD_FAILURE; /*renxiaobao add 20180731*/
|
||
|
||
/* NOTE: don't actually change any value because GI is automatically*/
|
||
/* set back to 0 when GI is complete. */
|
||
}
|
||
else /* for any error, return TEMP_UNAVAIL */
|
||
mvluWrVaCtrl->wrVaCtrl->failure = ARE_TEMP_UNAVAIL;
|
||
mvlu_wr_prim_done (mvluWrVaCtrl, rc);
|
||
}
|
||
|
||
/************************************************************************/
|
||
/* mvlu_entryid_wr_ind */
|
||
/************************************************************************/
|
||
ST_VOID mvlu_entryid_wr_ind (MVLU_WR_VA_CTRL *mvluWrVaCtrl)
|
||
{
|
||
MVLU_BASRCB *rcb;
|
||
MVLU_RPT_CLIENT *rptClient;
|
||
ST_RET rc;
|
||
|
||
rc = SD_FAILURE;
|
||
/* Only for 61850, so use mvl61850_get_rcb to find the RCB. */
|
||
rcb = mvl61850_get_rcb (mvluWrVaCtrl->wrVaCtrl->va->base_va,
|
||
mvluWrVaCtrl->rt,
|
||
&rptClient);
|
||
if (_rcb_writable (rcb, rptClient, mvluWrVaCtrl))
|
||
{
|
||
/* NOTE: primData points to EntryID as Ostring8. */
|
||
rc = mvl61850_brcb_rpt_set_entryid (rptClient, (ST_UCHAR *) mvluWrVaCtrl->primData);
|
||
}
|
||
else
|
||
mvluWrVaCtrl->wrVaCtrl->failure = ARE_TEMP_UNAVAIL;
|
||
mvlu_wr_prim_done (mvluWrVaCtrl, rc);
|
||
}
|
||
|
||
/************************************************************************/
|
||
/* mvlu_sqnum_int16u_rd_ind_fun */
|
||
/* Same as mvlu_sqnum_rd_ind_fun except uses SqNumInt16u */
|
||
/* (UINT16 instead of UINT8). */
|
||
/************************************************************************/
|
||
|
||
ST_VOID mvlu_sqnum_int16u_rd_ind_fun (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
|
||
{
|
||
ST_UINT16 *dest;
|
||
MVLU_BASRCB *rcb;
|
||
ST_RET rc;
|
||
|
||
rc = SD_FAILURE;
|
||
/* Only for 61850, so use mvl61850_get_rcb to find the RCB. */
|
||
rcb = mvl61850_get_rcb (mvluRdVaCtrl->rdVaCtrl->va->base_va,
|
||
mvluRdVaCtrl->rt,
|
||
NULL);
|
||
if (rcb != NULL)
|
||
{
|
||
dest = (ST_UINT16 *) mvluRdVaCtrl->primData;
|
||
*dest = rcb->SqNumInt16u;
|
||
rc = SD_SUCCESS;
|
||
}
|
||
mvlu_rd_prim_done (mvluRdVaCtrl, rc);
|
||
}
|
||
|
||
/************************************************************************/
|
||
/* mvl61850_beh_stval_rd_ind */
|
||
/************************************************************************/
|
||
ST_VOID mvl61850_beh_stval_rd_ind (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
|
||
{
|
||
MVL_VAR_ASSOC *base_var;
|
||
MVL_VAR_ASSOC *lln0_var;
|
||
ST_RET retcode = SD_SUCCESS;
|
||
ST_INT32 mod_stval; /* ST$Mod$stVal from this LN */
|
||
ST_INT32 lln0mod_stval; /* ST$Mod$stVal from LLN0 */
|
||
ST_INT32 beh_stval; /* ST$Beh$stVal from this LN (computed) */
|
||
MVL_DOM_CTRL *dom;
|
||
ST_INT j;
|
||
|
||
base_var = mvluRdVaCtrl->rdVaCtrl->va->base_va;
|
||
|
||
/* Get value of "ST$Mod$stVal" in this LN. */
|
||
retcode = mvlu_get_leaf_val_int_any (base_var, "ST$Mod$stVal", &mod_stval);
|
||
|
||
if (retcode == SD_SUCCESS)
|
||
{
|
||
/* Get value of "ST$Mod$stVal" in LLN0 of the same domain. */
|
||
lln0_var = NULL; /* assume it's not found */
|
||
assert (mvluRdVaCtrl->rdVaCtrl->va_scope.scope == DOM_SPEC);
|
||
dom = mvluRdVaCtrl->rdVaCtrl->va_scope.dom;
|
||
for (j = 0; j < dom->num_var_assoc; j++)
|
||
{
|
||
/* Use "strstr" because name may have prefix and suffix*/
|
||
if (strstr (dom->var_assoc_tbl[j]->name, "LLN0") != NULL)
|
||
{
|
||
lln0_var = dom->var_assoc_tbl[j];
|
||
break; /* found it. stop looping */
|
||
}
|
||
}
|
||
if (lln0_var)
|
||
retcode = mvlu_get_leaf_val_int_any (lln0_var, "ST$Mod$stVal", &lln0mod_stval);
|
||
else
|
||
retcode = SD_FAILURE;
|
||
}
|
||
|
||
if (retcode == SD_SUCCESS)
|
||
{ /* both successful. combine results. */
|
||
switch (lln0mod_stval)
|
||
{
|
||
case 1:
|
||
if (mod_stval > 0 && mod_stval <= 5)
|
||
beh_stval = mod_stval;
|
||
else
|
||
beh_stval = 0; /* Mod is illegal, so set Beh to illegal value too*/
|
||
break;
|
||
|
||
case 2:
|
||
switch (mod_stval)
|
||
{
|
||
case 1:
|
||
case 2:
|
||
beh_stval = 2; break;
|
||
case 3:
|
||
case 4:
|
||
beh_stval = 4; break;
|
||
case 5:
|
||
beh_stval = 5; break;
|
||
default:
|
||
beh_stval = 0; break; /* Mod is illegal, so set Beh to illegal value too*/
|
||
}
|
||
break;
|
||
|
||
case 3:
|
||
switch (mod_stval)
|
||
{
|
||
case 1:
|
||
beh_stval = 3; break;
|
||
case 2:
|
||
beh_stval = 4; break;
|
||
case 3:
|
||
beh_stval = 3; break;
|
||
case 4:
|
||
beh_stval = 4; break;
|
||
case 5:
|
||
beh_stval = 5; break;
|
||
default:
|
||
beh_stval = 0; break; /* Mod is illegal, so set Beh to illegal value too*/
|
||
}
|
||
break;
|
||
|
||
case 4:
|
||
switch (mod_stval)
|
||
{
|
||
case 1:
|
||
case 2:
|
||
case 3:
|
||
case 4:
|
||
beh_stval = 4; break;
|
||
case 5:
|
||
beh_stval = 5; break;
|
||
default:
|
||
beh_stval = 0; break; /* Mod is illegal, so set Beh to illegal value too*/
|
||
}
|
||
break;
|
||
|
||
case 5:
|
||
if (mod_stval > 0 && mod_stval <= 5)
|
||
beh_stval = 5;
|
||
else
|
||
beh_stval = 0; /* Mod is illegal, so set Beh to illegal value too*/
|
||
break;
|
||
|
||
default:
|
||
beh_stval = 0; /* LLNO Mod is illegal, so set Beh to illegal value too*/
|
||
break;
|
||
} /* end outer switch */
|
||
}
|
||
|
||
if (retcode == SD_SUCCESS)
|
||
{
|
||
/* Convert data to configured type (INT8, INT16, or INT32). */
|
||
if (mvluRdVaCtrl->rt->u.p.el_len == 1)
|
||
*(ST_INT8 *) mvluRdVaCtrl->primData = (ST_INT8) beh_stval; /* copy data */
|
||
else if (mvluRdVaCtrl->rt->u.p.el_len == 2)
|
||
*(ST_INT16 *) mvluRdVaCtrl->primData = (ST_INT16) beh_stval; /* copy data */
|
||
else if (mvluRdVaCtrl->rt->u.p.el_len == 4)
|
||
*(ST_INT32 *) mvluRdVaCtrl->primData = beh_stval; /* copy data */
|
||
else
|
||
retcode = SD_FAILURE; /* unsupported len. Should never happen.*/
|
||
}
|
||
mvlu_rd_prim_done (mvluRdVaCtrl, retcode);
|
||
}
|
||
|
||
/************************************************************************/
|
||
/* mvl61850_datset_wr_ind */
|
||
/* Try to change IEC 61850 Report Dataset. */
|
||
/* NOTE: for IEC 61850 only (not for UCA). */
|
||
/************************************************************************/
|
||
ST_VOID mvl61850_datset_wr_ind (MVLU_WR_VA_CTRL *mvluWrVaCtrl)
|
||
{
|
||
MVLU_BASRCB *rcb;
|
||
ST_RET retCode;
|
||
ST_CHAR tmpbuf [MVL61850_MAX_OBJREF_LEN+1];
|
||
OBJECT_NAME nvl_oname;
|
||
MVLU_RPT_CLIENT *rptClient;
|
||
MVL_NVLIST_CTRL *nvl;
|
||
ST_BOOLEAN do_purge = SD_FALSE; /* Chg to SD_TRUE if Purge Buffer required*/
|
||
ST_INT16 failure = ARE_TEMP_UNAVAIL; /* use on err if no other value set*/
|
||
|
||
retCode = SD_FAILURE;
|
||
/* Only for 61850, so use mvl61850_get_rcb to find the RCB. */
|
||
rcb = mvl61850_get_rcb (mvluWrVaCtrl->wrVaCtrl->va->base_va,
|
||
mvluWrVaCtrl->rt,
|
||
&rptClient);
|
||
if (_rcb_writable (rcb, rptClient, mvluWrVaCtrl))
|
||
{
|
||
if (strcmp (rcb->DatSetNa, mvluWrVaCtrl->primData) == 0)
|
||
{
|
||
retCode = SD_SUCCESS; /* Same val written. Nothing to do but allow it*/
|
||
}
|
||
else if (mvluWrVaCtrl->primData[0] == '\0')
|
||
{ /* client wrote empty string, and it was NOT already empty */
|
||
mvl61850_rpt_dataset_destroy (rptClient->rpt_ctrl);
|
||
rcb->ConfRev++; /* DatSet changed. Must increment ConfRev. */
|
||
retCode = SD_SUCCESS;
|
||
}
|
||
else if (strlen ((ST_CHAR *) mvluWrVaCtrl->primData) > MVL61850_MAX_OBJREF_LEN)
|
||
{
|
||
failure = ARE_OBJ_VALUE_INVALID;
|
||
}
|
||
else
|
||
{
|
||
/* strtok modifies input buffer, so copy to local buffer first. */
|
||
strcpy (tmpbuf, (ST_CHAR *) mvluWrVaCtrl->primData);
|
||
if (tmpbuf[0] == '@')
|
||
{ /* AA-Specific Dataset */
|
||
nvl_oname.obj_name.vmd_spec = &tmpbuf[1]; /*extract NVL name*/
|
||
nvl_oname.object_tag = AA_SPEC;
|
||
}
|
||
else if (tmpbuf[0] == '/')
|
||
{ /* VMD-Specific Dataset */
|
||
nvl_oname.obj_name.vmd_spec = &tmpbuf[1]; /*extract NVL name*/
|
||
nvl_oname.object_tag = VMD_SPEC;
|
||
}
|
||
else
|
||
{ /* must be Domain-Specific Dataset */
|
||
nvl_oname.domain_id = strtok (tmpbuf, "/"); /*extract dom name*/
|
||
nvl_oname.obj_name.vmd_spec = strtok (NULL, ""); /*extract NVL name*/
|
||
nvl_oname.object_tag = DOM_SPEC;
|
||
}
|
||
|
||
/* If DatSet name is illegal, fail.*/
|
||
if (nvl_oname.obj_name.vmd_spec == NULL) /* strtok failed*/
|
||
{
|
||
MVL_LOG_ERR1 ("DatSet name '%s' is invalid. Cannot write it to RCB.",
|
||
(ST_CHAR *) mvluWrVaCtrl->primData);
|
||
failure = ARE_OBJ_VALUE_INVALID;
|
||
}
|
||
else if (nvl_oname.object_tag == AA_SPEC &&
|
||
rptClient->rpt_ctrl->rcb_type == RCB_TYPE_IEC_BRCB)
|
||
{
|
||
MVL_LOG_ERR1 ("Writing AA-Specific DatSet '%s' to BRCB is NEVER allowed.",
|
||
(ST_CHAR *) mvluWrVaCtrl->primData);
|
||
failure = ARE_OBJ_VALUE_INVALID;
|
||
}
|
||
else if (nvl_oname.object_tag == AA_SPEC &&
|
||
rptClient->rpt_ctrl->rcb_type == RCB_TYPE_IEC_URCB &&
|
||
rptClient->basrcb.Resv == 0)
|
||
{
|
||
MVL_LOG_ERR1 ("Writing AA-Specific DatSet '%s' to URCB not allowed because URCB is not reserved.",
|
||
(ST_CHAR *) mvluWrVaCtrl->primData);
|
||
failure = ARE_OBJ_VALUE_INVALID;
|
||
}
|
||
else
|
||
{
|
||
nvl = mvl_vmd_find_nvl (&mvl_vmd, &nvl_oname,
|
||
mvluWrVaCtrl->indCtrl->event->net_info); /* for AA_SPEC*/
|
||
if (nvl)
|
||
{
|
||
if (rptClient->rpt_ctrl->rcb_type == RCB_TYPE_IEC_BRCB)
|
||
do_purge = SD_TRUE; /* BRCB & val changed, must purge buffer*/
|
||
/* Replace "dataset". */
|
||
/* NOTE: If dataset was empty, destroy function does nothing. */
|
||
mvl61850_rpt_dataset_destroy (rptClient->rpt_ctrl);
|
||
retCode = mvl61850_rpt_dataset_create (rptClient->rpt_ctrl, nvl);
|
||
/* WARNING: if create fails, old dataset is lost.*/
|
||
if (retCode)
|
||
{
|
||
MVL_LOG_ERR1 ("DatSet change failed. RCB DatSet '%s' is no longer valid.", rcb->DatSetNa);
|
||
/* Old DatSet is no longer valid, so clear it.*/
|
||
rcb->DatSetNa[0] = '\0';
|
||
}
|
||
/* Even if create failed, DatSet changed (to NULL) so inc ConfRev.*/
|
||
rcb->ConfRev++; /* DatSet changed. Must increment ConfRev.*/
|
||
/*renxiaobao 61850 <20>ڶ<EFBFBD><DAB6><EFBFBD><EFBFBD><EFBFBD>*/
|
||
memset(rcb->EntryID,0,8);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (do_purge)
|
||
mvl61850_brcb_rpt_lists_clean (&rptClient->rpt_ctrl->brcbCtrl);
|
||
|
||
if (retCode != SD_SUCCESS)
|
||
mvluWrVaCtrl->wrVaCtrl->failure = failure;
|
||
|
||
mvlu_wr_prim_done (mvluWrVaCtrl, retCode);
|
||
}
|
||
|
||
/************************************************************************/
|
||
/* fill_asn1_len_array */
|
||
/* Compute the ASN.1 encoded len for each var in a rpt dataset. */
|
||
/************************************************************************/
|
||
static ST_RET fill_asn1_len_array (MVLU_RPT_CLIENT *rptClient,
|
||
MVL_NVLIST_CTRL *dsNvl, /* rpt dataset */
|
||
ST_INT *asn1_len_array) /* array of ASN.1 lengths, one for each var*/
|
||
/* array filled in by this function */
|
||
{
|
||
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 */
|
||
|
||
/* Alloc tmp ASN.1 buffer. Use it for each encode in this loop. */
|
||
tmp_asn1_buf = chk_malloc (tmp_asn1_buf_size);
|
||
for (j = 0; j < dsNvl->num_of_entries; ++j)
|
||
{
|
||
if (rptClient->reasons_data[j]) /* 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,
|
||
dsNvl->entries[j]->data, /* CRITICAL: use data from var*/
|
||
tmp_asn1_buf,
|
||
tmp_asn1_buf_size,
|
||
&asn1_ptr, /* function sets asn1_ptr (not needed here)*/
|
||
&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);
|
||
}
|
||
|
||
/************************************************************************/
|
||
/* mvl61850_objref_create */
|
||
/* Create the ObjectReference for this Object. */
|
||
/* objRef must point to buffer of at least [MVL61850_MAX_OBJREF_LEN+1] char*/
|
||
/************************************************************************/
|
||
ST_RET mvl61850_objref_create (ST_CHAR *objName,
|
||
MVL_SCOPE *objScope,
|
||
ST_CHAR *objRef) /* ptr to ObjectReference */
|
||
{
|
||
switch (objScope->scope)
|
||
{
|
||
case VMD_SPEC:
|
||
strcpy (objRef, "/");
|
||
break;
|
||
|
||
case DOM_SPEC:
|
||
strcpy (objRef, objScope->dom->name);
|
||
strcat (objRef, "/");
|
||
break;
|
||
|
||
case AA_SPEC:
|
||
strcpy (objRef,"@/");
|
||
break;
|
||
}
|
||
|
||
/* Scope in place, now append the name of the Object. */
|
||
if (strlen(objRef) + strlen(objName) > MVL61850_MAX_OBJREF_LEN)
|
||
{ /* should NEVER happen */
|
||
MVL_LOG_ERR0 ("Can't generate ObjectReference (too long)");
|
||
objRef [0] = '\0'; /* return empty string */
|
||
return (SD_FAILURE);
|
||
}
|
||
strcat (objRef, objName);
|
||
return (SD_SUCCESS);
|
||
}
|
||
|