From 32768ed7ca2fa3a1c908133371bcfa9a5e6ec948 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 11 Feb 2025 18:42:07 +0000 Subject: [PATCH] - SV Publisher: added support for optional gmIdentity field --- src/sampled_values/sv_publisher.c | 104 +++++++++++++++++++----------- src/sampled_values/sv_publisher.h | 52 ++++++++++----- 2 files changed, 103 insertions(+), 53 deletions(-) diff --git a/src/sampled_values/sv_publisher.c b/src/sampled_values/sv_publisher.c index 90179cf6..b3f0f8b0 100644 --- a/src/sampled_values/sv_publisher.c +++ b/src/sampled_values/sv_publisher.c @@ -1,7 +1,7 @@ /* * sv_publisher.c * - * Copyright 2016-2024 Michael Zillgith + * Copyright 2016-2025 Michael Zillgith * * This file is part of libIEC61850. * @@ -21,14 +21,14 @@ * See COPYING file for the complete license text. */ -#include "stack_config.h" #include "libiec61850_platform_includes.h" +#include "stack_config.h" -#include #include "sv_publisher.h" +#include -#include "hal_ethernet.h" #include "ber_encoder.h" +#include "hal_ethernet.h" #include "r_session_internal.h" @@ -44,14 +44,17 @@ #define SV_MAX_MESSAGE_SIZE 1518 -struct sSVPublisher_ASDU { +struct sSVPublisher_ASDU +{ const char* svID; const char* datset; int dataSize; + /* flags to indicate presence of optional fields */ bool hasRefrTm; bool hasSmpRate; bool hasSmpMod; + bool hasGmIdentity; uint8_t* _dataBuffer; @@ -60,6 +63,8 @@ struct sSVPublisher_ASDU { uint16_t smpCntLimit; uint32_t confRev; + uint8_t gmIdentity[8]; + Timestamp* refrTm; uint8_t smpMod; uint16_t smpRate; @@ -70,7 +75,8 @@ struct sSVPublisher_ASDU { SVPublisher_ASDU _next; }; -struct sSVPublisher { +struct sSVPublisher +{ uint8_t* buffer; uint16_t appId; @@ -141,7 +147,7 @@ preparePacketBuffer(SVPublisher self, CommParameters* parameters, const char* in return false; } - self->buffer = (uint8_t*) GLOBAL_MALLOC(SV_MAX_MESSAGE_SIZE); + self->buffer = (uint8_t*)GLOBAL_MALLOC(SV_MAX_MESSAGE_SIZE); if (self->buffer) { @@ -201,7 +207,7 @@ preparePacketBuffer(SVPublisher self, CommParameters* parameters, const char* in static int encodeUInt16FixedSize(uint16_t value, uint8_t* buffer, int bufPos) { - uint8_t* valueArray = (uint8_t*) &value; + uint8_t* valueArray = (uint8_t*)&value; #if (ORDER_LITTLE_ENDIAN == 1) buffer[bufPos++] = valueArray[1]; @@ -217,7 +223,7 @@ encodeUInt16FixedSize(uint16_t value, uint8_t* buffer, int bufPos) static int encodeUInt32FixedSize(uint32_t value, uint8_t* buffer, int bufPos) { - uint8_t* valueArray = (uint8_t*) &value; + uint8_t* valueArray = (uint8_t*)&value; #if (ORDER_LITTLE_ENDIAN == 1) buffer[bufPos++] = valueArray[3]; @@ -237,7 +243,7 @@ encodeUInt32FixedSize(uint32_t value, uint8_t* buffer, int bufPos) static int encodeInt32FixedSize(int32_t value, uint8_t* buffer, int bufPos) { - uint8_t* valueArray = (uint8_t*) &value; + uint8_t* valueArray = (uint8_t*)&value; #if (ORDER_LITTLE_ENDIAN == 1) buffer[bufPos++] = valueArray[3]; @@ -257,7 +263,7 @@ encodeInt32FixedSize(int32_t value, uint8_t* buffer, int bufPos) static int encodeInt64FixedSize(int64_t value, uint8_t* buffer, int bufPos) { - uint8_t* valueArray = (uint8_t*) &value; + uint8_t* valueArray = (uint8_t*)&value; #if (ORDER_LITTLE_ENDIAN == 1) buffer[bufPos++] = valueArray[7]; @@ -286,12 +292,13 @@ encodeInt64FixedSize(int64_t value, uint8_t* buffer, int bufPos) SVPublisher SVPublisher_createRemote(RSession session, uint16_t appId) { - SVPublisher self = (SVPublisher) GLOBAL_CALLOC(1, sizeof(struct sSVPublisher)); + SVPublisher self = (SVPublisher)GLOBAL_CALLOC(1, sizeof(struct sSVPublisher)); - if (self) { + if (self) + { self->asduList = NULL; - self->buffer = (uint8_t*) GLOBAL_MALLOC(SV_MAX_MESSAGE_SIZE); + self->buffer = (uint8_t*)GLOBAL_MALLOC(SV_MAX_MESSAGE_SIZE); self->payloadStart = 0; self->remoteSession = session; @@ -309,7 +316,7 @@ SVPublisher_createRemote(RSession session, uint16_t appId) SVPublisher SVPublisher_createEx(CommParameters* parameters, const char* interfaceId, bool useVlanTag) { - SVPublisher self = (SVPublisher) GLOBAL_CALLOC(1, sizeof(struct sSVPublisher)); + SVPublisher self = (SVPublisher)GLOBAL_CALLOC(1, sizeof(struct sSVPublisher)); if (self) { @@ -321,7 +328,6 @@ SVPublisher_createEx(CommParameters* parameters, const char* interfaceId, bool u SVPublisher_destroy(self); self = NULL; } - } return self; @@ -337,25 +343,28 @@ SVPublisher_create(CommParameters* parameters, const char* interfaceId) SVPublisher_ASDU SVPublisher_addASDU(SVPublisher self, const char* svID, const char* datset, uint32_t confRev) { - SVPublisher_ASDU newAsdu = (SVPublisher_ASDU) GLOBAL_CALLOC(1, sizeof(struct sSVPublisher_ASDU)); - - newAsdu->svID = svID; - newAsdu->datset = datset; - newAsdu->confRev = confRev; - newAsdu->smpCntLimit = UINT16_MAX; - newAsdu->_next = NULL; + SVPublisher_ASDU newAsdu = (SVPublisher_ASDU)GLOBAL_CALLOC(1, sizeof(struct sSVPublisher_ASDU)); - /* append new ASDU to list */ - if (self->asduList == NULL) - self->asduList = newAsdu; - else + if (newAsdu) { - SVPublisher_ASDU lastAsdu = self->asduList; + newAsdu->svID = svID; + newAsdu->datset = datset; + newAsdu->confRev = confRev; + newAsdu->smpCntLimit = UINT16_MAX; + newAsdu->_next = NULL; + + /* append new ASDU to list */ + if (self->asduList == NULL) + self->asduList = newAsdu; + else + { + SVPublisher_ASDU lastAsdu = self->asduList; - while (lastAsdu->_next) - lastAsdu = lastAsdu->_next; + while (lastAsdu->_next) + lastAsdu = lastAsdu->_next; - lastAsdu->_next = newAsdu; + lastAsdu->_next = newAsdu; + } } return newAsdu; @@ -385,7 +394,7 @@ SVPublisher_ASDU_getEncodedSize(SVPublisher_ASDU self) /* refrTm */ if (self->hasRefrTm) - encodedSize += 10; /* ??? */ + encodedSize += 10; /* smpSynch */ encodedSize += 3; @@ -402,6 +411,10 @@ SVPublisher_ASDU_getEncodedSize(SVPublisher_ASDU self) if (self->hasSmpMod) encodedSize += 4; + /* gmIdentity */ + if (self->hasGmIdentity) + encodedSize += 10; + return encodedSize; } @@ -433,7 +446,7 @@ SVPublisher_ASDU_encodeToBuffer(SVPublisher_ASDU self, uint8_t* buffer, int bufP if (self->hasRefrTm) { bufPos = BerEncoder_encodeTL(0x84, 8, buffer, bufPos); - self->refrTm = (Timestamp*) (buffer + bufPos); + self->refrTm = (Timestamp*)(buffer + bufPos); bufPos += 8; } @@ -455,7 +468,7 @@ SVPublisher_ASDU_encodeToBuffer(SVPublisher_ASDU self, uint8_t* buffer, int bufP self->_dataBuffer = buffer + bufPos; bufPos += self->dataSize; /* data has to be inserted by user before sending message */ - + /* SmpMod */ if (self->hasSmpMod) { @@ -463,6 +476,12 @@ SVPublisher_ASDU_encodeToBuffer(SVPublisher_ASDU self, uint8_t* buffer, int bufP bufPos = encodeUInt16FixedSize(self->smpMod, buffer, bufPos); } + /* gmIdentity */ + if (self->hasGmIdentity) + { + bufPos = BerEncoder_encodeOctetString(0x89, self->gmIdentity, 8, buffer, bufPos); + } + return bufPos; } @@ -518,7 +537,8 @@ SVPublisher_setupComplete(SVPublisher self) size_t msgLength = payloadLength + 8; - if (self->lengthField != 0) { + if (self->lengthField != 0) + { int lengthIndex = self->lengthField; self->buffer[lengthIndex] = msgLength / 256; @@ -547,7 +567,8 @@ SVPublisher_publish(SVPublisher self) if (DEBUG_SV_PUBLISHER) printf("SV_PUBLISHER: send R-SV message\n"); - RSession_sendMessage(self->remoteSession, RSESSION_SPDU_ID_SV, self->simulation, self->appId, self->buffer, self->payloadLength); + RSession_sendMessage(self->remoteSession, RSESSION_SPDU_ID_SV, self->simulation, self->appId, self->buffer, + self->payloadLength); } #endif /* (CONFIG_IEC61850_R_SMV == 1) */ } @@ -647,7 +668,7 @@ SVPublisher_ASDU_addFLOAT(SVPublisher_ASDU self) void SVPublisher_ASDU_setFLOAT(SVPublisher_ASDU self, int index, float value) { - uint8_t* buf = (uint8_t*) &value; + uint8_t* buf = (uint8_t*)&value; #if (ORDER_LITTLE_ENDIAN == 1) BerEncoder_revertByteOrder(buf, 4); @@ -674,7 +695,7 @@ SVPublisher_ASDU_addFLOAT64(SVPublisher_ASDU self) void SVPublisher_ASDU_setFLOAT64(SVPublisher_ASDU self, int index, double value) { - uint8_t* buf = (uint8_t*) &value; + uint8_t* buf = (uint8_t*)&value; #if (ORDER_LITTLE_ENDIAN == 1) BerEncoder_revertByteOrder(buf, 8); @@ -818,3 +839,10 @@ SVPublisher_ASDU_setSmpSynch(SVPublisher_ASDU self, uint16_t smpSynch) self->smpSynch = smpSynch; *(self->smpSynchBuf) = self->smpSynch; } + +void +SVPublisher_ASDU_setGmIdentity(SVPublisher_ASDU self, uint8_t* gmIdentity) +{ + self->hasGmIdentity = true; + memcpy(self->gmIdentity, gmIdentity, 8); +} diff --git a/src/sampled_values/sv_publisher.h b/src/sampled_values/sv_publisher.h index ebb1e7fe..866db09b 100644 --- a/src/sampled_values/sv_publisher.h +++ b/src/sampled_values/sv_publisher.h @@ -1,7 +1,7 @@ /* * sv_publisher.h * - * Copyright 2016-2022 Michael Zillgith + * Copyright 2016-2025 Michael Zillgith * * This file is part of libIEC61850. * @@ -21,7 +21,6 @@ * See COPYING file for the complete license text. */ - #ifndef LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_H_ #define LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_H_ @@ -29,13 +28,15 @@ #include "r_session.h" #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif #ifndef GOOSE_SV_COMM_PARAMETERS #define GOOSE_SV_COMM_PARAMETERS -typedef struct sCommParameters { +typedef struct sCommParameters +{ uint8_t vlanPriority; uint16_t vlanId; uint16_t appId; @@ -75,6 +76,7 @@ typedef struct sSVPublisher_ASDU* SVPublisher_ASDU; * * \param[in] interfaceId the name of the interface over which the SV publisher should send SV packets. * \param[in] parameters optional parameters for setting VLAN options and destination MAC address. Use NULL for default values. + * * \return the new SV publisher instance. */ LIB61850_API SVPublisher @@ -84,8 +86,10 @@ SVPublisher_create(CommParameters* parameters, const char* interfaceId); * \brief Create a new IEC61850-9-2 Sampled Values publisher. * * \param[in] interfaceId the name of the interface over which the SV publisher should send SV packets. - * \param[in] parameters optional parameters for setting VLAN options and destination MAC address. Use NULL for default values. + * \param[in] parameters optional parameters for setting VLAN options and destination MAC address. Use NULL for default + * values. * \param[in] useVlanTags enable(true)/disable(false) VLAN tagging + * * \return the new SV publisher instance. */ LIB61850_API SVPublisher @@ -96,7 +100,7 @@ SVPublisher_createEx(CommParameters* parameters, const char* interfaceId, bool u * * \param session R-session protocol instance to use * \param appId the appID value to use - * + * * \return the new SVPublisher instance */ LIB61850_API SVPublisher @@ -107,7 +111,8 @@ SVPublisher_createRemote(RSession session, uint16_t appId); * * \param[in] svID * \param[in] datset - * \param[in] confRev Configuration revision number. Should be incremented each time that the configuration of the logical device changes. + * \param[in] confRev Configuration revision number. Should be incremented each time that the configuration of the + * logical device changes. * \return the new ASDU instance. */ LIB61850_API SVPublisher_ASDU @@ -159,6 +164,7 @@ SVPublisher_ASDU_resetBuffer(SVPublisher_ASDU self); * \brief Reserve memory for a signed 8-bit integer in the ASDU. * * \param[in] self the Sampled Values ASDU instance. + * * \return the offset in bytes within the ASDU data block. */ LIB61850_API int @@ -178,6 +184,7 @@ SVPublisher_ASDU_setINT8(SVPublisher_ASDU self, int index, int8_t value); * \brief Reserve memory for a signed 32-bit integer in the ASDU. * * \param[in] self the Sampled Values ASDU instance. + * * \return the offset in bytes within the ASDU data block. */ LIB61850_API int @@ -197,6 +204,7 @@ SVPublisher_ASDU_setINT32(SVPublisher_ASDU self, int index, int32_t value); * \brief Reserve memory for a signed 64-bit integer in the ASDU. * * \param[in] self the Sampled Values ASDU instance. + * * \return the offset in bytes of the new element within the ASDU data block. */ LIB61850_API int @@ -216,6 +224,7 @@ SVPublisher_ASDU_setINT64(SVPublisher_ASDU self, int index, int64_t value); * \brief Reserve memory for a single precision floating point number in the ASDU. * * \param[in] self the Sampled Values ASDU instance. + * * \return the offset in bytes of the new element within the ASDU data block. */ LIB61850_API int @@ -235,6 +244,7 @@ SVPublisher_ASDU_setFLOAT(SVPublisher_ASDU self, int index, float value); * \brief Reserve memory for a double precision floating point number in the ASDU. * * \param[in] self the Sampled Values ASDU instance. + * * \return the offset in bytes of the new element within the ASDU data block. */ LIB61850_API int @@ -254,6 +264,7 @@ SVPublisher_ASDU_setFLOAT64(SVPublisher_ASDU self, int index, double value); * \brief Reserve memory for a 64 bit time stamp in the ASDU * * \param[in] self the Sampled Values ASDU instance. + * * \return the offset in bytes of the new element within the ASDU data block. */ LIB61850_API int @@ -275,6 +286,7 @@ SVPublisher_ASDU_setTimestamp(SVPublisher_ASDU self, int index, Timestamp value) * NOTE: Quality is encoded as BITSTRING (4 byte) * * \param[in] self the Sampled Values ASDU instance. + * * \return the offset in bytes of the new element within the ASDU data block. */ LIB61850_API int @@ -310,9 +322,9 @@ SVPublisher_ASDU_getSmpCnt(SVPublisher_ASDU self); /** * \brief Increment the sample count attribute of the ASDU. * - * The parameter SmpCnt shall contain the values of a counter, which is incremented each time a new sample of the analogue value is taken. - * The sample values shall be kept in the right order. - * If the counter is used to indicate time consistency of various sampled values, the counter shall be reset by an external synchronization event. + * The parameter SmpCnt shall contain the values of a counter, which is incremented each time a new sample of the + * analogue value is taken. The sample values shall be kept in the right order. If the counter is used to indicate time + * consistency of various sampled values, the counter shall be reset by an external synchronization event. * * \param[in] self the Sampled Values ASDU instance. */ @@ -372,13 +384,14 @@ SVPublisher_ASDU_setRefrTmByTimestamp(SVPublisher_ASDU self, Timestamp* refrTm); /** * \brief Set the sample mode attribute of the ASDU. * - * The attribute SmpMod shall specify if the sample rate is defined in units of samples per nominal period, samples per second or seconds per sample. - * If it is missing, the default value is samples per period. + * The attribute SmpMod shall specify if the sample rate is defined in units of samples per nominal period, samples per + * second or seconds per sample. If it is missing, the default value is samples per period. * * NOTE: Function has to be called before calling \ref SVPublisher_setupComplete * * \param[in] self the Sampled Values ASDU instance. - * \param[in] smpMod one of IEC61850_SV_SMPMOD_PER_NOMINAL_PERIOD, IEC61850_SV_SMPMOD_SAMPLES_PER_SECOND or IEC61850_SV_SMPMOD_SECONDS_PER_SAMPLE + * \param[in] smpMod one of IEC61850_SV_SMPMOD_PER_NOMINAL_PERIOD, IEC61850_SV_SMPMOD_SAMPLES_PER_SECOND or + * IEC61850_SV_SMPMOD_SECONDS_PER_SAMPLE */ LIB61850_API void SVPublisher_ASDU_setSmpMod(SVPublisher_ASDU self, uint8_t smpMod); @@ -414,13 +427,22 @@ SVPublisher_ASDU_setSmpRate(SVPublisher_ASDU self, uint16_t smpRate); LIB61850_API void SVPublisher_ASDU_setSmpSynch(SVPublisher_ASDU self, uint16_t smpSynch); +/** + * \brief Set the gmIdentity value + * + * \param[in] self the Sampled Values ASDU instance. + * \param[in] gmIdentity the gmIdentity value (octet string/byte array of size 8 bytes) + */ +LIB61850_API void +SVPublisher_ASDU_setGmIdentity(SVPublisher_ASDU self, uint8_t* gmIdentity); + /**@} @}*/ #ifndef DEPRECATED #if defined(__GNUC__) || defined(__clang__) - #define DEPRECATED __attribute__((deprecated)) +#define DEPRECATED __attribute__((deprecated)) #else - #define DEPRECATED +#define DEPRECATED #endif #endif