|
|
|
@ -3,7 +3,7 @@
|
|
|
|
|
*
|
|
|
|
|
* Client implementation for IEC 61850 reporting.
|
|
|
|
|
*
|
|
|
|
|
* Copyright 2013-2024 Michael Zillgith
|
|
|
|
|
* Copyright 2013-2025 Michael Zillgith
|
|
|
|
|
*
|
|
|
|
|
* This file is part of libIEC61850.
|
|
|
|
|
*
|
|
|
|
@ -93,7 +93,10 @@ ClientReport_create()
|
|
|
|
|
{
|
|
|
|
|
ClientReport self = (ClientReport) GLOBAL_CALLOC(1, sizeof(struct sClientReport));
|
|
|
|
|
|
|
|
|
|
self->dataSetSize = -1;
|
|
|
|
|
if (self)
|
|
|
|
|
{
|
|
|
|
|
self->dataSetSize = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
@ -101,27 +104,30 @@ ClientReport_create()
|
|
|
|
|
void
|
|
|
|
|
ClientReport_destroy(ClientReport self)
|
|
|
|
|
{
|
|
|
|
|
if (self->entryId)
|
|
|
|
|
MmsValue_delete(self->entryId);
|
|
|
|
|
if (self)
|
|
|
|
|
{
|
|
|
|
|
if (self->entryId)
|
|
|
|
|
MmsValue_delete(self->entryId);
|
|
|
|
|
|
|
|
|
|
GLOBAL_FREEMEM(self->rcbReference);
|
|
|
|
|
GLOBAL_FREEMEM(self->rcbReference);
|
|
|
|
|
|
|
|
|
|
if (self->rptId != NULL)
|
|
|
|
|
GLOBAL_FREEMEM(self->rptId);
|
|
|
|
|
if (self->rptId)
|
|
|
|
|
GLOBAL_FREEMEM(self->rptId);
|
|
|
|
|
|
|
|
|
|
if (self->dataSetValues != NULL)
|
|
|
|
|
MmsValue_delete(self->dataSetValues);
|
|
|
|
|
if (self->dataSetValues)
|
|
|
|
|
MmsValue_delete(self->dataSetValues);
|
|
|
|
|
|
|
|
|
|
if (self->dataReferences != NULL)
|
|
|
|
|
MmsValue_delete(self->dataReferences);
|
|
|
|
|
if (self->dataReferences)
|
|
|
|
|
MmsValue_delete(self->dataReferences);
|
|
|
|
|
|
|
|
|
|
if (self->reasonForInclusion != NULL)
|
|
|
|
|
GLOBAL_FREEMEM(self->reasonForInclusion);
|
|
|
|
|
if (self->reasonForInclusion)
|
|
|
|
|
GLOBAL_FREEMEM(self->reasonForInclusion);
|
|
|
|
|
|
|
|
|
|
if (self->dataSetName != NULL)
|
|
|
|
|
GLOBAL_FREEMEM((void*) self->dataSetName);
|
|
|
|
|
if (self->dataSetName)
|
|
|
|
|
GLOBAL_FREEMEM((void*) self->dataSetName);
|
|
|
|
|
|
|
|
|
|
GLOBAL_FREEMEM(self);
|
|
|
|
|
GLOBAL_FREEMEM(self);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char*
|
|
|
|
@ -163,7 +169,6 @@ ClientReport_getTimestamp(ClientReport self)
|
|
|
|
|
return self->timestamp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ClientReport_hasSeqNum(ClientReport self)
|
|
|
|
|
{
|
|
|
|
@ -223,11 +228,14 @@ ClientReport_getDataReference(ClientReport self, int elementIndex)
|
|
|
|
|
{
|
|
|
|
|
char* dataReference = NULL;
|
|
|
|
|
|
|
|
|
|
if (self->dataReferences != NULL) {
|
|
|
|
|
if (self->dataReferences)
|
|
|
|
|
{
|
|
|
|
|
MmsValue* dataRefValue = MmsValue_getElement(self->dataReferences, elementIndex);
|
|
|
|
|
|
|
|
|
|
if (dataRefValue != NULL) {
|
|
|
|
|
if (MmsValue_getType(dataRefValue) == MMS_VISIBLE_STRING) {
|
|
|
|
|
if (dataRefValue)
|
|
|
|
|
{
|
|
|
|
|
if (MmsValue_getType(dataRefValue) == MMS_VISIBLE_STRING)
|
|
|
|
|
{
|
|
|
|
|
return MmsValue_toString(dataRefValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -271,8 +279,9 @@ lookupReportHandler(IedConnection self, const char* rcbReference)
|
|
|
|
|
{
|
|
|
|
|
LinkedList element = LinkedList_getNext(self->enabledReports);
|
|
|
|
|
|
|
|
|
|
while (element != NULL) {
|
|
|
|
|
ClientReport report = (ClientReport) element->data;
|
|
|
|
|
while (element)
|
|
|
|
|
{
|
|
|
|
|
ClientReport report = (ClientReport)element->data;
|
|
|
|
|
|
|
|
|
|
if (strcmp(report->rcbReference, rcbReference) == 0)
|
|
|
|
|
return report;
|
|
|
|
@ -288,21 +297,23 @@ uninstallReportHandler(IedConnection self, const char* rcbReference)
|
|
|
|
|
{
|
|
|
|
|
ClientReport report = lookupReportHandler(self, rcbReference);
|
|
|
|
|
|
|
|
|
|
if (report != NULL) {
|
|
|
|
|
if (report)
|
|
|
|
|
{
|
|
|
|
|
LinkedList_remove(self->enabledReports, report);
|
|
|
|
|
ClientReport_destroy(report);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
IedConnection_installReportHandler(IedConnection self, const char* rcbReference, const char* rptId, ReportCallbackFunction handler,
|
|
|
|
|
void* handlerParameter)
|
|
|
|
|
IedConnection_installReportHandler(IedConnection self, const char* rcbReference, const char* rptId,
|
|
|
|
|
ReportCallbackFunction handler, void* handlerParameter)
|
|
|
|
|
{
|
|
|
|
|
Semaphore_wait(self->reportHandlerMutex);
|
|
|
|
|
|
|
|
|
|
ClientReport report = lookupReportHandler(self, rcbReference);
|
|
|
|
|
|
|
|
|
|
if (report != NULL) {
|
|
|
|
|
if (report)
|
|
|
|
|
{
|
|
|
|
|
uninstallReportHandler(self, rcbReference);
|
|
|
|
|
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
@ -310,21 +321,25 @@ IedConnection_installReportHandler(IedConnection self, const char* rcbReference,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
report = ClientReport_create();
|
|
|
|
|
report->callback = handler;
|
|
|
|
|
report->callbackParameter = handlerParameter;
|
|
|
|
|
report->rcbReference = StringUtils_copyString(rcbReference);
|
|
|
|
|
|
|
|
|
|
if (rptId != NULL)
|
|
|
|
|
report->rptId = StringUtils_copyString(rptId);
|
|
|
|
|
else
|
|
|
|
|
report->rptId = NULL;
|
|
|
|
|
if (report)
|
|
|
|
|
{
|
|
|
|
|
report->callback = handler;
|
|
|
|
|
report->callbackParameter = handlerParameter;
|
|
|
|
|
report->rcbReference = StringUtils_copyString(rcbReference);
|
|
|
|
|
|
|
|
|
|
LinkedList_add(self->enabledReports, report);
|
|
|
|
|
if (rptId)
|
|
|
|
|
report->rptId = StringUtils_copyString(rptId);
|
|
|
|
|
else
|
|
|
|
|
report->rptId = NULL;
|
|
|
|
|
|
|
|
|
|
Semaphore_post(self->reportHandlerMutex);
|
|
|
|
|
LinkedList_add(self->enabledReports, report);
|
|
|
|
|
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("DEBUG_IED_CLIENT: Installed new report callback handler for %s\n", rcbReference);
|
|
|
|
|
Semaphore_post(self->reportHandlerMutex);
|
|
|
|
|
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("DEBUG_IED_CLIENT: Installed new report callback handler for %s\n", rcbReference);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
@ -361,13 +376,15 @@ IedConnection_triggerGIReport(IedConnection self, IedClientError* error, const c
|
|
|
|
|
|
|
|
|
|
MmsValue_delete(gi);
|
|
|
|
|
|
|
|
|
|
if (mmsError != MMS_ERROR_NONE) {
|
|
|
|
|
if (mmsError != MMS_ERROR_NONE)
|
|
|
|
|
{
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("DEBUG_IED_CLIENT: failed to trigger GI for %s!\n", rcbReference);
|
|
|
|
|
|
|
|
|
|
*error = iedConnection_mapMmsErrorToIedError(mmsError);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
*error = IED_ERROR_OK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -379,7 +396,8 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
|
|
|
|
|
|
|
|
|
|
MmsValue* rptIdValue = MmsValue_getElement(value, 0);
|
|
|
|
|
|
|
|
|
|
if ((rptIdValue == NULL) || (MmsValue_getType(rptIdValue) != MMS_VISIBLE_STRING)) {
|
|
|
|
|
if ((rptIdValue == NULL) || (MmsValue_getType(rptIdValue) != MMS_VISIBLE_STRING))
|
|
|
|
|
{
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: received malformed report (RptId)\n");
|
|
|
|
|
|
|
|
|
@ -389,19 +407,22 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
|
|
|
|
|
LinkedList element = LinkedList_getNext(self->enabledReports);
|
|
|
|
|
ClientReport matchingReport = NULL;
|
|
|
|
|
|
|
|
|
|
while (element != NULL) {
|
|
|
|
|
ClientReport report = (ClientReport) element->data;
|
|
|
|
|
while (element)
|
|
|
|
|
{
|
|
|
|
|
ClientReport report = (ClientReport)element->data;
|
|
|
|
|
char defaultRptId[130];
|
|
|
|
|
char* rptId = report->rptId;
|
|
|
|
|
|
|
|
|
|
if ((rptId == NULL) || (strlen(rptId) == 0)) {
|
|
|
|
|
if ((rptId == NULL) || (strlen(rptId) == 0))
|
|
|
|
|
{
|
|
|
|
|
StringUtils_concatString(defaultRptId, 130, report->rcbReference, "");
|
|
|
|
|
StringUtils_replace(defaultRptId, '.', '$');
|
|
|
|
|
|
|
|
|
|
rptId = defaultRptId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(MmsValue_toString(rptIdValue), rptId) == 0) {
|
|
|
|
|
if (strcmp(MmsValue_toString(rptIdValue), rptId) == 0)
|
|
|
|
|
{
|
|
|
|
|
matchingReport = report;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
@ -421,12 +442,13 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
|
|
|
|
|
matchingReport->hasBufOverflow = false;
|
|
|
|
|
matchingReport->hasSubSequenceNumber = false;
|
|
|
|
|
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: received report with ID %s\n", MmsValue_toString(rptIdValue));
|
|
|
|
|
|
|
|
|
|
MmsValue* optFlds = MmsValue_getElement(value, 1);
|
|
|
|
|
|
|
|
|
|
if ((optFlds == NULL) || (MmsValue_getType(optFlds) != MMS_BIT_STRING)) {
|
|
|
|
|
if ((optFlds == NULL) || (MmsValue_getType(optFlds) != MMS_BIT_STRING))
|
|
|
|
|
{
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: received malformed report (OptFlds)\n");
|
|
|
|
|
|
|
|
|
@ -436,29 +458,31 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
|
|
|
|
|
int inclusionIndex = 2;
|
|
|
|
|
|
|
|
|
|
/* has sequence-number */
|
|
|
|
|
if (MmsValue_getBitStringBit(optFlds, 1) == true) {
|
|
|
|
|
|
|
|
|
|
if (MmsValue_getBitStringBit(optFlds, 1) == true)
|
|
|
|
|
{
|
|
|
|
|
MmsValue* seqNum = MmsValue_getElement(value, inclusionIndex);
|
|
|
|
|
|
|
|
|
|
if ((seqNum == NULL) || (MmsValue_getType(seqNum) != MMS_UNSIGNED)) {
|
|
|
|
|
if ((seqNum == NULL) || (MmsValue_getType(seqNum) != MMS_UNSIGNED))
|
|
|
|
|
{
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: received malformed report (seqNum)\n");
|
|
|
|
|
|
|
|
|
|
goto exit_function;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
matchingReport->seqNum = (uint16_t) MmsValue_toUint32(seqNum);
|
|
|
|
|
matchingReport->seqNum = (uint16_t)MmsValue_toUint32(seqNum);
|
|
|
|
|
matchingReport->hasSequenceNumber = true;
|
|
|
|
|
|
|
|
|
|
inclusionIndex++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* has report-timestamp */
|
|
|
|
|
if (MmsValue_getBitStringBit(optFlds, 2) == true) {
|
|
|
|
|
|
|
|
|
|
if (MmsValue_getBitStringBit(optFlds, 2) == true)
|
|
|
|
|
{
|
|
|
|
|
MmsValue* timeStampValue = MmsValue_getElement(value, inclusionIndex);
|
|
|
|
|
|
|
|
|
|
if ((timeStampValue == NULL) || (MmsValue_getType(timeStampValue) != MMS_BINARY_TIME)) {
|
|
|
|
|
if ((timeStampValue == NULL) || (MmsValue_getType(timeStampValue) != MMS_BINARY_TIME))
|
|
|
|
|
{
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: received malformed report (timeStamp)\n");
|
|
|
|
|
|
|
|
|
@ -469,18 +493,20 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
|
|
|
|
|
matchingReport->timestamp = MmsValue_getBinaryTimeAsUtcMs(timeStampValue);
|
|
|
|
|
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: report has timestamp %llu\n", (unsigned long long) matchingReport->timestamp);
|
|
|
|
|
printf("IED_CLIENT: report has timestamp %llu\n", (unsigned long long)matchingReport->timestamp);
|
|
|
|
|
|
|
|
|
|
inclusionIndex++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* check if data set name is present */
|
|
|
|
|
if (MmsValue_getBitStringBit(optFlds, 4) == true) {
|
|
|
|
|
if (MmsValue_getBitStringBit(optFlds, 4) == true)
|
|
|
|
|
{
|
|
|
|
|
matchingReport->hasDataSetName = true;
|
|
|
|
|
|
|
|
|
|
MmsValue* dataSetName = MmsValue_getElement(value, inclusionIndex);
|
|
|
|
|
|
|
|
|
|
if ((dataSetName == NULL) || (MmsValue_getType(dataSetName) != MMS_VISIBLE_STRING)) {
|
|
|
|
|
if ((dataSetName == NULL) || (MmsValue_getType(dataSetName) != MMS_VISIBLE_STRING))
|
|
|
|
|
{
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: received malformed report (DatSet)\n");
|
|
|
|
|
|
|
|
|
@ -490,14 +516,17 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
|
|
|
|
|
int dataSetNameSize = MmsValue_getStringSize(dataSetName);
|
|
|
|
|
|
|
|
|
|
/* limit to prevent large memory allocation */
|
|
|
|
|
if (dataSetNameSize < 130) {
|
|
|
|
|
if (dataSetNameSize < 130)
|
|
|
|
|
{
|
|
|
|
|
const char* dataSetNameStr = MmsValue_toString(dataSetName);
|
|
|
|
|
|
|
|
|
|
if (matchingReport->dataSetName == NULL) {
|
|
|
|
|
matchingReport->dataSetName = (char*) GLOBAL_MALLOC(dataSetNameSize + 1);
|
|
|
|
|
if (matchingReport->dataSetName == NULL)
|
|
|
|
|
{
|
|
|
|
|
matchingReport->dataSetName = (char*)GLOBAL_MALLOC(dataSetNameSize + 1);
|
|
|
|
|
|
|
|
|
|
if (matchingReport->dataSetName == NULL) {
|
|
|
|
|
matchingReport->dataSetNameSize = 0;
|
|
|
|
|
if (matchingReport->dataSetName == NULL)
|
|
|
|
|
{
|
|
|
|
|
matchingReport->dataSetNameSize = 0;
|
|
|
|
|
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: failed to allocate memory\n");
|
|
|
|
@ -507,14 +536,17 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
|
|
|
|
|
|
|
|
|
|
matchingReport->dataSetNameSize = dataSetNameSize + 1;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (matchingReport->dataSetNameSize < MmsValue_getStringSize(dataSetName) + 1) {
|
|
|
|
|
GLOBAL_FREEMEM((void*) matchingReport->dataSetName);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (matchingReport->dataSetNameSize < MmsValue_getStringSize(dataSetName) + 1)
|
|
|
|
|
{
|
|
|
|
|
GLOBAL_FREEMEM((void*)matchingReport->dataSetName);
|
|
|
|
|
|
|
|
|
|
matchingReport->dataSetName = (char*) GLOBAL_MALLOC(dataSetNameSize + 1);
|
|
|
|
|
matchingReport->dataSetName = (char*)GLOBAL_MALLOC(dataSetNameSize + 1);
|
|
|
|
|
|
|
|
|
|
if (matchingReport->dataSetName == NULL) {
|
|
|
|
|
matchingReport->dataSetNameSize = 0;
|
|
|
|
|
if (matchingReport->dataSetName == NULL)
|
|
|
|
|
{
|
|
|
|
|
matchingReport->dataSetNameSize = 0;
|
|
|
|
|
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: failed to allocate memory\n");
|
|
|
|
@ -528,7 +560,8 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
|
|
|
|
|
|
|
|
|
|
StringUtils_copyStringMax(matchingReport->dataSetName, dataSetNameSize + 1, dataSetNameStr);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: report DatSet name too large (%i)\n", dataSetNameSize);
|
|
|
|
|
|
|
|
|
@ -542,10 +575,12 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
|
|
|
|
|
printf("IED_CLIENT: Found enabled report!\n");
|
|
|
|
|
|
|
|
|
|
/* check bufOvfl */
|
|
|
|
|
if (MmsValue_getBitStringBit(optFlds, 6) == true) {
|
|
|
|
|
if (MmsValue_getBitStringBit(optFlds, 6) == true)
|
|
|
|
|
{
|
|
|
|
|
MmsValue* bufOverflow = MmsValue_getElement(value, inclusionIndex);
|
|
|
|
|
|
|
|
|
|
if ((bufOverflow == NULL) || (MmsValue_getType(bufOverflow) != MMS_BOOLEAN)) {
|
|
|
|
|
if ((bufOverflow == NULL) || (MmsValue_getType(bufOverflow) != MMS_BOOLEAN))
|
|
|
|
|
{
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: received malformed report (BufOvfl)\n");
|
|
|
|
|
|
|
|
|
@ -559,24 +594,28 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* check for entryId */
|
|
|
|
|
if (MmsValue_getBitStringBit(optFlds, 7) == true) {
|
|
|
|
|
if (MmsValue_getBitStringBit(optFlds, 7) == true)
|
|
|
|
|
{
|
|
|
|
|
MmsValue* entryId = MmsValue_getElement(value, inclusionIndex);
|
|
|
|
|
|
|
|
|
|
if ((entryId == NULL) || (MmsValue_getType(entryId) != MMS_OCTET_STRING)) {
|
|
|
|
|
if ((entryId == NULL) || (MmsValue_getType(entryId) != MMS_OCTET_STRING))
|
|
|
|
|
{
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: received malformed report (entryID)\n");
|
|
|
|
|
|
|
|
|
|
goto exit_function;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (matchingReport->entryId != NULL) {
|
|
|
|
|
|
|
|
|
|
if (!MmsValue_update(matchingReport->entryId, entryId)) {
|
|
|
|
|
if (matchingReport->entryId != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (!MmsValue_update(matchingReport->entryId, entryId))
|
|
|
|
|
{
|
|
|
|
|
MmsValue_delete(matchingReport->entryId);
|
|
|
|
|
matchingReport->entryId = MmsValue_clone(entryId);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
matchingReport->entryId = MmsValue_clone(entryId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -584,10 +623,12 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* check for confRev */
|
|
|
|
|
if (MmsValue_getBitStringBit(optFlds, 8) == true) {
|
|
|
|
|
if (MmsValue_getBitStringBit(optFlds, 8) == true)
|
|
|
|
|
{
|
|
|
|
|
MmsValue* confRev = MmsValue_getElement(value, inclusionIndex);
|
|
|
|
|
|
|
|
|
|
if ((confRev == NULL) || (MmsValue_getType(confRev) != MMS_UNSIGNED)) {
|
|
|
|
|
if ((confRev == NULL) || (MmsValue_getType(confRev) != MMS_UNSIGNED))
|
|
|
|
|
{
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: received malformed report (confRev)\n");
|
|
|
|
|
|
|
|
|
@ -601,46 +642,53 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* handle segmentation fields (check ReportedOptFlds.segmentation) */
|
|
|
|
|
if (MmsValue_getBitStringBit(optFlds, 9) == true) {
|
|
|
|
|
|
|
|
|
|
if (MmsValue_getBitStringBit(optFlds, 9) == true)
|
|
|
|
|
{
|
|
|
|
|
MmsValue* subSeqNum = MmsValue_getElement(value, inclusionIndex);
|
|
|
|
|
inclusionIndex++;
|
|
|
|
|
|
|
|
|
|
if ((subSeqNum == NULL) || (MmsValue_getType(subSeqNum) != MMS_UNSIGNED)) {
|
|
|
|
|
if ((subSeqNum == NULL) || (MmsValue_getType(subSeqNum) != MMS_UNSIGNED))
|
|
|
|
|
{
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: received malformed report (SubSeqNum)\n");
|
|
|
|
|
|
|
|
|
|
goto exit_function;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
matchingReport->subSeqNum = (uint16_t) MmsValue_toUint32(subSeqNum);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
matchingReport->subSeqNum = (uint16_t)MmsValue_toUint32(subSeqNum);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MmsValue* moreSegmentsFollow = MmsValue_getElement(value, inclusionIndex);
|
|
|
|
|
inclusionIndex++;
|
|
|
|
|
|
|
|
|
|
if ((moreSegmentsFollow == NULL) || (MmsValue_getType(moreSegmentsFollow) != MMS_BOOLEAN)) {
|
|
|
|
|
if ((subSeqNum == NULL) || (MmsValue_getType(subSeqNum) != MMS_UNSIGNED)) {
|
|
|
|
|
if ((moreSegmentsFollow == NULL) || (MmsValue_getType(moreSegmentsFollow) != MMS_BOOLEAN))
|
|
|
|
|
{
|
|
|
|
|
if ((subSeqNum == NULL) || (MmsValue_getType(subSeqNum) != MMS_UNSIGNED))
|
|
|
|
|
{
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: received malformed report (MoreSegmentsFollow)\n");
|
|
|
|
|
|
|
|
|
|
goto exit_function;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
matchingReport->moreSegementsFollow = MmsValue_getBoolean(moreSegmentsFollow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
matchingReport->hasSequenceNumber = true;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
matchingReport->subSeqNum = 0;
|
|
|
|
|
matchingReport->moreSegementsFollow = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MmsValue* inclusion = MmsValue_getElement(value, inclusionIndex);
|
|
|
|
|
|
|
|
|
|
if ((inclusion == NULL) || (MmsValue_getType(inclusion) != MMS_BIT_STRING)) {
|
|
|
|
|
if ((inclusion == NULL) || (MmsValue_getType(inclusion) != MMS_BIT_STRING))
|
|
|
|
|
{
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: received malformed report (inclusion)\n");
|
|
|
|
|
|
|
|
|
@ -649,11 +697,14 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
|
|
|
|
|
|
|
|
|
|
int dataSetSize = MmsValue_getBitStringSize(inclusion);
|
|
|
|
|
|
|
|
|
|
if (matchingReport->dataSetSize == -1) {
|
|
|
|
|
if (matchingReport->dataSetSize == -1)
|
|
|
|
|
{
|
|
|
|
|
matchingReport->dataSetSize = dataSetSize;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (dataSetSize != matchingReport->dataSetSize) {
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (dataSetSize != matchingReport->dataSetSize)
|
|
|
|
|
{
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: received malformed report (inclusion has no plausible size)\n");
|
|
|
|
|
|
|
|
|
@ -664,14 +715,13 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
|
|
|
|
|
int includedElements = MmsValue_getNumberOfSetBits(inclusion);
|
|
|
|
|
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: Report includes %i data set elements of %i\n", includedElements,
|
|
|
|
|
dataSetSize);
|
|
|
|
|
printf("IED_CLIENT: Report includes %i data set elements of %i\n", includedElements, dataSetSize);
|
|
|
|
|
|
|
|
|
|
int valueIndex = inclusionIndex + 1;
|
|
|
|
|
|
|
|
|
|
/* parse data-references if required */
|
|
|
|
|
if (MmsValue_getBitStringBit(optFlds, 5) == true) {
|
|
|
|
|
|
|
|
|
|
if (MmsValue_getBitStringBit(optFlds, 5) == true)
|
|
|
|
|
{
|
|
|
|
|
if (matchingReport->dataReferences == NULL)
|
|
|
|
|
matchingReport->dataReferences = MmsValue_createEmptyArray(dataSetSize);
|
|
|
|
|
|
|
|
|
@ -679,19 +729,23 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
|
|
|
|
|
|
|
|
|
|
int elementIndex;
|
|
|
|
|
|
|
|
|
|
for (elementIndex = 0; elementIndex < dataSetSize; elementIndex++) {
|
|
|
|
|
if (MmsValue_getBitStringBit(inclusion, elementIndex) == true) {
|
|
|
|
|
for (elementIndex = 0; elementIndex < dataSetSize; elementIndex++)
|
|
|
|
|
{
|
|
|
|
|
if (MmsValue_getBitStringBit(inclusion, elementIndex) == true)
|
|
|
|
|
{
|
|
|
|
|
MmsValue* dataSetElement = MmsValue_getElement(matchingReport->dataReferences, elementIndex);
|
|
|
|
|
|
|
|
|
|
if (dataSetElement == NULL) {
|
|
|
|
|
|
|
|
|
|
if (dataSetElement == NULL)
|
|
|
|
|
{
|
|
|
|
|
MmsValue* dataRefValue = MmsValue_getElement(value, valueIndex);
|
|
|
|
|
|
|
|
|
|
if ((dataRefValue == NULL) || (MmsValue_getType(dataRefValue) != MMS_VISIBLE_STRING)) {
|
|
|
|
|
if ((dataRefValue == NULL) || (MmsValue_getType(dataRefValue) != MMS_VISIBLE_STRING))
|
|
|
|
|
{
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: report contains invalid data reference\n");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dataSetElement = MmsValue_clone(dataRefValue);
|
|
|
|
|
|
|
|
|
|
MmsValue_setElement(matchingReport->dataReferences, elementIndex, dataSetElement);
|
|
|
|
@ -705,16 +759,23 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
|
|
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (matchingReport->dataSetValues == NULL) {
|
|
|
|
|
if (matchingReport->dataSetValues == NULL)
|
|
|
|
|
{
|
|
|
|
|
matchingReport->dataSetValues = MmsValue_createEmptyArray(dataSetSize);
|
|
|
|
|
matchingReport->reasonForInclusion = (ReasonForInclusion*)
|
|
|
|
|
GLOBAL_MALLOC(sizeof(ReasonForInclusion) * dataSetSize);
|
|
|
|
|
|
|
|
|
|
int elementIndex;
|
|
|
|
|
matchingReport->reasonForInclusion =
|
|
|
|
|
(ReasonForInclusion*)GLOBAL_MALLOC(sizeof(ReasonForInclusion) * dataSetSize);
|
|
|
|
|
|
|
|
|
|
for (elementIndex = 0; elementIndex < dataSetSize; elementIndex++)
|
|
|
|
|
matchingReport->reasonForInclusion[elementIndex] = IEC61850_REASON_NOT_INCLUDED;
|
|
|
|
|
if (matchingReport->reasonForInclusion)
|
|
|
|
|
{
|
|
|
|
|
int elementIndex;
|
|
|
|
|
|
|
|
|
|
for (elementIndex = 0; elementIndex < dataSetSize; elementIndex++)
|
|
|
|
|
matchingReport->reasonForInclusion[elementIndex] = IEC61850_REASON_NOT_INCLUDED;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
goto exit_function;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MmsValue* dataSetValues = matchingReport->dataSetValues;
|
|
|
|
@ -724,14 +785,16 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
|
|
|
|
|
if (hasReasonForInclusion)
|
|
|
|
|
matchingReport->hasReasonForInclusion = true;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < dataSetSize; i++) {
|
|
|
|
|
if (MmsValue_getBitStringBit(inclusion, i) == true) {
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < dataSetSize; i++)
|
|
|
|
|
{
|
|
|
|
|
if (MmsValue_getBitStringBit(inclusion, i) == true)
|
|
|
|
|
{
|
|
|
|
|
MmsValue* dataSetElement = MmsValue_getElement(dataSetValues, i);
|
|
|
|
|
|
|
|
|
|
MmsValue* newElementValue = MmsValue_getElement(value, valueIndex);
|
|
|
|
|
|
|
|
|
|
if (newElementValue == NULL) {
|
|
|
|
|
if (newElementValue == NULL)
|
|
|
|
|
{
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: report is missing expected element value\n");
|
|
|
|
|
|
|
|
|
@ -746,10 +809,12 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: update element value type: %i\n", MmsValue_getType(newElementValue));
|
|
|
|
|
|
|
|
|
|
if (hasReasonForInclusion) {
|
|
|
|
|
if (hasReasonForInclusion)
|
|
|
|
|
{
|
|
|
|
|
MmsValue* reasonForInclusion = MmsValue_getElement(value, includedElements + valueIndex);
|
|
|
|
|
|
|
|
|
|
if ((reasonForInclusion == NULL) || (MmsValue_getType(reasonForInclusion) != MMS_BIT_STRING)) {
|
|
|
|
|
if ((reasonForInclusion == NULL) || (MmsValue_getType(reasonForInclusion) != MMS_BIT_STRING))
|
|
|
|
|
{
|
|
|
|
|
if (DEBUG_IED_CLIENT)
|
|
|
|
|
printf("IED_CLIENT: report contains invalid reason-for-inclusion\n");
|
|
|
|
|
|
|
|
|
@ -759,7 +824,7 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
|
|
|
|
|
matchingReport->reasonForInclusion[i] = IEC61850_REASON_NOT_INCLUDED;
|
|
|
|
|
|
|
|
|
|
if (MmsValue_getBitStringBit(reasonForInclusion, 1) == true)
|
|
|
|
|
matchingReport->reasonForInclusion[i] |= (ReasonForInclusion) IEC61850_REASON_DATA_CHANGE;
|
|
|
|
|
matchingReport->reasonForInclusion[i] |= (ReasonForInclusion)IEC61850_REASON_DATA_CHANGE;
|
|
|
|
|
if (MmsValue_getBitStringBit(reasonForInclusion, 2) == true)
|
|
|
|
|
matchingReport->reasonForInclusion[i] |= IEC61850_REASON_QUALITY_CHANGE;
|
|
|
|
|
if (MmsValue_getBitStringBit(reasonForInclusion, 3) == true)
|
|
|
|
@ -769,18 +834,20 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
|
|
|
|
|
if (MmsValue_getBitStringBit(reasonForInclusion, 5) == true)
|
|
|
|
|
matchingReport->reasonForInclusion[i] |= IEC61850_REASON_GI;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
matchingReport->reasonForInclusion[i] = IEC61850_REASON_UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
valueIndex++;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
matchingReport->reasonForInclusion[i] = IEC61850_REASON_NOT_INCLUDED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (matchingReport->callback != NULL)
|
|
|
|
|
|
|
|
|
|
if (matchingReport->callback)
|
|
|
|
|
matchingReport->callback(matchingReport->callbackParameter, matchingReport);
|
|
|
|
|
|
|
|
|
|
exit_function:
|
|
|
|
@ -789,4 +856,3 @@ exit_function:
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|