From 9a8415b3e64017037886a257308c7c9dcfba83a7 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 26 May 2018 14:29:10 +0200 Subject: [PATCH] - IEC 61850 server: prevent sending reports when data model is locked (updated) --- .../server_example_basic_io.c | 4 +- src/iec61850/inc/iec61850_server.h | 6 +- src/iec61850/inc_private/mms_mapping.h | 9 ++- .../inc_private/mms_mapping_internal.h | 5 +- src/iec61850/inc_private/reporting.h | 7 +- src/iec61850/server/impl/ied_server.c | 7 ++ src/iec61850/server/mms_mapping/mms_mapping.c | 5 +- src/iec61850/server/mms_mapping/reporting.c | 75 +++++++++++++++++-- 8 files changed, 99 insertions(+), 19 deletions(-) diff --git a/examples/server_example_basic_io/server_example_basic_io.c b/examples/server_example_basic_io/server_example_basic_io.c index 30faf6e4..14c44e32 100644 --- a/examples/server_example_basic_io/server_example_basic_io.c +++ b/examples/server_example_basic_io/server_example_basic_io.c @@ -133,8 +133,6 @@ main(int argc, char** argv) float an3 = sinf(t + 2.f); float an4 = sinf(t + 3.f); - IedServer_lockDataModel(iedServer); - Timestamp iecTimestamp; Timestamp_clearFlags(&iecTimestamp); @@ -145,6 +143,8 @@ main(int argc, char** argv) if (((int) t % 2) == 0) Timestamp_setClockNotSynchronized(&iecTimestamp, true); + IedServer_lockDataModel(iedServer); + IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_t, &iecTimestamp); IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f, an1); diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index 8fdd91ee..eadafaef 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -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. * * 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); /** - * \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 * a library callback the data model is always already locked! diff --git a/src/iec61850/inc_private/mms_mapping.h b/src/iec61850/inc_private/mms_mapping.h index cff0969f..ec6240ad 100644 --- a/src/iec61850/inc_private/mms_mapping.h +++ b/src/iec61850/inc_private/mms_mapping.h @@ -29,10 +29,11 @@ #include "control.h" typedef enum { - REPORT_CONTROL_NONE, - REPORT_CONTROL_VALUE_UPDATE, - REPORT_CONTROL_VALUE_CHANGED, - REPORT_CONTROL_QUALITY_CHANGED + REPORT_CONTROL_NONE = 0, + REPORT_CONTROL_VALUE_UPDATE = 1, + REPORT_CONTROL_VALUE_CHANGED = 2, + REPORT_CONTROL_QUALITY_CHANGED = 4, + REPORT_CONTROL_NOT_UPDATED = 8 } ReportInclusionFlag; typedef enum { diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h index 52b7cf2d..f136a39a 100644 --- a/src/iec61850/inc_private/mms_mapping_internal.h +++ b/src/iec61850/inc_private/mms_mapping_internal.h @@ -1,7 +1,7 @@ /* * mms_mapping_internal.h * - * Copyright 2013-2016 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -64,6 +64,9 @@ struct sMmsMapping { Thread reportWorkerThread; #endif + /* flag indicates if data model is locked --> prevents reports to be sent */ + bool isModelLocked; + IedServer iedServer; IedConnectionIndicationHandler connectionIndicationHandler; diff --git a/src/iec61850/inc_private/reporting.h b/src/iec61850/inc_private/reporting.h index e484ad1e..dce535a4 100644 --- a/src/iec61850/inc_private/reporting.h +++ b/src/iec61850/inc_private/reporting.h @@ -103,7 +103,7 @@ void ReportControl_destroy(ReportControl* self); void -ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, ReportInclusionFlag flag); +ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, ReportInclusionFlag flag, bool modelLocked); MmsValue* ReportControl_getRCBValue(ReportControl* rc, char* elementName); @@ -123,9 +123,14 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme void Reporting_activateBufferedReports(MmsMapping* self); +/* periodic check if reports have to be sent */ void Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs); +/* check if report have to be sent after data model update */ +void +Reporting_processReportEventsAfterUnlock(MmsMapping* self); + void Reporting_deactivateReportsForConnection(MmsMapping* self, MmsServerConnection connection); diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index 1c47e085..c219b9a0 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -649,11 +649,18 @@ void IedServer_lockDataModel(IedServer self) { MmsServer_lockModel(self->mmsServer); + + self->mmsMapping->isModelLocked = true; } void IedServer_unlockDataModel(IedServer self) { + /* check if reports have to be sent! */ + Reporting_processReportEventsAfterUnlock(self->mmsMapping); + + self->mmsMapping->isModelLocked = false; + MmsServer_unlockModel(self->mmsServer); } diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 5e4b2fdd..7cf2225b 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -2757,7 +2757,10 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, ReportInclu } if (DataSet_isMemberValue(rc->dataSet, value, &index)) { - ReportControl_valueUpdated(rc, index, flag); + + bool modelLocked = self->isModelLocked; + + ReportControl_valueUpdated(rc, index, flag, modelLocked); } } } diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index 21d2e91a..642d06fc 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -2513,37 +2513,96 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs) void 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; + uint64_t currentTime = Hal_getTimeInMs(); + while ((element = LinkedList_getNext(element)) != NULL ) { ReportControl* rc = (ReportControl*) element->data; ReportControl_lockNotify(rc); - processEventsForReport(rc, currentTimeInMs); + if ((rc->enabled) || (rc->isBuffering)) { + copyValuesToReportBuffer(rc); + + processEventsForReport(rc, currentTime); + } ReportControl_unlockNotify(rc); } } void -ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, ReportInclusionFlag flag) +ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, ReportInclusionFlag flag, bool modelLocked) { ReportControl_lockNotify(self); if (self->inclusionFlags[dataSetEntryIndex] != 0) { /* report for this data set entry is already pending (bypass BufTm) */ self->reportTime = Hal_getTimeInMs(); + + if (modelLocked) { + /* buffer all relevant values */ + copyValuesToReportBuffer(self); + } + 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) - self->bufferedDataSetValues[dataSetEntryIndex] = MmsValue_clone(self->valueReferences[dataSetEntryIndex]); - else - MmsValue_update(self->bufferedDataSetValues[dataSetEntryIndex], self->valueReferences[dataSetEntryIndex]); + } + else { + self->inclusionFlags[dataSetEntryIndex] = flag; + + /* buffer value for report */ + copySingleValueToReportBuffer(self, dataSetEntryIndex); + } if (self->triggered == false) { uint64_t currentTime = Hal_getTimeInMs();