- IEC 61850 server: prevent sending reports when data model is locked (updated)

pull/68/head
Michael Zillgith 7 years ago
parent 2c9a4bb088
commit 9a8415b3e6

@ -133,8 +133,6 @@ main(int argc, char** argv)
float an3 = sinf(t + 2.f); float an3 = sinf(t + 2.f);
float an4 = sinf(t + 3.f); float an4 = sinf(t + 3.f);
IedServer_lockDataModel(iedServer);
Timestamp iecTimestamp; Timestamp iecTimestamp;
Timestamp_clearFlags(&iecTimestamp); Timestamp_clearFlags(&iecTimestamp);
@ -145,6 +143,8 @@ main(int argc, char** argv)
if (((int) t % 2) == 0) if (((int) t % 2) == 0)
Timestamp_setClockNotSynchronized(&iecTimestamp, true); Timestamp_setClockNotSynchronized(&iecTimestamp, true);
IedServer_lockDataModel(iedServer);
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_t, &iecTimestamp); IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_t, &iecTimestamp);
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f, an1); IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f, an1);

@ -338,8 +338,10 @@ IedServer_setConnectionIndicationHandler(IedServer self, IedConnectionIndication
/** /**
* \brief Lock the MMS server data model. * \brief Lock the data model for data update.
* *
* This function should be called before the data model is updated.
* After updating the data model the function \ref IedServer_unlockDataModel should be called.
* Client requests will be postponed until the lock is removed. * Client requests will be postponed until the lock is removed.
* *
* NOTE: This method should never be called inside of a library callback function. In the context of * NOTE: This method should never be called inside of a library callback function. In the context of
@ -352,7 +354,7 @@ void
IedServer_lockDataModel(IedServer self); IedServer_lockDataModel(IedServer self);
/** /**
* \brief Unlock the MMS server data model and process pending client requests. * \brief Unlock the data model and process pending client requests.
* *
* NOTE: This method should never be called inside of a library callback function. In the context of * NOTE: This method should never be called inside of a library callback function. In the context of
* a library callback the data model is always already locked! * a library callback the data model is always already locked!

@ -29,10 +29,11 @@
#include "control.h" #include "control.h"
typedef enum { typedef enum {
REPORT_CONTROL_NONE, REPORT_CONTROL_NONE = 0,
REPORT_CONTROL_VALUE_UPDATE, REPORT_CONTROL_VALUE_UPDATE = 1,
REPORT_CONTROL_VALUE_CHANGED, REPORT_CONTROL_VALUE_CHANGED = 2,
REPORT_CONTROL_QUALITY_CHANGED REPORT_CONTROL_QUALITY_CHANGED = 4,
REPORT_CONTROL_NOT_UPDATED = 8
} ReportInclusionFlag; } ReportInclusionFlag;
typedef enum { typedef enum {

@ -1,7 +1,7 @@
/* /*
* mms_mapping_internal.h * mms_mapping_internal.h
* *
* Copyright 2013-2016 Michael Zillgith * Copyright 2013-2018 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -64,6 +64,9 @@ struct sMmsMapping {
Thread reportWorkerThread; Thread reportWorkerThread;
#endif #endif
/* flag indicates if data model is locked --> prevents reports to be sent */
bool isModelLocked;
IedServer iedServer; IedServer iedServer;
IedConnectionIndicationHandler connectionIndicationHandler; IedConnectionIndicationHandler connectionIndicationHandler;

@ -103,7 +103,7 @@ void
ReportControl_destroy(ReportControl* self); ReportControl_destroy(ReportControl* self);
void void
ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, ReportInclusionFlag flag); ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, ReportInclusionFlag flag, bool modelLocked);
MmsValue* MmsValue*
ReportControl_getRCBValue(ReportControl* rc, char* elementName); ReportControl_getRCBValue(ReportControl* rc, char* elementName);
@ -123,9 +123,14 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
void void
Reporting_activateBufferedReports(MmsMapping* self); Reporting_activateBufferedReports(MmsMapping* self);
/* periodic check if reports have to be sent */
void void
Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs); Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs);
/* check if report have to be sent after data model update */
void
Reporting_processReportEventsAfterUnlock(MmsMapping* self);
void void
Reporting_deactivateReportsForConnection(MmsMapping* self, MmsServerConnection connection); Reporting_deactivateReportsForConnection(MmsMapping* self, MmsServerConnection connection);

