- IEC 61850 server: refactored reporting module; unbuffered reports are stored to report buffer and sent by connection handling thread

pull/211/head
Michael Zillgith 6 years ago
parent 0237c9b95c
commit 18c2e30f87

@ -3,7 +3,7 @@
*
* IEC 61850 server API for libiec61850.
*
* Copyright 2013-2019 Michael Zillgith
* Copyright 2013-2020 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -51,6 +51,9 @@ struct sIedServerConfig
/** size of the report buffer associated with a buffered report control block */
int reportBufferSize;
/** size of the report buffer associated with an unbuffered report control block */
int reportBufferSizeURCBs;
/** Base path (directory where the file service serves files */
char* fileServiceBasepath;
@ -125,6 +128,22 @@ IedServerConfig_setReportBufferSize(IedServerConfig self, int reportBufferSize);
LIB61850_API int
IedServerConfig_getReportBufferSize(IedServerConfig self);
/**
* \brief Set the report buffer size for unbuffered reporting
*
* \param reportBufferSize the buffer size for each unbuffered report control block
*/
LIB61850_API void
IedServerConfig_setReportBufferSizeForURCBs(IedServerConfig self, int reportBufferSize);
/**
* \brief Gets the report buffer size for unbuffered reporting
*
* \return the buffer size for each unbuffered report control block
*/
LIB61850_API int
IedServerConfig_getReportBufferSizeForURCBs(IedServerConfig self);
/**
* \brief Set the maximum number of MMS (TCP) connections the server accepts
*

@ -44,7 +44,8 @@ struct sIedServer
uint8_t writeAccessPolicies;
#if (CONFIG_IEC61850_REPORT_SERVICE == 1)
int reportBufferSize;
int reportBufferSizeBRCBs;
int reportBufferSizeURCBs;
#endif
#if (CONFIG_MMS_THREADLESS_STACK != 1)

@ -95,7 +95,6 @@ typedef struct {
/*
* the following members are only required for buffered RCBs *
* TODO move to ReportBuffer structure!
*/
bool isBuffering; /* true if buffered RCB is buffering (datSet is set to a valid value) */
@ -146,6 +145,10 @@ Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs);
LIB61850_INTERNAL void
Reporting_processReportEventsAfterUnlock(MmsMapping* self);
/* send reports in report buffer */
LIB61850_INTERNAL void
Reporting_sendReports(MmsMapping* self, MmsServerConnection connection);
LIB61850_INTERNAL void
Reporting_deactivateReportsForConnection(MmsMapping* self, MmsServerConnection connection);

@ -1,7 +1,7 @@
/*
* ied_server.c
*
* Copyright 2013-2018 Michael Zillgith
* Copyright 2013-2020 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -431,10 +431,14 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio
#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */
#if (CONFIG_IEC61850_REPORT_SERVICE == 1)
if (serverConfiguration)
self->reportBufferSize = serverConfiguration->reportBufferSize;
else
self->reportBufferSize = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
if (serverConfiguration) {
self->reportBufferSizeBRCBs = serverConfiguration->reportBufferSize;
self->reportBufferSizeURCBs = serverConfiguration->reportBufferSizeURCBs;
}
else {
self->reportBufferSizeBRCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
self->reportBufferSizeURCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
}
#endif
self->mmsMapping = MmsMapping_create(dataModel, self);

