diff --git a/examples/server_example_basic_io/simpleIO_direct_control.cid b/examples/server_example_basic_io/simpleIO_direct_control.cid index cb763780..e4845770 100644 --- a/examples/server_example_basic_io/simpleIO_direct_control.cid +++ b/examples/server_example_basic_io/simpleIO_direct_control.cid @@ -68,7 +68,13 @@ - + + + + + + + diff --git a/examples/server_example_basic_io/static_model.c b/examples/server_example_basic_io/static_model.c index 63ef37bb..ead912c0 100644 --- a/examples/server_example_basic_io/static_model.c +++ b/examples/server_example_basic_io/static_model.c @@ -2008,14 +2008,16 @@ extern ReportControlBlock iedModel_GenericIO_LLN0_report3; extern ReportControlBlock iedModel_GenericIO_LLN0_report4; extern ReportControlBlock iedModel_GenericIO_LLN0_report5; extern ReportControlBlock iedModel_GenericIO_LLN0_report6; - -ReportControlBlock iedModel_GenericIO_LLN0_report0 = {&iedModel_GenericIO_LLN0, "EventsRCB01", "Events1", false, "Events", 4294967295, 24, 175, 50, 1000, &iedModel_GenericIO_LLN0_report1}; -ReportControlBlock iedModel_GenericIO_LLN0_report1 = {&iedModel_GenericIO_LLN0, "EventsIndexed01", "Events2", false, "Events", 1, 24, 175, 50, 1000, &iedModel_GenericIO_LLN0_report2}; -ReportControlBlock iedModel_GenericIO_LLN0_report2 = {&iedModel_GenericIO_LLN0, "EventsIndexed02", "Events2", false, "Events", 1, 24, 175, 50, 1000, &iedModel_GenericIO_LLN0_report3}; -ReportControlBlock iedModel_GenericIO_LLN0_report3 = {&iedModel_GenericIO_LLN0, "EventsIndexed03", "Events2", false, "Events", 1, 24, 175, 50, 1000, &iedModel_GenericIO_LLN0_report4}; -ReportControlBlock iedModel_GenericIO_LLN0_report4 = {&iedModel_GenericIO_LLN0, "Measurements01", "Measurements", true, "Measurements", 1, 16, 239, 50, 1000, &iedModel_GenericIO_LLN0_report5}; -ReportControlBlock iedModel_GenericIO_LLN0_report5 = {&iedModel_GenericIO_LLN0, "Measurements02", "Measurements", true, "Measurements", 1, 16, 239, 50, 1000, &iedModel_GenericIO_LLN0_report6}; -ReportControlBlock iedModel_GenericIO_LLN0_report6 = {&iedModel_GenericIO_LLN0, "Measurements03", "Measurements", true, "Measurements", 1, 16, 239, 50, 1000, NULL}; +extern ReportControlBlock iedModel_GenericIO_LLN0_report7; + +ReportControlBlock iedModel_GenericIO_LLN0_report0 = {&iedModel_GenericIO_LLN0, "EventsRCB01", "Events1", false, "Events", 1, 24, 175, 50, 1000, &iedModel_GenericIO_LLN0_report1}; +ReportControlBlock iedModel_GenericIO_LLN0_report1 = {&iedModel_GenericIO_LLN0, "EventsBRCB01", "Events2", true, "Events", 1, 24, 175, 50, 1000, &iedModel_GenericIO_LLN0_report2}; +ReportControlBlock iedModel_GenericIO_LLN0_report2 = {&iedModel_GenericIO_LLN0, "EventsIndexed01", "Events2", false, "Events", 1, 24, 175, 50, 1000, &iedModel_GenericIO_LLN0_report3}; +ReportControlBlock iedModel_GenericIO_LLN0_report3 = {&iedModel_GenericIO_LLN0, "EventsIndexed02", "Events2", false, "Events", 1, 24, 175, 50, 1000, &iedModel_GenericIO_LLN0_report4}; +ReportControlBlock iedModel_GenericIO_LLN0_report4 = {&iedModel_GenericIO_LLN0, "EventsIndexed03", "Events2", false, "Events", 1, 24, 175, 50, 1000, &iedModel_GenericIO_LLN0_report5}; +ReportControlBlock iedModel_GenericIO_LLN0_report5 = {&iedModel_GenericIO_LLN0, "Measurements01", "Measurements", true, "Measurements", 1, 16, 239, 50, 1000, &iedModel_GenericIO_LLN0_report6}; +ReportControlBlock iedModel_GenericIO_LLN0_report6 = {&iedModel_GenericIO_LLN0, "Measurements02", "Measurements", true, "Measurements", 1, 16, 239, 50, 1000, &iedModel_GenericIO_LLN0_report7}; +ReportControlBlock iedModel_GenericIO_LLN0_report7 = {&iedModel_GenericIO_LLN0, "Measurements03", "Measurements", true, "Measurements", 1, 16, 239, 50, 1000, NULL}; diff --git a/examples/server_example_logging/CMakeLists.txt b/examples/server_example_logging/CMakeLists.txt index 598cfaef..0630a48c 100644 --- a/examples/server_example_logging/CMakeLists.txt +++ b/examples/server_example_logging/CMakeLists.txt @@ -10,11 +10,11 @@ include_directories( set(server_example_SRCS server_example_logging.c static_model.c - ${CMAKE_SOURCE_DIR}/src/logging/drivers/sqlite/log_storage_sqlite.c + ${CMAKE_CURRENT_LIST_DIR}/../../src/logging/drivers/sqlite/log_storage_sqlite.c ) set(sqlite_SRCS - ${CMAKE_SOURCE_DIR}/third_party/sqlite/sqlite3.c + ${CMAKE_CURRENT_LIST_DIR}/../../third_party/sqlite/sqlite3.c ) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION") diff --git a/src/iec61850/inc_private/reporting.h b/src/iec61850/inc_private/reporting.h index 540078b1..d6b5102d 100644 --- a/src/iec61850/inc_private/reporting.h +++ b/src/iec61850/inc_private/reporting.h @@ -42,6 +42,8 @@ typedef struct { ReportBufferEntry* lastEnqueuedReport; ReportBufferEntry* nextToTransmit; bool isOverflow; /* true if overflow condition is active */ + + Semaphore lock; /* protect access to report buffer */ } ReportBuffer; typedef struct { @@ -53,6 +55,7 @@ typedef struct { MmsValue* rcbValues; MmsValue* inclusionField; MmsValue* confRev; + DataSet* dataSet; bool isDynamicDataSet; bool enabled; @@ -76,6 +79,12 @@ typedef struct { int triggerOps; + /* information for segmented reporting */ + bool segmented; /* indicates that a segmented report is in process */ + int startIndexForNextSegment; /* start data set index for the next report segment */ + MmsValue* subSeqVal; /* sub sequence value for segmented reporting */ + uint64_t segmentedReportTimestamp; /* time stamp used for all report segments */ + #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore createNotificationsMutex; /* { covered by mutex } */ #endif diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index 129092b3..fae76e3d 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -1,7 +1,7 @@ /* * reporting.c * - * Copyright 2013-2018 Michael Zillgith + * Copyright 2013-2019 Michael Zillgith * * This file is part of libIEC61850. * @@ -29,9 +29,11 @@ #include "simple_allocator.h" #include "mem_alloc_linked_list.h" +#include "ber_encoder.h" #include "mms_mapping_internal.h" #include "mms_value_internal.h" +#include "mms_server_internal.h" #include "conversions.h" #include "reporting.h" #include "ied_server_private.h" @@ -66,6 +68,10 @@ ReportBuffer_create(int bufferSize) GLOBAL_FREEMEM(self); self = NULL; } + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + self->lock = Semaphore_create(1); +#endif } return self; @@ -75,6 +81,11 @@ static void ReportBuffer_destroy(ReportBuffer* self) { GLOBAL_FREEMEM(self->memoryBlock); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_destroy(self->lock); +#endif + GLOBAL_FREEMEM(self); } @@ -87,6 +98,9 @@ ReportControl_create(bool buffered, LogicalNode* parentLN, int reportBufferSize, self->parentLN = parentLN; self->rcbValues = NULL; self->confRev = NULL; + self->subSeqVal = MmsValue_newUnsigned(16); + self->segmented = false; + self->startIndexForNextSegment = 0; self->enabled = false; self->reserved = false; self->buffered = buffered; @@ -196,6 +210,8 @@ ReportControl_destroy(ReportControl* self) if (self->buffered == false) MmsValue_delete(self->timeOfEntry); + MmsValue_delete(self->subSeqVal); + deleteDataSetValuesShadowBuffer(self); if (self->isDynamicDataSet) { @@ -308,63 +324,235 @@ updateTimeOfEntry(ReportControl* self, uint64_t currentTime) MmsValue_setBinaryTime(timeOfEntry, currentTime); } -static void -sendReport(ReportControl* self, bool isIntegrity, bool isGI) +static DataSetEntry* +getDataSetEntryWithIndex(DataSetEntry* dataSet, int index) { - updateTimeOfEntry(self, Hal_getTimeInMs()); + int i = 0; - LinkedList reportElements = LinkedList_create(); + while (dataSet) { + if (i == index) + return dataSet; + + i++; + + dataSet = dataSet->sibling; + } + + return NULL; +} + +static bool +sendReportSegment(ReportControl* self, bool isIntegrity, bool isGI) +{ + int maxMmsPduSize = MmsServerConnection_getMaxMmsPduSize(self->clientConnection); + int estimatedSegmentSize = 19; /* maximum size of header information (header can have 13-19 byte) */ + estimatedSegmentSize += 8; /* reserve space for more-segments-follow (3 byte) and sub-seq-num (3-5 byte) */ - LinkedList deletableElements = LinkedList_create(); + bool segmented = self->segmented; + bool moreFollows = false; + + bool hasSeqNum = false; + bool hasReportTimestamp = false; + bool hasDataSetReference = false; + bool hasConfRev = false; + + uint32_t accessResultSize = 0; MmsValue* rptId = ReportControl_getRCBValue(self, "RptID"); MmsValue* optFlds = ReportControl_getRCBValue(self, "OptFlds"); MmsValue* datSet = ReportControl_getRCBValue(self, "DatSet"); - LinkedList_add(reportElements, rptId); - LinkedList_add(reportElements, optFlds); + MmsValue timeOfEntry; + timeOfEntry.type = MMS_BINARY_TIME; + timeOfEntry.value.binaryTime.size = 6; + + accessResultSize += MmsValue_encodeMmsData(rptId, NULL, 0, false); + accessResultSize += MmsValue_encodeMmsData(optFlds, NULL, 0, false); /* TODO optimize - this is constant */ - /* delete option fields for unsupported options */ + /* delete option fields for unrelated options (not present in unbuffered report) */ MmsValue_setBitStringBit(optFlds, 6, false); /* bufOvfl */ MmsValue_setBitStringBit(optFlds, 7, false); /* entryID */ - MmsValue_setBitStringBit(optFlds, 9, false); /* segmentation */ MmsValue* sqNum = ReportControl_getRCBValue(self, "SqNum"); - if (MmsValue_getBitStringBit(optFlds, 1)) /* sequence number */ - LinkedList_add(reportElements, sqNum); + if (MmsValue_getBitStringBit(optFlds, 1)) { /* sequence number */ + hasSeqNum = true; + accessResultSize += MmsValue_encodeMmsData(sqNum, NULL, 0, false); + } + + if (MmsValue_getBitStringBit(optFlds, 2)) { /* report time stamp */ + hasReportTimestamp = true; + MmsValue_setBinaryTime(&timeOfEntry, self->segmentedReportTimestamp); + accessResultSize += MmsValue_encodeMmsData(&timeOfEntry, NULL, 0, false); + } + + if (MmsValue_getBitStringBit(optFlds, 4)) { /* data set reference */ + hasDataSetReference = true; + accessResultSize += MmsValue_encodeMmsData(datSet, NULL, 0, false); + } - if (MmsValue_getBitStringBit(optFlds, 2)) /* report time stamp */ - LinkedList_add(reportElements, self->timeOfEntry); + if (MmsValue_getBitStringBit(optFlds, 8)) { /* configuration revision */ + hasConfRev = true; + accessResultSize += MmsValue_encodeMmsData(self->confRev, NULL, 0, false); + } - if (MmsValue_getBitStringBit(optFlds, 4)) /* data set reference */ - LinkedList_add(reportElements, datSet); + MmsValue_deleteAllBitStringBits(self->inclusionField); + accessResultSize += MmsValue_encodeMmsData(self->inclusionField, NULL, 0, false); - if (MmsValue_getBitStringBit(optFlds, 8)) - LinkedList_add(reportElements, self->confRev); + /* here ends the base part that is equal for all sub reports and independent of the + * number of included data points + */ + estimatedSegmentSize += accessResultSize; - if (isGI || isIntegrity) - MmsValue_setAllBitStringBits(self->inclusionField); - else - MmsValue_deleteAllBitStringBits(self->inclusionField); + int startElementIndex = self->startIndexForNextSegment; /* get value from segmented report control info */ - LinkedList_add(reportElements, self->inclusionField); + bool withDataReference = MmsValue_getBitStringBit(optFlds, 5); + bool withReasonCode = MmsValue_getBitStringBit(optFlds, 3); - /* add data references if selected */ - if (MmsValue_getBitStringBit(optFlds, 5)) { /* data-reference */ - DataSetEntry* dataSetEntry = self->dataSet->fcdas; + LogicalDevice* ld = (LogicalDevice*) self->parentLN->parent; + + IedModel* iedModel = (IedModel*) ld->parent; + + int maxIndex = startElementIndex; + + char* iedName = iedModel->name; + int iedNameLength = strlen(iedName); + + int i; - LogicalDevice* ld = (LogicalDevice*) self->parentLN->parent; + MmsValue _moreFollows; + _moreFollows.type = MMS_BOOLEAN; + _moreFollows.value.boolean = false; - IedModel* iedModel = (IedModel*) ld->parent; + MmsValue* subSeqNum = self->subSeqVal; - char* iedName = iedModel->name; + for (i = startElementIndex; i < self->dataSet->elementCount; i++) { - int iedNameLength = strlen(iedName); + DataSetEntry* dataSetEntry = getDataSetEntryWithIndex(self->dataSet->fcdas, i); + + bool includeElementInReport = false; + + if (isGI || isIntegrity) + includeElementInReport = true; + else if (self->inclusionFlags[i] != REPORT_CONTROL_NONE) + includeElementInReport = true; + + if (includeElementInReport) { + + int elementSize = 0; + + if (withDataReference) { + int currentPos = 0; + + currentPos += iedNameLength; + currentPos += (int) strlen(dataSetEntry->logicalDeviceName); + currentPos++; + currentPos += (int) strlen(dataSetEntry->variableName); + + elementSize += (1 + BerEncoder_determineLengthSize(currentPos) + currentPos); + } + + if (self->inclusionFlags[i] != REPORT_CONTROL_NONE) { + elementSize += MmsValue_encodeMmsData(self->bufferedDataSetValues[i], NULL, 0, false); + } + else { + elementSize += MmsValue_encodeMmsData(dataSetEntry->value, NULL, 0, false); + } + + if (withReasonCode) { + elementSize += 4; /* reason code size is always 4 byte */ + } + + if ((estimatedSegmentSize + elementSize) > maxMmsPduSize) { + + segmented = true; + moreFollows = true; + _moreFollows.value.boolean = true; + + if (startElementIndex == 0) + MmsValue_setUint32(subSeqNum, 0); + + if (DEBUG_IED_SERVER) + printf("IED_SERVER: element doesn't fit into MMS PDU --> another report segment is required!\n"); + break; + } + + MmsValue_setBitStringBit(self->inclusionField, i, true); + + accessResultSize += elementSize; + estimatedSegmentSize += elementSize; + } + + maxIndex++; + } + + MmsValue_setBitStringBit(optFlds, 9, segmented); /* set segmentation flag */ + + /* now calculate the exact information report segment size */ + + if (segmented) { + int segmentedSize = MmsValue_encodeMmsData(&_moreFollows, NULL, 0, false) + MmsValue_encodeMmsData(subSeqNum, NULL, 0, false); + accessResultSize += segmentedSize; + } + + uint32_t variableAccessSpecSize = 7; /* T L "RPT" */ + uint32_t listOfAccessResultSize = accessResultSize + BerEncoder_determineLengthSize(accessResultSize) + 1; + uint32_t informationReportContentSize = variableAccessSpecSize + listOfAccessResultSize; + uint32_t informationReportSize = 1 + informationReportContentSize + BerEncoder_determineLengthSize(informationReportContentSize); + uint32_t completeMessageSize = 1 + informationReportSize + BerEncoder_determineLengthSize(informationReportSize); + + if ((int) completeMessageSize > maxMmsPduSize) { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: report message too large %i (max = %i) -> skip message!\n", completeMessageSize, maxMmsPduSize); + + goto exit_function; + } - int i = 0; + /* encode the report message */ - for (i = 0; i < self->dataSet->elementCount; i++) { + ByteBuffer* reportBuffer = MmsServer_reserveTransmitBuffer(self->server->mmsServer); + + uint8_t* buffer = reportBuffer->buffer; + int bufPos = 0; + + /* encode header */ + bufPos = BerEncoder_encodeTL(0xa3, informationReportSize, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0xa0, informationReportContentSize, buffer, bufPos); + + bufPos = BerEncoder_encodeTL(0xa1, 5, buffer, bufPos); + bufPos = BerEncoder_encodeStringWithTag(0x80, "RPT", buffer, bufPos); + + bufPos = BerEncoder_encodeTL(0xa0, accessResultSize, buffer, bufPos); + + /* encode access-results */ + + bufPos = MmsValue_encodeMmsData(rptId, buffer, bufPos, true); + bufPos = MmsValue_encodeMmsData(optFlds, buffer, bufPos, true); + + if (hasSeqNum) + bufPos = MmsValue_encodeMmsData(sqNum, buffer, bufPos, true); + + if (hasReportTimestamp) + bufPos = MmsValue_encodeMmsData(&timeOfEntry, buffer, bufPos, true); + + if (hasDataSetReference) + bufPos = MmsValue_encodeMmsData(datSet, buffer, bufPos, true); + + if (hasConfRev) + bufPos = MmsValue_encodeMmsData(self->confRev, buffer, bufPos, true); + + if (segmented) { + bufPos = MmsValue_encodeMmsData(subSeqNum, buffer, bufPos, true); + bufPos = MmsValue_encodeMmsData(&_moreFollows, buffer, bufPos, true); + } + + bufPos = MmsValue_encodeMmsData(self->inclusionField, buffer, bufPos, true); + + /* encode data references if selected */ + if (MmsValue_getBitStringBit(optFlds, 5)) { /* data-reference */ + DataSetEntry* dataSetEntry = getDataSetEntryWithIndex(self->dataSet->fcdas, startElementIndex); + + for (i = startElementIndex; i < maxIndex; i++) { assert(dataSetEntry->value != NULL); bool addReferenceForEntry = false; @@ -400,33 +588,31 @@ sendReport(ReportControl* self, bool isIntegrity, bool isGI) dataReference[currentPos] = 0; - MmsValue* dataRef = MmsValue_newVisibleString(dataReference); + MmsValue _dataRef; + _dataRef.type = MMS_VISIBLE_STRING; + _dataRef.value.visibleString.buf = dataReference; + _dataRef.value.visibleString.size = currentPos; - LinkedList_add(reportElements, dataRef); - LinkedList_add(deletableElements, dataRef); + bufPos = MmsValue_encodeMmsData(&_dataRef, buffer, bufPos, true); } dataSetEntry = dataSetEntry->sibling; - } } - /* add data set value elements */ - DataSetEntry* dataSetEntry = self->dataSet->fcdas; + /* encode data set value elements */ + DataSetEntry* dataSetEntry = getDataSetEntryWithIndex(self->dataSet->fcdas, startElementIndex); - int i = 0; - for (i = 0; i < self->dataSet->elementCount; i++) { - assert(dataSetEntry->value != NULL); + for (i = startElementIndex; i < maxIndex; i++) { if (isGI || isIntegrity) { - LinkedList_add(reportElements, dataSetEntry->value); + /* encode value from data set */ + bufPos = MmsValue_encodeMmsData(dataSetEntry->value, buffer, bufPos, true); } else { if (self->inclusionFlags[i] != REPORT_CONTROL_NONE) { - assert(self->bufferedDataSetValues[i] != NULL); - - LinkedList_add(reportElements, self->bufferedDataSetValues[i]); - MmsValue_setBitStringBit(self->inclusionField, i, true); + /* encode value from the event buffer */ + bufPos = MmsValue_encodeMmsData(self->bufferedDataSetValues[i], buffer, bufPos, true); } } @@ -434,58 +620,90 @@ sendReport(ReportControl* self, bool isIntegrity, bool isGI) } /* add reason code to report if requested */ - if (MmsValue_getBitStringBit(optFlds, 3)) { - for (i = 0; i < self->dataSet->elementCount; i++) { + if (withReasonCode) { + + uint8_t bsBuf[1]; + + MmsValue _reason; + _reason.type = MMS_BIT_STRING; + _reason.value.bitString.size = 6; + _reason.value.bitString.buf = bsBuf; + + for (i = startElementIndex; i < maxIndex; i++) { if (isGI || isIntegrity) { - MmsValue* reason = MmsValue_newBitString(6); + bsBuf[0] = 0; /* clear all bits */ if (isGI) - MmsValue_setBitStringBit(reason, 5, true); + MmsValue_setBitStringBit(&_reason, 5, true); if (isIntegrity) - MmsValue_setBitStringBit(reason, 4, true); + MmsValue_setBitStringBit(&_reason, 4, true); - LinkedList_add(reportElements, reason); - LinkedList_add(deletableElements, reason); + bufPos = MmsValue_encodeMmsData(&_reason, buffer, bufPos, true); } else if (self->inclusionFlags[i] != REPORT_CONTROL_NONE) { - MmsValue* reason = MmsValue_newBitString(6); + bsBuf[0] = 0; /* clear all bits */ if (self->inclusionFlags[i] == REPORT_CONTROL_QUALITY_CHANGED) - MmsValue_setBitStringBit(reason, 2, true); + MmsValue_setBitStringBit(&_reason, 2, true); else if (self->inclusionFlags[i] == REPORT_CONTROL_VALUE_CHANGED) - MmsValue_setBitStringBit(reason, 1, true); + MmsValue_setBitStringBit(&_reason, 1, true); else if (self->inclusionFlags[i] == REPORT_CONTROL_VALUE_UPDATE) - MmsValue_setBitStringBit(reason, 3, true); + MmsValue_setBitStringBit(&_reason, 3, true); - LinkedList_add(reportElements, reason); - LinkedList_add(deletableElements, reason); + bufPos = MmsValue_encodeMmsData(&_reason, buffer, bufPos, true); } } } /* clear inclusion flags */ - for (i = 0; i < self->dataSet->elementCount; i++) + for (i = startElementIndex; i < maxIndex; i++) self->inclusionFlags[i] = REPORT_CONTROL_NONE; - ReportControl_unlockNotify(self); + reportBuffer->size = bufPos; - MmsServerConnection_sendInformationReportVMDSpecific(self->clientConnection, "RPT", reportElements, false); + MmsServerConnection_sendMessage(self->clientConnection, reportBuffer, false); - ReportControl_lockNotify(self); + MmsServer_releaseTransmitBuffer(self->server->mmsServer); + + if (moreFollows == false) { + /* reset sub sequence number */ + segmented = false; + self->startIndexForNextSegment = 0; + } + else { + /* increase sub sequence number */ + uint32_t subSeqNumVal = MmsValue_toUint32(subSeqNum); + subSeqNumVal++; + MmsValue_setUint32(subSeqNum, subSeqNumVal); + + self->startIndexForNextSegment = maxIndex; + } + + if (segmented == false) { + /* Increase sequence number */ + self->sqNum++; - /* Increase sequence number */ - self->sqNum++; + /* Unbuffered reporting --> sqNum is 8 bit only!!! */ + if (self->sqNum == 256) + self->sqNum = 0; - /* Unbuffered reporting --> sqNum is 8 bit only!!! */ - if (self->sqNum == 256) - self->sqNum = 0; + MmsValue_setUint16(sqNum, self->sqNum); + } + +exit_function: + self->segmented = segmented; + return moreFollows; +} - MmsValue_setUint16(sqNum, self->sqNum); +static void +sendReport(ReportControl* self, bool isIntegrity, bool isGI, uint64_t currentTime) +{ + updateTimeOfEntry(self, currentTime); + self->segmentedReportTimestamp = currentTime; - LinkedList_destroyDeep(deletableElements, (LinkedListValueDeleteFunction) MmsValue_delete); - LinkedList_destroyStatic(reportElements); + while (sendReportSegment(self, isIntegrity, isGI)); } static void @@ -1276,11 +1494,9 @@ updateOwner(ReportControl* rc, MmsServerConnection connection) MmsValue_setOctetString(owner, emptyAddr, 0); } } - } } - static bool checkForZeroEntryID(MmsValue* value) { @@ -1747,8 +1963,12 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ updateTimeOfEntry(reportControl, Hal_getTimeInMs()); + int inclusionBitStringSize = MmsValue_getBitStringSize(reportControl->inclusionField); + ReportBuffer* buffer = reportControl->reportBuffer; + Semaphore_wait(buffer->lock); + /* calculate size of complete buffer entry */ int bufferEntrySize = MemoryAllocator_getAlignedSize(sizeof(ReportBufferEntry)); @@ -1757,7 +1977,7 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ MmsValue inclusionFieldStatic; inclusionFieldStatic.type = MMS_BIT_STRING; - inclusionFieldStatic.value.bitString.size = MmsValue_getBitStringSize(reportControl->inclusionField); + inclusionFieldStatic.value.bitString.size = inclusionBitStringSize; MmsValue* inclusionField = &inclusionFieldStatic; @@ -1767,7 +1987,7 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ int i; - for (i = 0; i < MmsValue_getBitStringSize(reportControl->inclusionField); i++) { + for (i = 0; i < inclusionBitStringSize; i++) { assert(dataSetEntry != NULL); bufferEntrySize += MemoryAllocator_getAlignedSize(1); /* reason-for-inclusion */ @@ -1782,7 +2002,7 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ int i; - for (i = 0; i < MmsValue_getBitStringSize(reportControl->inclusionField); i++) { + for (i = 0; i < inclusionBitStringSize; i++) { if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE) { bufferEntrySize += MemoryAllocator_getAlignedSize(1); /* reason-for-inclusion */ @@ -2017,7 +2237,7 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ int i; - for (i = 0; i < MmsValue_getBitStringSize(reportControl->inclusionField); i++) { + for (i = 0; i < inclusionBitStringSize; i++) { assert(dataSetEntry != NULL); @@ -2037,7 +2257,7 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ int i; - for (i = 0; i < MmsValue_getBitStringSize(reportControl->inclusionField); i++) { + for (i = 0; i < inclusionBitStringSize; i++) { if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE) { @@ -2073,26 +2293,37 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ reportControl->lastEntryId = entryId; exit_function: + + Semaphore_post(buffer->lock); + return; } /* enqueuReport() */ -static void -sendNextReportEntry(ReportControl* self) +static bool +sendNextReportEntrySegment(ReportControl* self) { -#define LOCAL_STORAGE_MEMORY_SIZE 65536 + Semaphore_wait(self->reportBuffer->lock); - if (self->reportBuffer->nextToTransmit == NULL) - return; + if (self->reportBuffer->nextToTransmit == NULL) { + Semaphore_post(self->reportBuffer->lock); + return false; + } - char* localStorage = (char*) GLOBAL_MALLOC(LOCAL_STORAGE_MEMORY_SIZE); /* reserve 64k for dynamic memory allocation - - this can be optimized - maybe there is a good guess for the - required memory size */ + int maxMmsPduSize = MmsServerConnection_getMaxMmsPduSize(self->clientConnection); + int estimatedSegmentSize = 19; /* maximum size of header information (header can have 13-19 byte) */ + estimatedSegmentSize += 8; /* reserve space for more-segments-follow (3 byte) and sub-seq-num (3-5 byte) */ - if (localStorage == NULL) /* out-of-memory */ - goto return_out_of_memory; + bool segmented = self->segmented; + bool moreFollows = false; - MemoryAllocator ma; - MemoryAllocator_init(&ma, localStorage, LOCAL_STORAGE_MEMORY_SIZE); + bool hasSeqNum = false; + bool hasReportTimestamp = false; + bool hasDataSetReference = false; + bool hasBufOvfl = false; + bool hasEntryId = false; + bool hasConfRev = false; + + uint32_t accessResultSize = 0; ReportBufferEntry* report = self->reportBuffer->nextToTransmit; @@ -2105,329 +2336,467 @@ sendNextReportEntry(ReportControl* self) MmsValue* entryIdValue = MmsValue_getElement(self->rcbValues, 11); MmsValue_setOctetString(entryIdValue, (uint8_t*) report->entryId, 8); - MemAllocLinkedList reportElements = MemAllocLinkedList_create(&ma); - - assert(reportElements != NULL); - - if (reportElements == NULL) - goto return_out_of_memory; - MmsValue* rptId = ReportControl_getRCBValue(self, "RptID"); MmsValue* optFlds = ReportControl_getRCBValue(self, "OptFlds"); - if (MemAllocLinkedList_add(reportElements, rptId) == NULL) - goto return_out_of_memory; - - if (MemAllocLinkedList_add(reportElements, optFlds) == NULL) - goto return_out_of_memory; + accessResultSize += MmsValue_encodeMmsData(rptId, NULL, 0, false); + accessResultSize += MmsValue_encodeMmsData(optFlds, NULL, 0, false); /* TODO optimize - this is constant */ MmsValue inclusionFieldStack; - inclusionFieldStack.type = MMS_BIT_STRING; - inclusionFieldStack.value.bitString.size = MmsValue_getBitStringSize(self->inclusionField); - uint8_t* currentReportBufferPos = (uint8_t*) report + sizeof(ReportBufferEntry); - inclusionFieldStack.value.bitString.buf = currentReportBufferPos; + MmsValue* inclusionField = NULL; - MmsValue* inclusionField = &inclusionFieldStack; + if (report->flags == 0) { - if (report->flags == 0) - currentReportBufferPos += MemoryAllocator_getAlignedSize(MmsValue_getBitStringByteSize(inclusionField)); - else { - inclusionFieldStack.value.bitString.buf = - (uint8_t*) MemoryAllocator_allocate(&ma, MmsValue_getBitStringByteSize(inclusionField)); + inclusionField = &inclusionFieldStack; + + inclusionFieldStack.type = MMS_BIT_STRING; + inclusionFieldStack.value.bitString.size = MmsValue_getBitStringSize(self->inclusionField); + inclusionFieldStack.value.bitString.buf = currentReportBufferPos; - if (inclusionFieldStack.value.bitString.buf == NULL) - goto return_out_of_memory; + currentReportBufferPos += MemoryAllocator_getAlignedSize(MmsValue_getBitStringByteSize(inclusionField)); } + MmsValue_deleteAllBitStringBits(self->inclusionField); + uint8_t* valuesInReportBuffer = currentReportBufferPos; MmsValue_setBitStringBit(optFlds, 9, false); /* segmentation */ MmsValue* sqNum = ReportControl_getRCBValue(self, "SqNum"); - if (MmsValue_getBitStringBit(optFlds, 1)) /* sequence number */ - if (MemAllocLinkedList_add(reportElements, sqNum) == NULL) - goto return_out_of_memory; - - if (MmsValue_getBitStringBit(optFlds, 2)) { /* report time stamp */ - MmsValue* timeOfEntry = (MmsValue*) MemoryAllocator_allocate(&ma, sizeof(MmsValue)); + if (MmsValue_getBitStringBit(optFlds, 1)) { /* sequence number */ + hasSeqNum = true; + accessResultSize += MmsValue_encodeMmsData(sqNum, NULL, 0, false); + } - if (timeOfEntry == NULL) goto return_out_of_memory; + MmsValue _timeOfEntry; + MmsValue* timeOfEntry = NULL; - timeOfEntry->deleteValue = 0; - timeOfEntry->type = MMS_BINARY_TIME; - timeOfEntry->value.binaryTime.size = 6; + if (MmsValue_getBitStringBit(optFlds, 2)) { /* report time stamp */ + hasReportTimestamp = true; + _timeOfEntry.type = MMS_BINARY_TIME; + _timeOfEntry.value.binaryTime.size = 6; + timeOfEntry = &_timeOfEntry; MmsValue_setBinaryTime(timeOfEntry, report->timeOfEntry); - if (MemAllocLinkedList_add(reportElements, timeOfEntry) == NULL) - goto return_out_of_memory; + accessResultSize += MmsValue_encodeMmsData(timeOfEntry, NULL, 0, false); } + MmsValue* datSet = ReportControl_getRCBValue(self, "DatSet"); + if (MmsValue_getBitStringBit(optFlds, 4)) {/* data set reference */ - MmsValue* datSet = ReportControl_getRCBValue(self, "DatSet"); - if (MemAllocLinkedList_add(reportElements, datSet) == NULL) - goto return_out_of_memory; + hasDataSetReference = true; + accessResultSize += MmsValue_encodeMmsData(datSet, NULL, 0, false); } - if (MmsValue_getBitStringBit(optFlds, 6)) { /* bufOvfl */ + MmsValue _bufOvfl; + MmsValue* bufOvfl = NULL; - MmsValue* bufOvfl = (MmsValue*) MemoryAllocator_allocate(&ma, sizeof(MmsValue)); + if (MmsValue_getBitStringBit(optFlds, 6)) { /* bufOvfl */ + hasBufOvfl = true; - if (bufOvfl == NULL) goto return_out_of_memory; + bufOvfl = &_bufOvfl; - bufOvfl->deleteValue = 0; - bufOvfl->type = MMS_BOOLEAN; - bufOvfl->value.boolean = self->reportBuffer->isOverflow; + _bufOvfl.type = MMS_BOOLEAN; + _bufOvfl.value.boolean = self->reportBuffer->isOverflow; - if (self->reportBuffer->isOverflow) - self->reportBuffer->isOverflow = false; - - if (MemAllocLinkedList_add(reportElements, bufOvfl) == NULL) - goto return_out_of_memory; + accessResultSize += MmsValue_encodeMmsData(bufOvfl, NULL, 0, false); } - if (MmsValue_getBitStringBit(optFlds, 7)) { /* entryID */ - MmsValue* entryId = (MmsValue*) MemoryAllocator_allocate(&ma, sizeof(MmsValue)); + MmsValue _entryId; + MmsValue* entryId = NULL; - if (entryId == NULL) goto return_out_of_memory; + if (MmsValue_getBitStringBit(optFlds, 7)) { /* entryID */ + hasEntryId = true; + entryId = &_entryId; - entryId->deleteValue = 0; entryId->type = MMS_OCTET_STRING; entryId->value.octetString.buf = report->entryId; entryId->value.octetString.size = 8; entryId->value.octetString.maxSize = 8; - if (MemAllocLinkedList_add(reportElements, entryId) == NULL) - goto return_out_of_memory; + accessResultSize += MmsValue_encodeMmsData(entryId, NULL, 0, false); } - if (MmsValue_getBitStringBit(optFlds, 8)) - if (MemAllocLinkedList_add(reportElements, self->confRev) == NULL) - goto return_out_of_memory; + if (MmsValue_getBitStringBit(optFlds, 8)) { + hasConfRev = true; + accessResultSize += MmsValue_encodeMmsData(self->confRev, NULL, 0, false); + } - if (report->flags > 0) - MmsValue_setAllBitStringBits(inclusionField); + accessResultSize += MmsValue_encodeMmsData(self->inclusionField, NULL, 0, false); - if (MemAllocLinkedList_add(reportElements, inclusionField) == NULL) - goto return_out_of_memory; + /* here ends the base part that is equal for all sub reports and independent of the + * number of included data points + */ - /* add data references if selected */ - if (MmsValue_getBitStringBit(optFlds, 5)) { /* data-reference */ - DataSetEntry* dataSetEntry = self->dataSet->fcdas; + estimatedSegmentSize += accessResultSize; + int startElementIndex = self->startIndexForNextSegment; /* get value from segmented report control info */ + + bool withDataReference = MmsValue_getBitStringBit(optFlds, 5); + bool withReasonCode = MmsValue_getBitStringBit(optFlds, 3); + + LogicalDevice* ld = (LogicalDevice*) self->parentLN->parent; + + IedModel* iedModel = (IedModel*) ld->parent; + + int maxIndex = startElementIndex; + + char* iedName = iedModel->name; + int iedNameLength = strlen(iedName); + + int i; - LogicalDevice* ld = (LogicalDevice*) self->parentLN->parent; + MmsValue _moreFollows; + _moreFollows.type = MMS_BOOLEAN; + _moreFollows.value.boolean = false; - IedModel* iedModel = (IedModel*) ld->parent; + MmsValue* subSeqNum = self->subSeqVal; - char* iedName = iedModel->name; + for (i = startElementIndex; i < self->dataSet->elementCount; i++) { - int iedNameLength = strlen(iedName); + DataSetEntry* dataSetEntry = getDataSetEntryWithIndex(self->dataSet->fcdas, i); - int i = 0; + if ((report->flags > 0) || MmsValue_getBitStringBit(inclusionField, i)) { - for (i = 0; i < self->dataSet->elementCount; i++) { - assert(dataSetEntry->value != NULL); + int elementSize = 0; - bool addReferenceForEntry = false; + if (withDataReference) { - if (report->flags > 0) - addReferenceForEntry = true; - else - if (MmsValue_getBitStringBit(inclusionField, i)) - addReferenceForEntry = true; + char dataReference[130]; + int currentPos = 0; - if (addReferenceForEntry) { + int j; - int ldNameLength = strlen(dataSetEntry->logicalDeviceName); - int variableNameLength = strlen(dataSetEntry->variableName); + for (j = 0; j < iedNameLength; j++) { + dataReference[currentPos++] = iedName[j]; + } - int refLen = iedNameLength - + ldNameLength - + variableNameLength + 1; + int ldNameLength = strlen(dataSetEntry->logicalDeviceName); + for (j = 0; j < ldNameLength; j++) { + dataReference[currentPos] = dataSetEntry->logicalDeviceName[j]; + currentPos++; + } - char* dataReference = (char*) MemoryAllocator_allocate(&ma, refLen + 1); + dataReference[currentPos++] = '/'; - if (dataReference == NULL) goto return_out_of_memory; + for (j = 0; j < (int) strlen(dataSetEntry->variableName); j++) { + dataReference[currentPos++] = dataSetEntry->variableName[j]; + } - int currentPos = 0; + dataReference[currentPos] = 0; - int j; + /* TODO optimize - 2 + string size ? */ - for (j = 0; j < iedNameLength; j++) { - dataReference[currentPos++] = iedName[j]; - } + MmsValue _dataRef; + _dataRef.type = MMS_VISIBLE_STRING; + _dataRef.value.visibleString.buf = dataReference; + _dataRef.value.visibleString.size = currentPos; - for (j = 0; j < ldNameLength; j++) { - dataReference[currentPos] = dataSetEntry->logicalDeviceName[j]; - currentPos++; - } + elementSize += MmsValue_encodeMmsData(&_dataRef, NULL, 0, false); + } - dataReference[currentPos++] = '/'; + if ((report->flags > 0) || MmsValue_getBitStringBit(inclusionField, i)) { + currentReportBufferPos += MemoryAllocator_getAlignedSize(1);; - for (j = 0; j < variableNameLength; j++) { - dataReference[currentPos++] = dataSetEntry->variableName[j]; - } + elementSize += MmsValue_encodeMmsData((MmsValue*) currentReportBufferPos, NULL, 0, false); - dataReference[currentPos] = 0; + currentReportBufferPos += MmsValue_getSizeInMemory((MmsValue*) currentReportBufferPos); + } - MmsValue* dataRef = (MmsValue*) MemoryAllocator_allocate(&ma, sizeof(MmsValue)); + if (withReasonCode) { + elementSize += 4; /* reason code size is always 4 byte */ + } - if (dataRef == NULL) goto return_out_of_memory; - dataRef->deleteValue = 0; - dataRef->type = MMS_VISIBLE_STRING; - dataRef->value.visibleString.buf = dataReference; - dataRef->value.visibleString.size = refLen; + if ((estimatedSegmentSize + elementSize) > maxMmsPduSize) { + segmented = true; + moreFollows = true; + _moreFollows.value.boolean = true; - if (MemAllocLinkedList_add(reportElements, dataRef) == NULL) - goto return_out_of_memory; + if (startElementIndex == 0) + MmsValue_setUint32(subSeqNum, 0); - } + break; + } - dataSetEntry = dataSetEntry->sibling; + MmsValue_setBitStringBit(self->inclusionField, i, true); + accessResultSize += elementSize; + estimatedSegmentSize += elementSize; } + + maxIndex++; } - /* add data set value elements */ - int i = 0; - for (i = 0; i < self->dataSet->elementCount; i++) { + MmsValue_setBitStringBit(optFlds, 9, segmented); /* set segmentation flag */ - if (report->flags > 0) { - currentReportBufferPos += MemoryAllocator_getAlignedSize(1);; - if (MemAllocLinkedList_add(reportElements, currentReportBufferPos) == NULL) - goto return_out_of_memory; + /* now calculate the exact information report segment size */ - currentReportBufferPos += MmsValue_getSizeInMemory((MmsValue*) currentReportBufferPos); - } - else { - if (MmsValue_getBitStringBit(inclusionField, i)) { - currentReportBufferPos += MemoryAllocator_getAlignedSize(1);; - if (MemAllocLinkedList_add(reportElements, currentReportBufferPos) == NULL) - goto return_out_of_memory; - currentReportBufferPos += MmsValue_getSizeInMemory((MmsValue*) currentReportBufferPos); + if (segmented) { + int segmentedSize = MmsValue_encodeMmsData(&_moreFollows, NULL, 0, false) + MmsValue_encodeMmsData(subSeqNum, NULL, 0, false); + accessResultSize += segmentedSize; + } + + uint32_t variableAccessSpecSize = 7; /* T L "RPT" */ + uint32_t listOfAccessResultSize = accessResultSize + BerEncoder_determineLengthSize(accessResultSize) + 1; + uint32_t informationReportContentSize = variableAccessSpecSize + listOfAccessResultSize; + uint32_t informationReportSize = 1 + informationReportContentSize + BerEncoder_determineLengthSize(informationReportContentSize); + uint32_t completeMessageSize = 1 + informationReportSize + BerEncoder_determineLengthSize(informationReportSize); + + if ((int) completeMessageSize > maxMmsPduSize) { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: report message too large %i (max = %i) -> skip message!\n", completeMessageSize, maxMmsPduSize); + + goto exit_function; + } + + /* encode the report message */ + + ReportControl_unlockNotify(self); + + ByteBuffer* reportBuffer = MmsServer_reserveTransmitBuffer(self->server->mmsServer); + + uint8_t* buffer = reportBuffer->buffer; + int bufPos = 0; + + /* encode header */ + bufPos = BerEncoder_encodeTL(0xa3, informationReportSize, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0xa0, informationReportContentSize, buffer, bufPos); + + bufPos = BerEncoder_encodeTL(0xa1, 5, buffer, bufPos); + bufPos = BerEncoder_encodeStringWithTag(0x80, "RPT", buffer, bufPos); + + bufPos = BerEncoder_encodeTL(0xa0, accessResultSize, buffer, bufPos); + + /* encode access-results */ + + bufPos = MmsValue_encodeMmsData(rptId, buffer, bufPos, true); + bufPos = MmsValue_encodeMmsData(optFlds, buffer, bufPos, true); + + if (hasSeqNum) + bufPos = MmsValue_encodeMmsData(sqNum, buffer, bufPos, true); + + if (hasReportTimestamp) + bufPos = MmsValue_encodeMmsData(self->timeOfEntry, buffer, bufPos, true); + + if (hasDataSetReference) + bufPos = MmsValue_encodeMmsData(datSet, buffer, bufPos, true); + + if (hasBufOvfl) + bufPos = MmsValue_encodeMmsData(bufOvfl, buffer, bufPos, true); + + if (hasEntryId) + bufPos = MmsValue_encodeMmsData(entryId, buffer, bufPos, true); + + if (hasConfRev) + bufPos = MmsValue_encodeMmsData(self->confRev, buffer, bufPos, true); + + if (segmented) { + bufPos = MmsValue_encodeMmsData(subSeqNum, buffer, bufPos, true); + bufPos = MmsValue_encodeMmsData(&_moreFollows, buffer, bufPos, true); + } + + bufPos = MmsValue_encodeMmsData(self->inclusionField, buffer, bufPos, true); + + /* encode data references if selected */ + if (MmsValue_getBitStringBit(optFlds, 5)) { /* data-reference */ + DataSetEntry* dataSetEntry = getDataSetEntryWithIndex(self->dataSet->fcdas, startElementIndex); + + for (i = startElementIndex; i < maxIndex; i++) { + assert(dataSetEntry->value != NULL); + + bool addReferenceForEntry = false; + + if (report->flags > 0) + addReferenceForEntry = true; + else if (MmsValue_getBitStringBit(self->inclusionField, i)) + addReferenceForEntry = true; + + if (addReferenceForEntry) { + + char dataReference[130]; + int currentPos = 0; + + int j; + + for (j = 0; j < iedNameLength; j++) { + dataReference[currentPos++] = iedName[j]; + } + + int ldNameLength = strlen(dataSetEntry->logicalDeviceName); + for (j = 0; j < ldNameLength; j++) { + dataReference[currentPos] = dataSetEntry->logicalDeviceName[j]; + currentPos++; + } + + dataReference[currentPos++] = '/'; + + for (j = 0; j < (int) strlen(dataSetEntry->variableName); j++) { + dataReference[currentPos++] = dataSetEntry->variableName[j]; + } + + dataReference[currentPos] = 0; + + MmsValue _dataRef; + _dataRef.type = MMS_VISIBLE_STRING; + _dataRef.value.visibleString.buf = dataReference; + _dataRef.value.visibleString.size = currentPos; + + bufPos = MmsValue_encodeMmsData(&_dataRef, buffer, bufPos, true); } + + dataSetEntry = dataSetEntry->sibling; } } - /* add reason code to report if requested */ - if (MmsValue_getBitStringBit(optFlds, 3)) { - currentReportBufferPos = valuesInReportBuffer; + /* move to start position in report buffer */ + currentReportBufferPos = valuesInReportBuffer; - for (i = 0; i < self->dataSet->elementCount; i++) { + /* encode data set value elements */ + for (i = 0; i < maxIndex; i++) { - if (report->flags > 0) { - MmsValue* reason = (MmsValue*) MemoryAllocator_allocate(&ma, sizeof(MmsValue)); + bool isInBuffer = false; - if (reason == NULL) goto return_out_of_memory; + if (report->flags > 0) + isInBuffer = true; + else { + if (MmsValue_getBitStringBit(inclusionField, i)) + isInBuffer = true; + } - reason->deleteValue = 0; - reason->type = MMS_BIT_STRING; - reason->value.bitString.size = 6; - reason->value.bitString.buf = (uint8_t*) MemoryAllocator_allocate(&ma, 1); + if (isInBuffer) + currentReportBufferPos += MemoryAllocator_getAlignedSize(1); - if (reason->value.bitString.buf == NULL) goto return_out_of_memory; + if (i >= startElementIndex) { + if ((report->flags > 0) || MmsValue_getBitStringBit(self->inclusionField, i)) { + /* encode value from report buffer entry*/ + bufPos = MmsValue_encodeMmsData((MmsValue*) currentReportBufferPos, buffer, bufPos, true); + } + } - MmsValue_deleteAllBitStringBits(reason); + if (isInBuffer) + currentReportBufferPos += MmsValue_getSizeInMemory((MmsValue*) currentReportBufferPos); + } - if (report->flags & 0x02) /* GI */ - MmsValue_setBitStringBit(reason, 5, true); + /* add reason code to report if requested */ + if (withReasonCode) { - if (report->flags & 0x01) /* Integrity */ - MmsValue_setBitStringBit(reason, 4, true); + /* move to start position in report buffer */ + currentReportBufferPos = valuesInReportBuffer; - if (MemAllocLinkedList_add(reportElements, reason) == NULL) - goto return_out_of_memory; + uint8_t bsBuf[1]; - currentReportBufferPos += MemoryAllocator_getAlignedSize(1); + MmsValue _reason; + _reason.type = MMS_BIT_STRING; + _reason.value.bitString.size = 6; + _reason.value.bitString.buf = bsBuf; - MmsValue* dataSetElement = (MmsValue*) currentReportBufferPos; + for (i = 0; i < maxIndex; i++) { - currentReportBufferPos += MmsValue_getSizeInMemory(dataSetElement); - } - else if (MmsValue_getBitStringBit(inclusionField, i)) { - MmsValue* reason = (MmsValue*) MemoryAllocator_allocate(&ma, sizeof(MmsValue)); + bool isIncluded = false; - if (reason == NULL) goto return_out_of_memory; + if (report->flags > 0) { + bsBuf[0] = 0; /* clear all bits */ - reason->deleteValue = 0; - reason->type = MMS_BIT_STRING; - reason->value.bitString.size = 6; - reason->value.bitString.buf = (uint8_t*) MemoryAllocator_allocate(&ma, 1); + if (report->flags & 0x02) /* GI */ + MmsValue_setBitStringBit(&_reason, 5, true); - if (reason->value.bitString.buf == NULL) - goto return_out_of_memory; + if (report->flags & 0x01) /* Integrity */ + MmsValue_setBitStringBit(&_reason, 4, true); - MmsValue_deleteAllBitStringBits(reason); + isIncluded = true; + } + else if (MmsValue_getBitStringBit(self->inclusionField, i)) { + bsBuf[0] = 0; /* clear all bits */ switch((int) *currentReportBufferPos) { case REPORT_CONTROL_QUALITY_CHANGED: - MmsValue_setBitStringBit(reason, 2, true); + MmsValue_setBitStringBit(&_reason, 2, true); break; case REPORT_CONTROL_VALUE_CHANGED: - MmsValue_setBitStringBit(reason, 1, true); + MmsValue_setBitStringBit(&_reason, 1, true); break; case REPORT_CONTROL_VALUE_UPDATE: - MmsValue_setBitStringBit(reason, 3, true); + MmsValue_setBitStringBit(&_reason, 3, true); break; default: break; } + isIncluded = true; + } + + if (isIncluded) { + + if (i >= startElementIndex) + bufPos = MmsValue_encodeMmsData(&_reason, buffer, bufPos, true); + } + + if ((report->flags > 0) || MmsValue_getBitStringBit(inclusionField, i)) { currentReportBufferPos += MemoryAllocator_getAlignedSize(1); MmsValue* dataSetElement = (MmsValue*) currentReportBufferPos; currentReportBufferPos += MmsValue_getSizeInMemory(dataSetElement); - - if (MemAllocLinkedList_add(reportElements, reason) == NULL) - goto return_out_of_memory; } } } - ReportControl_unlockNotify(self); - - MmsServerConnection_sendInformationReportVMDSpecific(self->clientConnection, "RPT", (LinkedList) reportElements, false); + reportBuffer->size = bufPos; - ReportControl_lockNotify(self); + MmsServerConnection_sendMessage(self->clientConnection, reportBuffer, false); - /* Increase sequence number */ - self->sqNum++; - MmsValue_setUint32(sqNum, self->sqNum); + MmsServer_releaseTransmitBuffer(self->server->mmsServer); - assert(self->reportBuffer->nextToTransmit != self->reportBuffer->nextToTransmit->next); + if (moreFollows == false) { + /* reset sub sequence number */ + segmented = false; + self->startIndexForNextSegment = 0; + } + else { + /* increase sub sequence number */ + uint32_t subSeqNumVal = MmsValue_toUint32(subSeqNum); + subSeqNumVal++; + MmsValue_setUint32(subSeqNum, subSeqNumVal); - self->reportBuffer->nextToTransmit = self->reportBuffer->nextToTransmit->next; + self->startIndexForNextSegment = maxIndex; + } - if (DEBUG_IED_SERVER) - printf("IED_SERVER: sendNextReportEntry: memory(used/size): %i/%i\n", - (int) (ma.currentPtr - ma.memoryBlock), ma.size); + if (segmented == false) { -#if (DEBUG_IED_SERVER == 1) - printf("IED_SERVER: reporting.c nextToTransmit: %p\n", self->reportBuffer->nextToTransmit); - printEnqueuedReports(self); -#endif + assert(self->reportBuffer->nextToTransmit != self->reportBuffer->nextToTransmit->next); - goto cleanup_and_return; + self->reportBuffer->nextToTransmit = self->reportBuffer->nextToTransmit->next; -return_out_of_memory: + #if (DEBUG_IED_SERVER == 1) + printf("IED_SERVER: reporting.c nextToTransmit: %p\n", self->reportBuffer->nextToTransmit); + printEnqueuedReports(self); + #endif - if (DEBUG_IED_SERVER) - printf("IED_SERVER: sendNextReportEntry failed - memory allocation problem!\n"); + /* Increase sequence number */ + self->sqNum++; - //TODO set some flag to notify application here + MmsValue_setUint16(sqNum, self->sqNum); -cleanup_and_return: + if (self->reportBuffer->isOverflow) + self->reportBuffer->isOverflow = false; + } - if (localStorage != NULL) - GLOBAL_FREEMEM(localStorage); +exit_function: + self->segmented = segmented; + Semaphore_post(self->reportBuffer->lock); + return moreFollows; +} +static void +sendNextReportEntry(ReportControl* self) +{ + while (sendNextReportEntrySegment(self)); } void @@ -2463,13 +2832,13 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs) if (rc->buffered) enqueueReport(rc, false, false, currentTimeInMs); else - sendReport(rc, false, false); + sendReport(rc, false, false, currentTimeInMs); } if (rc->buffered) enqueueReport(rc, false, true, currentTimeInMs); else - sendReport(rc, false, true); + sendReport(rc, false, true, currentTimeInMs); rc->gi = false; @@ -2487,7 +2856,7 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs) if (rc->buffered) enqueueReport(rc, false, false, currentTimeInMs); else - sendReport(rc, false, false); + sendReport(rc, false, false, currentTimeInMs); rc->triggered = false; } @@ -2497,7 +2866,7 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs) if (rc->buffered) enqueueReport(rc, true, false, currentTimeInMs); else - sendReport(rc, true, false); + sendReport(rc, true, false, currentTimeInMs); rc->triggered = false; } @@ -2510,7 +2879,7 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs) if (rc->buffered) enqueueReport(rc, false, false, currentTimeInMs); else - sendReport(rc, false, false); + sendReport(rc, false, false, currentTimeInMs); rc->triggered = false; } diff --git a/src/mms/inc_private/mms_server_connection.h b/src/mms/inc_private/mms_server_connection.h index b39028da..1d716dd6 100644 --- a/src/mms/inc_private/mms_server_connection.h +++ b/src/mms/inc_private/mms_server_connection.h @@ -48,6 +48,12 @@ MmsServerConnection_init(MmsServerConnection connection, MmsServer server, IsoCo void MmsServerConnection_destroy(MmsServerConnection connection); +int +MmsServerConnection_getMaxMmsPduSize(MmsServerConnection self); + +void +MmsServerConnection_sendMessage(MmsServerConnection self, ByteBuffer* message, bool handlerMode); + bool MmsServerConnection_addNamedVariableList(MmsServerConnection self, MmsNamedVariableList variableList); diff --git a/src/mms/iso_mms/common/mms_value.c b/src/mms/iso_mms/common/mms_value.c index f2453907..40e08d06 100644 --- a/src/mms/iso_mms/common/mms_value.c +++ b/src/mms/iso_mms/common/mms_value.c @@ -315,7 +315,6 @@ MmsValue* MmsValue_newBitString(int bitSize) { MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - ; if (self == NULL) return NULL; diff --git a/src/mms/iso_mms/server/mms_information_report.c b/src/mms/iso_mms/server/mms_information_report.c index 30b17d82..87264f0b 100644 --- a/src/mms/iso_mms/server/mms_information_report.c +++ b/src/mms/iso_mms/server/mms_information_report.c @@ -220,7 +220,6 @@ exit_function: return; } - void /* send information report for a named variable list */ MmsServerConnection_sendInformationReportVMDSpecific(MmsServerConnection self, char* itemId, LinkedList values, bool handlerMode) @@ -254,6 +253,7 @@ MmsServerConnection_sendInformationReportVMDSpecific(MmsServerConnection self, c uint32_t informationReportContentSize = variableAccessSpecSize + listOfAccessResultSize; + informationReportSize = 1 + informationReportContentSize + BerEncoder_determineLengthSize(informationReportContentSize); @@ -266,8 +266,6 @@ MmsServerConnection_sendInformationReportVMDSpecific(MmsServerConnection self, c goto exit_function; } - if (DEBUG_MMS_SERVER) printf("MMS_SERVER: sendInfReport\n"); - ByteBuffer* reportBuffer = MmsServer_reserveTransmitBuffer(self->server); uint8_t* buffer = reportBuffer->buffer; diff --git a/src/mms/iso_mms/server/mms_server_connection.c b/src/mms/iso_mms/server/mms_server_connection.c index c689fdb5..6d84fe32 100644 --- a/src/mms/iso_mms/server/mms_server_connection.c +++ b/src/mms/iso_mms/server/mms_server_connection.c @@ -739,6 +739,18 @@ MmsServerConnection_destroy(MmsServerConnection self) GLOBAL_FREEMEM(self); } +int +MmsServerConnection_getMaxMmsPduSize(MmsServerConnection self) +{ + return self->maxPduSize; +} + +void +MmsServerConnection_sendMessage(MmsServerConnection self, ByteBuffer* message, bool handlerMode) +{ + IsoConnection_sendMessage(self->isoConnection, message, false); +} + #if (MMS_DYNAMIC_DATA_SETS == 1) bool MmsServerConnection_addNamedVariableList(MmsServerConnection self, MmsNamedVariableList variableList) diff --git a/src/mms/iso_mms/server/mms_write_service.c b/src/mms/iso_mms/server/mms_write_service.c index 13eaec62..aed96c54 100644 --- a/src/mms/iso_mms/server/mms_write_service.c +++ b/src/mms/iso_mms/server/mms_write_service.c @@ -80,7 +80,6 @@ mmsServer_createMmsWriteResponse(MmsServerConnection connection, response->size = bufPos; } - void MmsServerConnection_sendWriteResponse(MmsServerConnection self, uint32_t invokeId, MmsDataAccessError indication, bool handlerMode) {