@ -649,11 +649,18 @@ void
IedServer_lockDataModel(IedServer self) IedServer_lockDataModel(IedServer self)
{ {
MmsServer_lockModel(self->mmsServer); MmsServer_lockModel(self->mmsServer);
self->mmsMapping->isModelLocked = true;
} }
void void
IedServer_unlockDataModel(IedServer self) IedServer_unlockDataModel(IedServer self)
{ {
/* check if reports have to be sent! */
Reporting_processReportEventsAfterUnlock(self->mmsMapping);
self->mmsMapping->isModelLocked = false;
MmsServer_unlockModel(self->mmsServer); MmsServer_unlockModel(self->mmsServer);
} }

@ -2757,7 +2757,10 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, ReportInclu
} }
if (DataSet_isMemberValue(rc->dataSet, value, &index)) { if (DataSet_isMemberValue(rc->dataSet, value, &index)) {
ReportControl_valueUpdated(rc, index, flag);
bool modelLocked = self->isModelLocked;
ReportControl_valueUpdated(rc, index, flag, modelLocked);
} }
} }
} }

@ -2513,37 +2513,96 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs)
void void
Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs) Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs)
{
if (self->isModelLocked == false) {
LinkedList element = self->reportControls;
while ((element = LinkedList_getNext(element)) != NULL ) {
ReportControl* rc = (ReportControl*) element->data;
ReportControl_lockNotify(rc);
processEventsForReport(rc, currentTimeInMs);
ReportControl_unlockNotify(rc);
}
}
}
static inline void
copySingleValueToReportBuffer(ReportControl* self, int dataSetEntryIndex)
{
if (self->bufferedDataSetValues[dataSetEntryIndex] == NULL)
self->bufferedDataSetValues[dataSetEntryIndex] = MmsValue_clone(self->valueReferences[dataSetEntryIndex]);
else
MmsValue_update(self->bufferedDataSetValues[dataSetEntryIndex], self->valueReferences[dataSetEntryIndex]);
}
static void
copyValuesToReportBuffer(ReportControl* self)
{
int i;
for (i = 0; i < self->dataSet->elementCount; i++) {
if (self->inclusionFlags[i] & REPORT_CONTROL_NOT_UPDATED) {
copySingleValueToReportBuffer(self, i);
/* clear not-updated flag */
self->inclusionFlags[i] &= (~REPORT_CONTROL_NOT_UPDATED);
}
}
}
/* check if report have to be sent after data model update */
void
Reporting_processReportEventsAfterUnlock(MmsMapping* self)
{ {
LinkedList element = self->reportControls; LinkedList element = self->reportControls;
uint64_t currentTime = Hal_getTimeInMs();
while ((element = LinkedList_getNext(element)) != NULL ) { while ((element = LinkedList_getNext(element)) != NULL ) {
ReportControl* rc = (ReportControl*) element->data; ReportControl* rc = (ReportControl*) element->data;
ReportControl_lockNotify(rc); ReportControl_lockNotify(rc);
processEventsForReport(rc, currentTimeInMs); if ((rc->enabled) || (rc->isBuffering)) {
copyValuesToReportBuffer(rc);
processEventsForReport(rc, currentTime);
}
ReportControl_unlockNotify(rc); ReportControl_unlockNotify(rc);
} }
} }
void void
ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, ReportInclusionFlag flag) ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, ReportInclusionFlag flag, bool modelLocked)
{ {
ReportControl_lockNotify(self); ReportControl_lockNotify(self);
if (self->inclusionFlags[dataSetEntryIndex] != 0) { /* report for this data set entry is already pending (bypass BufTm) */ if (self->inclusionFlags[dataSetEntryIndex] != 0) { /* report for this data set entry is already pending (bypass BufTm) */
self->reportTime = Hal_getTimeInMs(); self->reportTime = Hal_getTimeInMs();
if (modelLocked) {
/* buffer all relevant values */
copyValuesToReportBuffer(self);
}
processEventsForReport(self, self->reportTime); processEventsForReport(self, self->reportTime);
} }
self->inclusionFlags[dataSetEntryIndex] = flag; if (modelLocked) {
/* set flag to update values when report is to be sent or data model unlocked */
self->inclusionFlags[dataSetEntryIndex] = flag | REPORT_CONTROL_NOT_UPDATED;
/* buffer value for report */ }
if (self->bufferedDataSetValues[dataSetEntryIndex] == NULL) else {
self->bufferedDataSetValues[dataSetEntryIndex] = MmsValue_clone(self->valueReferences[dataSetEntryIndex]); self->inclusionFlags[dataSetEntryIndex] = flag;
else
MmsValue_update(self->bufferedDataSetValues[dataSetEntryIndex], self->valueReferences[dataSetEntryIndex]); /* buffer value for report */
copySingleValueToReportBuffer(self, dataSetEntryIndex);
}
if (self->triggered == false) { if (self->triggered == false) {
uint64_t currentTime = Hal_getTimeInMs(); uint64_t currentTime = Hal_getTimeInMs();

Loading…
Cancel
Save