diff --git a/CHANGELOG b/CHANGELOG index 7f6e4b20..1e9d5045 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,12 @@ +Changes to version 1.3.1 +------------------------ +- IEC 61850 client: improved support for handling segmented reports +- .NET API: Added some additional access function to ReportControlBlock +- Java SCL parser: added support for timestamp values in "Val" elements +- fixed bug in cmake file (winpcap support) +- added C# example code for client side setting group handling +- .NET API: added some additional wrapper code for MmsVariableSpecification functions + Changes to version 1.3.0 ------------------------ - IEC 61850 server: more features configurable at runtime @@ -17,7 +26,6 @@ Changes to version 1.3.0 - MMS server: fixed bug in delete variable list service - scope of delete was not considered optional - some more small bug fixes and optimizations - Changes to version 1.2.2 ------------------------ diff --git a/dotnet/IEC61850forCSharp/Reporting.cs b/dotnet/IEC61850forCSharp/Reporting.cs index 90ddba2d..398db95a 100644 --- a/dotnet/IEC61850forCSharp/Reporting.cs +++ b/dotnet/IEC61850forCSharp/Reporting.cs @@ -1,7 +1,7 @@ /* * Reporting.cs * - * Copyright 2014 Michael Zillgith + * Copyright 2014-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -147,6 +147,15 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ClientReport_getDataReference(IntPtr self, int elementIndex); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern bool ClientReport_hasSubSeqNum(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt16 ClientReport_getSubSeqNum(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern bool ClientReport_getMoreSeqmentsFollow(IntPtr self); + private IntPtr self; private IntPtr dataSetValues = IntPtr.Zero; @@ -213,16 +222,55 @@ namespace IEC61850 return ClientReport_getBufOvfl(self); } + /// + /// Indicates if the report contains a sequence number (SeqNum) field + /// + /// true if this instance has SeqNum; otherwise, false. public bool HasSeqNum () { return ClientReport_hasSeqNum(self); } + /// + /// Gets the value of the SeqNum field + /// + /// The report sequence number public UInt16 GetSeqNum () { return ClientReport_getSeqNum(self); } + /// + /// Indicates if the report contains a sub sequence number (SubSeqNum) and more segments follow (MoreSegmentsFollow) field + /// + /// true if this instance has SubSeqNum and MoreSegmentsFollow; otherwise, false. + public bool HasSubSeqNum() + { + return ClientReport_hasSubSeqNum(self); + } + + /// + /// Gets the sub sequence number (SubSeqNum) value of a segmented report + /// + /// The sub sequence number. + public UInt16 GetSubSeqNum() + { + return ClientReport_getSubSeqNum(self); + } + + /// + /// Gets the more segments follow (MoreSegmentsFollow) flag + /// + /// true, if more report segments follow, false otherwise. + public bool GetMoreSegmentsFollow() + { + return ClientReport_getMoreSeqmentsFollow(self); + } + + /// + /// Determines whether this report contains reason for inclusion information + /// + /// true if this report contains reason for inclusion information; otherwise, false. public bool HasReasonForInclusion () { return ClientReport_hasReasonForInclusion(self); diff --git a/src/iec61850/client/client_report.c b/src/iec61850/client/client_report.c index b2a76df9..26c670c4 100644 --- a/src/iec61850/client/client_report.c +++ b/src/iec61850/client/client_report.c @@ -3,7 +3,7 @@ * * Client implementation for IEC 61850 reporting. * - * Copyright 2013, 2014 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -56,11 +56,15 @@ struct sClientReport bool hasConfRev; bool hasTimestamp; bool hasBufOverflow; + bool hasSubSequenceNumber; uint64_t timestamp; uint16_t seqNum; uint32_t confRev; bool bufOverflow; + + uint16_t subSeqNum; + bool moreSegementsFollow; }; char* @@ -242,6 +246,24 @@ ClientReport_getDataSetValues(ClientReport self) return self->dataSetValues; } +bool +ClientReport_hasSubSeqNum(ClientReport self) +{ + return self->hasSubSequenceNumber; +} + +uint16_t +ClientReport_getSubSeqNum(ClientReport self) +{ + return self->subSeqNum; +} + +bool +ClientReport_getMoreSeqmentsFollow(ClientReport self) +{ + return self->moreSegementsFollow; +} + static ClientReport lookupReportHandler(IedConnection self, const char* rcbReference) { @@ -386,6 +408,7 @@ iedConnection_handleReport(IedConnection self, MmsValue* value) matchingReport->hasConfRev = false; matchingReport->hasDataSetName = false; matchingReport->hasBufOverflow = false; + matchingReport->hasSubSequenceNumber = false; if (DEBUG_IED_CLIENT) printf("IED_CLIENT: received report with ID %s\n", MmsValue_toString(rptIdValue)); @@ -535,10 +558,43 @@ iedConnection_handleReport(IedConnection self, MmsValue* value) inclusionIndex++; } + /* handle segmentation fields (check ReportedOptFlds.segmentation) */ + if (MmsValue_getBitStringBit(optFlds, 9) == true) { + + MmsValue* subSeqNum = MmsValue_getElement(value, inclusionIndex); + inclusionIndex++; + + if ((subSeqNum == NULL) || (MmsValue_getType(subSeqNum) != MMS_UNSIGNED)) { + if (DEBUG_IED_CLIENT) + printf("IED_CLIENT: received malformed report (SubSeqNum)\n"); + + goto exit_function; + } + else { + matchingReport->subSeqNum = (uint16_t) MmsValue_toUint32(subSeqNum); + } + + MmsValue* moreSegmentsFollow = MmsValue_getElement(value, inclusionIndex); + inclusionIndex++; + + if ((moreSegmentsFollow == NULL) || (MmsValue_getType(moreSegmentsFollow) != MMS_BOOLEAN)) { + if ((subSeqNum == NULL) || (MmsValue_getType(subSeqNum) != MMS_UNSIGNED)) { + if (DEBUG_IED_CLIENT) + printf("IED_CLIENT: received malformed report (MoreSegmentsFollow)\n"); + + goto exit_function; + } + } + else { + matchingReport->moreSegementsFollow = MmsValue_getBoolean(moreSegmentsFollow); + } - /* skip segmentation fields */ - if (MmsValue_getBitStringBit(optFlds, 9) == true) - inclusionIndex += 2; + matchingReport->hasSequenceNumber = true; + } + else { + matchingReport->subSeqNum = 0; + matchingReport->moreSegementsFollow = false; + } MmsValue* inclusion = MmsValue_getElement(value, inclusionIndex); diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h index 594d97ee..549ef634 100644 --- a/src/iec61850/inc/iec61850_client.h +++ b/src/iec61850/inc/iec61850_client.h @@ -1336,6 +1336,42 @@ ClientReport_getDataReference(ClientReport self, int elementIndex); LIB61850_API uint64_t ClientReport_getTimestamp(ClientReport self); +/** + * \brief indicates if the report contains a sub sequence number and a more segments follow flags (for segmented reporting) + * + * \param self the ClientReport instance + * + * \returns true if the report contains sub-sequence-number and more-follows-flag, false otherwise + */ +LIB61850_API bool +ClientReport_hasSubSeqNum(ClientReport self); + +/** + * \brief get the sub sequence number of the report (for segmented reporting) + * + * Returns the sub sequence number of the report. This is 0 for the first report of a segmented report and + * will be increased by one for each report segment. + * + * \param self the ClientReport instance + * + * \return the sub sequence number of the last received report message. + */ +LIB61850_API uint16_t +ClientReport_getSubSeqNum(ClientReport self); + +/** + * \brief get the more segments follow flag of the received report segment (for segmented reporting) + * + * Will return true in case this is part of a segmented report and more report segments will follow or false, if + * the current report is not a segmented report or is the last segment of a segmented report. + * + * \param self the ClientReport instance + * + * \return true when more segments of the current report will follow, false otherwise + */ +LIB61850_API bool +ClientReport_getMoreSeqmentsFollow(ClientReport self); + /** * \brief get the reason for inclusion of as a human readable string *