@ -1,7 +1,7 @@
/*
* ied_server_config.c
*
* Copyright 2018 Michael Zillgith
* Copyright 2018-2020 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -43,6 +43,7 @@ IedServerConfig_create()
if (self) {
self->reportBufferSize = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
self->reportBufferSizeURCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
self->fileServiceBasepath = StringUtils_copyString(CONFIG_VIRTUAL_FILESTORE_BASEPATH);
self->enableFileService = true;
self->enableDynamicDataSetService = true;
@ -88,6 +89,18 @@ IedServerConfig_getReportBufferSize(IedServerConfig self)
return self->reportBufferSize;
}
void
IedServerConfig_setReportBufferSizeForURCBs(IedServerConfig self, int reportBufferSize)
{
self->reportBufferSizeURCBs = reportBufferSize;
}
int
IedServerConfig_getReportBufferSizeForURCBs(IedServerConfig self)
{
return self->reportBufferSizeURCBs;
}
void
IedServerConfig_setFileServiceBasePath(IedServerConfig self, const char* basepath)
{

@ -24,6 +24,7 @@
#include "libiec61850_platform_includes.h"
#include "mms_mapping.h"
#include "mms_mapping_internal.h"
#include "mms_server_internal.h"
#include "stack_config.h"
#include "mms_goose.h"
@ -2389,12 +2390,15 @@ unselectControlsForConnection(MmsMapping* self, MmsServerConnection connection)
}
#endif /* (CONFIG_IEC61850_CONTROL_SERVICE == 1) */
static void /* is called by MMS server layer */
static void /* is called by MMS server layer and runs in the connection handling thread */
mmsConnectionHandler(void* parameter, MmsServerConnection connection, MmsServerEvent event)
{
MmsMapping* self = (MmsMapping*) parameter;
if (event == MMS_SERVER_CONNECTION_CLOSED) {
if (event == MMS_SERVER_CONNECTION_TICK) {
Reporting_sendReports(self, connection);
}
else if (event == MMS_SERVER_CONNECTION_CLOSED) {
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection);
/* call user provided handler function */

@ -1,7 +1,7 @@
/*
* reporting.c
*
* Copyright 2013-2019 Michael Zillgith
* Copyright 2013-2020 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -134,9 +134,7 @@ ReportControl_create(bool buffered, LogicalNode* parentLN, int reportBufferSize,
self->server = iedServer;
if (buffered) {
self->reportBuffer = ReportBuffer_create(reportBufferSize);
}
return self;
}
@ -343,397 +341,6 @@ getDataSetEntryWithIndex(DataSetEntry* dataSet, int index)
return NULL;
}
static bool
sendReportSegment(ReportControl* self, bool isIntegrity, bool isGI)
{
if (self->clientConnection == NULL)
return false;
IsoConnection_lock(self->clientConnection->isoConnection);
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) */
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");
MmsValue timeOfEntry;
timeOfEntry.type = MMS_BINARY_TIME;
timeOfEntry.value.binaryTime.size = 6;
accessResultSize += MmsValue_encodeMmsData(rptId, NULL, 0, false);
accessResultSize += 5; /* size of OptFlds */
/* delete option fields for unrelated options (not present in unbuffered report) */
MmsValue_setBitStringBit(optFlds, 6, false); /* bufOvfl */
MmsValue_setBitStringBit(optFlds, 7, false); /* entryID */
MmsValue* sqNum = ReportControl_getRCBValue(self, "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, 8)) { /* configuration revision */
hasConfRev = true;
accessResultSize += MmsValue_encodeMmsData(self->confRev, NULL, 0, false);
}
MmsValue_deleteAllBitStringBits(self->inclusionField);
accessResultSize += MmsValue_encodeMmsData(self->inclusionField, NULL, 0, false);
/* here ends the base part that is equal for all sub reports and independent of the
* number of included data points
*/
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 = (int) strlen(iedName);
int i;
MmsValue _moreFollows;
_moreFollows.type = MMS_BOOLEAN;
_moreFollows.value.boolean = false;
MmsValue* subSeqNum = self->subSeqVal;
for (i = startElementIndex; i < self->dataSet->elementCount; i++) {
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 {
if (dataSetEntry->value) {
elementSize += MmsValue_encodeMmsData(dataSetEntry->value, NULL, 0, false);
}
else {
MmsValue _errVal;
_errVal.type = MMS_DATA_ACCESS_ERROR;
_errVal.value.dataAccessError = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
elementSize += MmsValue_encodeMmsData(&_errVal, 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;
}
/* encode the report message */
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;
if (isGI || isIntegrity)
addReferenceForEntry = true;
else
if (self->inclusionFlags[i] != REPORT_CONTROL_NONE)
addReferenceForEntry = true;
if (addReferenceForEntry) {
char dataReference[130];
int currentPos = 0;
int j;
for (j = 0; j < iedNameLength; j++) {
dataReference[currentPos++] = iedName[j];
}
int ldNameLength = (int) 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;
}
}
/* encode data set value elements */
DataSetEntry* dataSetEntry = getDataSetEntryWithIndex(self->dataSet->fcdas, startElementIndex);
for (i = startElementIndex; i < maxIndex; i++) {
if (isGI || isIntegrity) {
/* encode value from data set */
if (dataSetEntry->value) {
bufPos = MmsValue_encodeMmsData(dataSetEntry->value, buffer, bufPos, true);
}
else {
MmsValue _errVal;
_errVal.type = MMS_DATA_ACCESS_ERROR;
_errVal.value.dataAccessError = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
bufPos = MmsValue_encodeMmsData(&_errVal, buffer, bufPos, true);
}
}
else {
if (self->inclusionFlags[i] != REPORT_CONTROL_NONE) {
/* encode value from the event buffer */
bufPos = MmsValue_encodeMmsData(self->bufferedDataSetValues[i], buffer, bufPos, true);
}
}
dataSetEntry = dataSetEntry->sibling;
}
/* add reason code to report if requested */
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) {
bsBuf[0] = 0; /* clear all bits */
if (isGI)
MmsValue_setBitStringBit(&_reason, 5, true);
if (isIntegrity)
MmsValue_setBitStringBit(&_reason, 4, true);
bufPos = MmsValue_encodeMmsData(&_reason, buffer, bufPos, true);
}
else if (self->inclusionFlags[i] != REPORT_CONTROL_NONE) {
bsBuf[0] = 0; /* clear all bits */
if (self->inclusionFlags[i] == REPORT_CONTROL_QUALITY_CHANGED)
MmsValue_setBitStringBit(&_reason, 2, true);
else if (self->inclusionFlags[i] == REPORT_CONTROL_VALUE_CHANGED)
MmsValue_setBitStringBit(&_reason, 1, true);
else if (self->inclusionFlags[i] == REPORT_CONTROL_VALUE_UPDATE)
MmsValue_setBitStringBit(&_reason, 3, true);
bufPos = MmsValue_encodeMmsData(&_reason, buffer, bufPos, true);
}
}
}
/* clear inclusion flags */
for (i = startElementIndex; i < maxIndex; i++)
self->inclusionFlags[i] = REPORT_CONTROL_NONE;
reportBuffer->size = bufPos;
MmsServerConnection_sendMessage(self->clientConnection, reportBuffer);
MmsServer_releaseTransmitBuffer(self->server->mmsServer);
IsoConnection_unlock(self->clientConnection->isoConnection);
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++;
/* 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;
}
static void
sendReport(ReportControl* self, bool isIntegrity, bool isGI, uint64_t currentTime)
{
updateTimeOfEntry(self, currentTime);
self->segmentedReportTimestamp = currentTime;
while (sendReportSegment(self, isIntegrity, isGI));
}
static void
createDataSetValuesShadowBuffer(ReportControl* rc)
{
@ -1432,7 +1039,7 @@ Reporting_createMmsBufferedRCBs(MmsMapping* self, MmsDomain* domain,
int currentReport = 0;
while (currentReport < reportsCount) {
ReportControl* rc = ReportControl_create(true, logicalNode, self->iedServer->reportBufferSize, self->iedServer);
ReportControl* rc = ReportControl_create(true, logicalNode, self->iedServer->reportBufferSizeBRCBs, self->iedServer);
rc->domain = domain;
@ -1469,7 +1076,7 @@ Reporting_createMmsUnbufferedRCBs(MmsMapping* self, MmsDomain* domain,
int currentReport = 0;
while (currentReport < reportsCount) {
ReportControl* rc = ReportControl_create(false, logicalNode, self->iedServer->reportBufferSize, self->iedServer);
ReportControl* rc = ReportControl_create(false, logicalNode, self->iedServer->reportBufferSizeURCBs, self->iedServer);
rc->domain = domain;
@ -1833,7 +1440,8 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
if (rc->dataSet)
clearInclusionFlags(rc);
rc->triggered = false;
/* clear report buffer */
purgeBuf(rc);
}
rc->enabled = false;
@ -2108,6 +1716,9 @@ Reporting_deactivateReportsForConnection(MmsMapping* self, MmsServerConnection c
if (rc->resvTms != -1)
updateOwner(rc, NULL);
/* delete buffer content */
purgeBuf(rc);
}
else {
if (rc->resvTms == 0)
@ -2227,6 +1838,9 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
reportControl->name, (unsigned) reportControl->sqNum, reportControl->enabled,
reportControl->isBuffering, reportControl->buffered, isIntegrity, isGI);
bool isBuffered = reportControl->buffered;
bool overflow = false;
updateTimeOfEntry(reportControl, Hal_getTimeInMs());
int inclusionBitStringSize = MmsValue_getBitStringSize(reportControl->inclusionField);
@ -2312,13 +1926,16 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
goto exit_function;
}
if (isBuffered) {
/* remove old buffered GI reports */
if (isGI) removeAllGIReportsFromReportBuffer(buffer);
}
uint8_t* entryBufPos = NULL;
uint8_t* entryStartPos;
if (DEBUG_IED_SERVER)
printf("IED_SERVER: number of buffered reports:%i\n", buffer->reportsCount);
printf("IED_SERVER: number of reports in report buffer: %i\n", buffer->reportsCount);
if (buffer->lastEnqueuedReport == NULL) { /* buffer is empty - we start at the beginning of the memory block */
entryBufPos = buffer->memoryBlock;
@ -2379,6 +1996,7 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
if (buffer->nextToTransmit == buffer->oldestReport) {
buffer->nextToTransmit = buffer->oldestReport->next;
buffer->isOverflow = true;
overflow = true;
}
#if (DEBUG_IED_SERVER == 1)
@ -2414,6 +2032,7 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
if (buffer->nextToTransmit == buffer->oldestReport) {
buffer->nextToTransmit = buffer->oldestReport->next;
buffer->isOverflow = true;
overflow = true;
}
#if (DEBUG_IED_SERVER == 1)
@ -2436,6 +2055,7 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
if (buffer->nextToTransmit == buffer->oldestReport) {
buffer->nextToTransmit = buffer->oldestReport->next;
buffer->isOverflow = true;
overflow = true;
}
#if (DEBUG_IED_SERVER == 1)
@ -2459,6 +2079,7 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
if (buffer->nextToTransmit == buffer->oldestReport) {
buffer->nextToTransmit = buffer->oldestReport->next;
buffer->isOverflow = true;
overflow = true;
}
#if (DEBUG_IED_SERVER == 1)
@ -2484,6 +2105,7 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
ReportBufferEntry* entry = (ReportBufferEntry*) entryBufPos;
if (isBuffered) {
/* ENTRY_ID is set to system time in ms! */
uint64_t entryId = timeOfEntry;
@ -2509,6 +2131,9 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
MmsValue_setOctetString(entryIdValue, (uint8_t*) entry->entryId, 8);
}
reportControl->lastEntryId = entryId;
}
if (isIntegrity)
entry->flags = 1;
else if (isGI)
@ -2600,10 +2225,12 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
if (buffer->oldestReport == NULL)
buffer->oldestReport = buffer->lastEnqueuedReport;
reportControl->lastEntryId = entryId;
exit_function:
if (overflow) {
/* TODO call user callback handler */
}
Semaphore_post(buffer->lock);
return;
@ -2615,12 +2242,11 @@ sendNextReportEntrySegment(ReportControl* self)
if (self->clientConnection == NULL)
return false;
int maxMmsPduSize = MmsServerConnection_getMaxMmsPduSize(self->clientConnection);
bool isBuffered = self->buffered;
Semaphore_wait(self->reportBuffer->lock);
int maxMmsPduSize = MmsServerConnection_getMaxMmsPduSize(self->clientConnection);
if (self->reportBuffer->nextToTransmit == NULL) {
Semaphore_post(self->reportBuffer->lock);
return false;
}
@ -2643,20 +2269,29 @@ sendNextReportEntrySegment(ReportControl* self)
#if (DEBUG_IED_SERVER == 1)
printf("IED_SERVER: SEND NEXT REPORT: ");
if (isBuffered)
printReportId(report);
printf(" size: %i\n", report->entryLength);
#endif
if (isBuffered) {
MmsValue* entryIdValue = MmsValue_getElement(self->rcbValues, 11);
MmsValue_setOctetString(entryIdValue, (uint8_t*) report->entryId, 8);
}
MmsValue* rptId = ReportControl_getRCBValue(self, "RptID");
MmsValue* optFlds = ReportControl_getRCBValue(self, "OptFlds");
if (isBuffered == false) {
/* delete option fields for unrelated options (not present in unbuffered report) */
MmsValue_setBitStringBit(optFlds, 6, false); /* bufOvfl */
MmsValue_setBitStringBit(optFlds, 7, false); /* entryID */
}
accessResultSize += MmsValue_encodeMmsData(rptId, NULL, 0, false);
accessResultSize += 5; /* add size of OptFlds */
MmsValue inclusionFieldStack;
MmsValue _inclusionField;
uint8_t* currentReportBufferPos = (uint8_t*) report + sizeof(ReportBufferEntry);
@ -2664,11 +2299,11 @@ sendNextReportEntrySegment(ReportControl* self)
if (report->flags == 0) {
inclusionField = &inclusionFieldStack;
inclusionField = &_inclusionField;
inclusionFieldStack.type = MMS_BIT_STRING;
inclusionFieldStack.value.bitString.size = MmsValue_getBitStringSize(self->inclusionField);
inclusionFieldStack.value.bitString.buf = currentReportBufferPos;
_inclusionField.type = MMS_BIT_STRING;
_inclusionField.value.bitString.size = MmsValue_getBitStringSize(self->inclusionField);
_inclusionField.value.bitString.buf = currentReportBufferPos;
currentReportBufferPos += MemoryAllocator_getAlignedSize(MmsValue_getBitStringByteSize(inclusionField));
}
@ -2716,6 +2351,7 @@ sendNextReportEntrySegment(ReportControl* self)
MmsValue _bufOvfl;
MmsValue* bufOvfl = NULL;
if (isBuffered) {
if (MmsValue_getBitStringBit(optFlds, 6)) { /* bufOvfl */
hasBufOvfl = true;
@ -2726,10 +2362,12 @@ sendNextReportEntrySegment(ReportControl* self)
accessResultSize += MmsValue_encodeMmsData(bufOvfl, NULL, 0, false);
}
}
MmsValue _entryId;
MmsValue* entryId = NULL;
if (isBuffered) {
if (MmsValue_getBitStringBit(optFlds, 7)) { /* entryID */
hasEntryId = true;
entryId = &_entryId;
@ -2741,6 +2379,7 @@ sendNextReportEntrySegment(ReportControl* self)
accessResultSize += MmsValue_encodeMmsData(entryId, NULL, 0, false);
}
}
if (MmsValue_getBitStringBit(optFlds, 8)) {
hasConfRev = true;
@ -2763,7 +2402,7 @@ sendNextReportEntrySegment(ReportControl* self)
IedModel* iedModel = (IedModel*) ld->parent;
int maxIndex = startElementIndex;
int maxIndex = 0;
char* iedName = iedModel->name;
int iedNameLength = (int) strlen(iedName);
@ -2776,16 +2415,18 @@ sendNextReportEntrySegment(ReportControl* self)
MmsValue* subSeqNum = self->subSeqVal;
for (i = startElementIndex; i < self->dataSet->elementCount; i++) {
DataSetEntry* dataSetEntry = getDataSetEntryWithIndex(self->dataSet->fcdas, i);
for (i = 0; i < self->dataSet->elementCount; i++) {
if ((report->flags > 0) || MmsValue_getBitStringBit(inclusionField, i)) {
int elementSize = 0;
if (i >= startElementIndex) {
if (withDataReference) {
DataSetEntry* dataSetEntry = getDataSetEntryWithIndex(self->dataSet->fcdas, i);
char dataReference[130];
int currentPos = 0;
@ -2818,7 +2459,7 @@ sendNextReportEntrySegment(ReportControl* self)
}
/* get size of data */
if ((report->flags > 0) || MmsValue_getBitStringBit(inclusionField, i)) {
{
int length;
int lenSize = BerDecoder_decodeLength(currentReportBufferPos + 1, &length, 0, report->entryLength);
@ -2836,11 +2477,11 @@ sendNextReportEntrySegment(ReportControl* self)
currentReportBufferPos += dataElementSize;
}
if (withReasonCode) {
elementSize += 4; /* reason code size is always 4 byte */
}
if ((estimatedSegmentSize + elementSize) > maxMmsPduSize) {
segmented = true;
@ -2858,6 +2499,24 @@ sendNextReportEntrySegment(ReportControl* self)
accessResultSize += elementSize;
estimatedSegmentSize += elementSize;
}
else {
/* move element pointer in report buffer to skip elements that were already sent in former segments */
int length;
int lenSize = BerDecoder_decodeLength(currentReportBufferPos + 1, &length, 0, report->entryLength);
if (lenSize < 0) {
if (DEBUG_IED_SERVER)
printf("IED_SERVER: internal error in report buffer\n");
return false;
}
int dataElementSize = 1 + lenSize + length;
currentReportBufferPos += dataElementSize;
}
}
maxIndex++;
}
@ -3119,6 +2778,12 @@ sendNextReportEntrySegment(ReportControl* self)
/* Increase sequence number */
self->sqNum++;
if (isBuffered == false) {
/* Unbuffered reporting --> sqNum is 8 bit only!!! */
if (self->sqNum == 256)
self->sqNum = 0;
}
MmsValue_setUint16(sqNum, self->sqNum);
if (self->reportBuffer->isOverflow)
@ -3127,14 +2792,27 @@ sendNextReportEntrySegment(ReportControl* self)
exit_function:
self->segmented = segmented;
Semaphore_post(self->reportBuffer->lock);
return moreFollows;
}
static void
sendNextReportEntry(ReportControl* self)
{
while (sendNextReportEntrySegment(self));
Semaphore_wait(self->reportBuffer->lock);
int messageCount = 0;
while (self->reportBuffer->nextToTransmit) {
messageCount++;
while (sendNextReportEntrySegment(self)) {
messageCount++;
}
if (messageCount > 100)
break;
}
Semaphore_post(self->reportBuffer->lock);
}
void
@ -3164,19 +2842,11 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs)
/* send current events in event buffer before GI report */
if (rc->triggered) {
rc->triggered = false;
if (rc->buffered)
enqueueReport(rc, false, false, currentTimeInMs);
else
sendReport(rc, false, false, currentTimeInMs);
}
if (rc->buffered)
enqueueReport(rc, false, true, currentTimeInMs);
else
sendReport(rc, false, true, currentTimeInMs);
rc->gi = false;
@ -3191,20 +2861,13 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs)
/* send current events in event buffer before integrity report */
if (rc->triggered) {
if (rc->buffered)
enqueueReport(rc, false, false, currentTimeInMs);
else
sendReport(rc, false, false, currentTimeInMs);
rc->triggered = false;
}
rc->nextIntgReportTime = currentTimeInMs + rc->intgPd;
if (rc->buffered)
enqueueReport(rc, true, false, currentTimeInMs);
else
sendReport(rc, true, false, currentTimeInMs);
rc->triggered = false;
}
@ -3214,20 +2877,12 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs)
if (rc->triggered) {
if (currentTimeInMs >= rc->reportTime) {
if (rc->buffered)
enqueueReport(rc, false, false, currentTimeInMs);
else
sendReport(rc, false, false, currentTimeInMs);
rc->triggered = false;
}
}
if (rc->buffered && rc->enabled)
sendNextReportEntry(rc);
}
}
void
@ -3249,6 +2904,32 @@ Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs)
}
}
/*
* To be called only by connection thread!
*/
void
Reporting_sendReports(MmsMapping* self, MmsServerConnection connection)
{
LinkedList element = LinkedList_getNext(self->reportControls);
while (element) {
ReportControl* rc = (ReportControl*) LinkedList_getData(element);
if (rc->clientConnection == connection) {
ReportControl_lockNotify(rc);
if (rc->enabled) {
sendNextReportEntry(rc);
}
ReportControl_unlockNotify(rc);
}
element = LinkedList_getNext(element);
}
}
static inline void
copySingleValueToReportBuffer(ReportControl* self, int dataSetEntryIndex)
{

@ -37,7 +37,9 @@ extern "C" {
#include "iso_connection_parameters.h"
typedef enum {
MMS_SERVER_NEW_CONNECTION, MMS_SERVER_CONNECTION_CLOSED
MMS_SERVER_NEW_CONNECTION,
MMS_SERVER_CONNECTION_CLOSED,
MMS_SERVER_CONNECTION_TICK
} MmsServerEvent;
typedef struct sMmsServer* MmsServer;

@ -66,6 +66,9 @@ typedef void
typedef void
(*MessageReceivedHandler)(void* parameter, ByteBuffer* message, ByteBuffer* response);
typedef void
(*UserLayerTickHandler)(void* parameter);
LIB61850_INTERNAL char*
IsoConnection_getPeerAddress(IsoConnection self);
@ -82,7 +85,8 @@ LIB61850_INTERNAL void
IsoConnection_unlock(IsoConnection self);
LIB61850_INTERNAL void
IsoConnection_installListener(IsoConnection self, MessageReceivedHandler handler,
IsoConnection_installListener(IsoConnection self, MessageReceivedHandler rcvdHandler,
UserLayerTickHandler tickHandler,
void* parameter);
LIB61850_INTERNAL void*

@ -219,6 +219,9 @@ MmsServer_reserveTransmitBuffer(MmsServer self);
LIB61850_INTERNAL void
MmsServer_releaseTransmitBuffer(MmsServer self);
LIB61850_INTERNAL void
MmsServer_callConnectionHandler(MmsServer self, MmsServerConnection connection);
/* write_out function required for ASN.1 encoding */
LIB61850_INTERNAL int
mmsServer_write_out(const void *buffer, size_t size, void *app_key);

@ -1401,7 +1401,7 @@ connectionHandlingThread(void* parameter)
while (self->connectionThreadRunning) {
if (MmsConnection_tick(self))
Thread_sleep(10);
Thread_sleep(1);
}
return NULL;

@ -516,7 +516,6 @@ mmsServer_fileUploadTask(MmsServer self, MmsObtainFileTask task)
{
/* send ObtainFileError */
IsoConnection_lock(task->connection->isoConnection);
ByteBuffer* response = MmsServer_reserveTransmitBuffer(self);
@ -545,7 +544,6 @@ mmsServer_fileUploadTask(MmsServer self, MmsObtainFileTask task)
case MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_DESTINATION:
{
/* send ObtainFileError */
IsoConnection_lock(task->connection->isoConnection);
ByteBuffer* response = MmsServer_reserveTransmitBuffer(self);

@ -504,6 +504,14 @@ MmsServer_handleBackgroundTasks(MmsServer self)
#endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */
}
void
MmsServer_callConnectionHandler(MmsServer self, MmsServerConnection connection)
{
if (self->connectionHandler) {
self->connectionHandler(self->connectionHandlerParameter, connection, MMS_SERVER_CONNECTION_TICK);
}
}
void
MmsServer_stopListeningThreadless(MmsServer self)
{

@ -696,7 +696,7 @@ MmsServerConnection_parseMessage(MmsServerConnection self, ByteBuffer* message,
return;
}
static void /* will be called by IsoConnection */
static void /* is called by IsoConnection */
messageReceived(void* parameter, ByteBuffer* message, ByteBuffer* response)
{
MmsServerConnection self = (MmsServerConnection) parameter;
@ -704,6 +704,14 @@ messageReceived(void* parameter, ByteBuffer* message, ByteBuffer* response)
MmsServerConnection_parseMessage(self, message, response);
}
static void /* is called by IsoConnection */
connectionTickHandler(void* parameter)
{
MmsServerConnection self = (MmsServerConnection) parameter;
MmsServer_callConnectionHandler(self->server, self);
}
/**********************************************************************************************
* MMS server connection public API functions
*********************************************************************************************/
@ -733,7 +741,9 @@ MmsServerConnection_init(MmsServerConnection connection, MmsServer server, IsoCo
self->lastRequestInvokeId = 0;
#endif
IsoConnection_installListener(isoCon, messageReceived, (void*) self);
IsoConnection_installListener(isoCon, messageReceived,
(UserLayerTickHandler) connectionTickHandler,
(void*) self);
return self;
}

@ -64,7 +64,8 @@ struct sIsoConnection
ByteBuffer cotpWriteBuffer;
MessageReceivedHandler msgRcvdHandler;
void* msgRcvdHandlerParameter;
UserLayerTickHandler tickHandler;
void* handlerParameter; /* context parameter for msgRcvdHandler */
IsoServer isoServer;
@ -141,6 +142,11 @@ IsoConnection_addHandleSet(const IsoConnection self, HandleSet handles)
void
IsoConnection_handleTcpConnection(IsoConnection self)
{
/* call tick handler */
if (self->tickHandler) {
self->tickHandler(self->handlerParameter);
}
#if (CONFIG_MMS_SINGLE_THREADED == 0)
if (IsoServer_waitReady(self->isoServer, 10) < 1)
goto exit_function;
@ -217,7 +223,7 @@ IsoConnection_handleTcpConnection(IsoConnection self)
ByteBuffer_wrap(&mmsResponseBuffer, self->sendBuffer, 0, SEND_BUF_SIZE);
if (self->msgRcvdHandler != NULL) {
self->msgRcvdHandler(self->msgRcvdHandlerParameter,
self->msgRcvdHandler(self->handlerParameter,
&mmsRequest, &mmsResponseBuffer);
}
@ -304,7 +310,7 @@ IsoConnection_handleTcpConnection(IsoConnection self)
if (self->msgRcvdHandler != NULL) {
self->msgRcvdHandler(self->msgRcvdHandlerParameter,
self->msgRcvdHandler(self->handlerParameter,
mmsRequest, &mmsResponseBuffer);
}
@ -470,7 +476,8 @@ IsoConnection_create(Socket socket, IsoServer isoServer)
self->receiveBuffer = (uint8_t*) GLOBAL_MALLOC(RECEIVE_BUF_SIZE);
self->sendBuffer = (uint8_t*) GLOBAL_MALLOC(SEND_BUF_SIZE);
self->msgRcvdHandler = NULL;
self->msgRcvdHandlerParameter = NULL;
self->tickHandler = NULL;
self->handlerParameter = NULL;
self->isoServer = isoServer;
self->state = ISO_CON_STATE_RUNNING;
self->clientAddress = Socket_getPeerAddress(self->socket);
@ -637,11 +644,13 @@ IsoConnection_close(IsoConnection self)
}
void
IsoConnection_installListener(IsoConnection self, MessageReceivedHandler handler,
IsoConnection_installListener(IsoConnection self, MessageReceivedHandler rcvdHandler,
UserLayerTickHandler tickHandler,
void* parameter)
{
self->msgRcvdHandler = handler;
self->msgRcvdHandlerParameter = parameter;
self->msgRcvdHandler = rcvdHandler;
self->tickHandler = tickHandler;
self->handlerParameter = parameter;
}
void*

Loading…
Cancel
Save