- SV Publisher: added support for optional gmIdentity field

v1.6
Michael Zillgith 7 months ago
parent 899a2bbe2e
commit 32768ed7ca

@ -1,7 +1,7 @@
/* /*
* sv_publisher.c * sv_publisher.c
* *
* Copyright 2016-2024 Michael Zillgith * Copyright 2016-2025 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -21,14 +21,14 @@
* See COPYING file for the complete license text. * See COPYING file for the complete license text.
*/ */
#include "stack_config.h"
#include "libiec61850_platform_includes.h" #include "libiec61850_platform_includes.h"
#include "stack_config.h"
#include <stdbool.h>
#include "sv_publisher.h" #include "sv_publisher.h"
#include <stdbool.h>
#include "hal_ethernet.h"
#include "ber_encoder.h" #include "ber_encoder.h"
#include "hal_ethernet.h"
#include "r_session_internal.h" #include "r_session_internal.h"
@ -44,14 +44,17 @@
#define SV_MAX_MESSAGE_SIZE 1518 #define SV_MAX_MESSAGE_SIZE 1518
struct sSVPublisher_ASDU { struct sSVPublisher_ASDU
{
const char* svID; const char* svID;
const char* datset; const char* datset;
int dataSize; int dataSize;
/* flags to indicate presence of optional fields */
bool hasRefrTm; bool hasRefrTm;
bool hasSmpRate; bool hasSmpRate;
bool hasSmpMod; bool hasSmpMod;
bool hasGmIdentity;
uint8_t* _dataBuffer; uint8_t* _dataBuffer;
@ -60,6 +63,8 @@ struct sSVPublisher_ASDU {
uint16_t smpCntLimit; uint16_t smpCntLimit;
uint32_t confRev; uint32_t confRev;
uint8_t gmIdentity[8];
Timestamp* refrTm; Timestamp* refrTm;
uint8_t smpMod; uint8_t smpMod;
uint16_t smpRate; uint16_t smpRate;
@ -70,7 +75,8 @@ struct sSVPublisher_ASDU {
SVPublisher_ASDU _next; SVPublisher_ASDU _next;
}; };
struct sSVPublisher { struct sSVPublisher
{
uint8_t* buffer; uint8_t* buffer;
uint16_t appId; uint16_t appId;
@ -141,7 +147,7 @@ preparePacketBuffer(SVPublisher self, CommParameters* parameters, const char* in
return false; 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) if (self->buffer)
{ {
@ -201,7 +207,7 @@ preparePacketBuffer(SVPublisher self, CommParameters* parameters, const char* in
static int static int
encodeUInt16FixedSize(uint16_t value, uint8_t* buffer, int bufPos) 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) #if (ORDER_LITTLE_ENDIAN == 1)
buffer[bufPos++] = valueArray[1]; buffer[bufPos++] = valueArray[1];
@ -217,7 +223,7 @@ encodeUInt16FixedSize(uint16_t value, uint8_t* buffer, int bufPos)
static int static int
encodeUInt32FixedSize(uint32_t value, uint8_t* buffer, int bufPos) 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) #if (ORDER_LITTLE_ENDIAN == 1)
buffer[bufPos++] = valueArray[3]; buffer[bufPos++] = valueArray[3];
@ -237,7 +243,7 @@ encodeUInt32FixedSize(uint32_t value, uint8_t* buffer, int bufPos)
static int static int
encodeInt32FixedSize(int32_t value, uint8_t* buffer, int bufPos) 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) #if (ORDER_LITTLE_ENDIAN == 1)
buffer[bufPos++] = valueArray[3]; buffer[bufPos++] = valueArray[3];
@ -257,7 +263,7 @@ encodeInt32FixedSize(int32_t value, uint8_t* buffer, int bufPos)
static int static int
encodeInt64FixedSize(int64_t value, uint8_t* buffer, int bufPos) 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) #if (ORDER_LITTLE_ENDIAN == 1)
buffer[bufPos++] = valueArray[7]; buffer[bufPos++] = valueArray[7];
@ -286,12 +292,13 @@ encodeInt64FixedSize(int64_t value, uint8_t* buffer, int bufPos)
SVPublisher SVPublisher
SVPublisher_createRemote(RSession session, uint16_t appId) 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->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->payloadStart = 0;
self->remoteSession = session; self->remoteSession = session;
@ -309,7 +316,7 @@ SVPublisher_createRemote(RSession session, uint16_t appId)
SVPublisher SVPublisher
SVPublisher_createEx(CommParameters* parameters, const char* interfaceId, bool useVlanTag) 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) if (self)
{ {
@ -321,7 +328,6 @@ SVPublisher_createEx(CommParameters* parameters, const char* interfaceId, bool u
SVPublisher_destroy(self); SVPublisher_destroy(self);
self = NULL; self = NULL;
} }
} }
return self; return self;
@ -337,8 +343,10 @@ SVPublisher_create(CommParameters* parameters, const char* interfaceId)
SVPublisher_ASDU SVPublisher_ASDU
SVPublisher_addASDU(SVPublisher self, const char* svID, const char* datset, uint32_t confRev) 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)); SVPublisher_ASDU newAsdu = (SVPublisher_ASDU)GLOBAL_CALLOC(1, sizeof(struct sSVPublisher_ASDU));
if (newAsdu)
{
newAsdu->svID = svID; newAsdu->svID = svID;
newAsdu->datset = datset; newAsdu->datset = datset;
newAsdu->confRev = confRev; newAsdu->confRev = confRev;
@ -357,6 +365,7 @@ SVPublisher_addASDU(SVPublisher self, const char* svID, const char* datset, uint
lastAsdu->_next = newAsdu; lastAsdu->_next = newAsdu;
} }
}
return newAsdu; return newAsdu;
} }
@ -385,7 +394,7 @@ SVPublisher_ASDU_getEncodedSize(SVPublisher_ASDU self)
/* refrTm */ /* refrTm */
if (self->hasRefrTm) if (self->hasRefrTm)
encodedSize += 10; /* ??? */ encodedSize += 10;
/* smpSynch */ /* smpSynch */
encodedSize += 3; encodedSize += 3;
@ -402,6 +411,10 @@ SVPublisher_ASDU_getEncodedSize(SVPublisher_ASDU self)
if (self->hasSmpMod) if (self->hasSmpMod)
encodedSize += 4; encodedSize += 4;
/* gmIdentity */
if (self->hasGmIdentity)
encodedSize += 10;
return encodedSize; return encodedSize;
} }
@ -433,7 +446,7 @@ SVPublisher_ASDU_encodeToBuffer(SVPublisher_ASDU self, uint8_t* buffer, int bufP
if (self->hasRefrTm) if (self->hasRefrTm)
{ {
bufPos = BerEncoder_encodeTL(0x84, 8, buffer, bufPos); bufPos = BerEncoder_encodeTL(0x84, 8, buffer, bufPos);
self->refrTm = (Timestamp*) (buffer + bufPos); self->refrTm = (Timestamp*)(buffer + bufPos);
bufPos += 8; bufPos += 8;
} }
@ -463,6 +476,12 @@ SVPublisher_ASDU_encodeToBuffer(SVPublisher_ASDU self, uint8_t* buffer, int bufP
bufPos = encodeUInt16FixedSize(self->smpMod, buffer, bufPos); bufPos = encodeUInt16FixedSize(self->smpMod, buffer, bufPos);
} }
/* gmIdentity */
if (self->hasGmIdentity)
{
bufPos = BerEncoder_encodeOctetString(0x89, self->gmIdentity, 8, buffer, bufPos);
}
return bufPos; return bufPos;
} }
@ -518,7 +537,8 @@ SVPublisher_setupComplete(SVPublisher self)
size_t msgLength = payloadLength + 8; size_t msgLength = payloadLength + 8;
if (self->lengthField != 0) { if (self->lengthField != 0)
{
int lengthIndex = self->lengthField; int lengthIndex = self->lengthField;
self->buffer[lengthIndex] = msgLength / 256; self->buffer[lengthIndex] = msgLength / 256;
@ -547,7 +567,8 @@ SVPublisher_publish(SVPublisher self)
if (DEBUG_SV_PUBLISHER) if (DEBUG_SV_PUBLISHER)
printf("SV_PUBLISHER: send R-SV message\n"); 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) */ #endif /* (CONFIG_IEC61850_R_SMV == 1) */
} }
@ -647,7 +668,7 @@ SVPublisher_ASDU_addFLOAT(SVPublisher_ASDU self)
void void
SVPublisher_ASDU_setFLOAT(SVPublisher_ASDU self, int index, float value) 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) #if (ORDER_LITTLE_ENDIAN == 1)
BerEncoder_revertByteOrder(buf, 4); BerEncoder_revertByteOrder(buf, 4);
@ -674,7 +695,7 @@ SVPublisher_ASDU_addFLOAT64(SVPublisher_ASDU self)
void void
SVPublisher_ASDU_setFLOAT64(SVPublisher_ASDU self, int index, double value) 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) #if (ORDER_LITTLE_ENDIAN == 1)
BerEncoder_revertByteOrder(buf, 8); BerEncoder_revertByteOrder(buf, 8);
@ -818,3 +839,10 @@ SVPublisher_ASDU_setSmpSynch(SVPublisher_ASDU self, uint16_t smpSynch)
self->smpSynch = smpSynch; self->smpSynch = smpSynch;
*(self->smpSynchBuf) = self->smpSynch; *(self->smpSynchBuf) = self->smpSynch;
} }
void
SVPublisher_ASDU_setGmIdentity(SVPublisher_ASDU self, uint8_t* gmIdentity)
{
self->hasGmIdentity = true;
memcpy(self->gmIdentity, gmIdentity, 8);
}

@ -1,7 +1,7 @@
/* /*
* sv_publisher.h * sv_publisher.h
* *
* Copyright 2016-2022 Michael Zillgith * Copyright 2016-2025 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -21,7 +21,6 @@
* See COPYING file for the complete license text. * See COPYING file for the complete license text.
*/ */
#ifndef LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_H_ #ifndef LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_H_
#define LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_H_ #define LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_H_
@ -29,13 +28,15 @@
#include "r_session.h" #include "r_session.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C"
{
#endif #endif
#ifndef GOOSE_SV_COMM_PARAMETERS #ifndef GOOSE_SV_COMM_PARAMETERS
#define GOOSE_SV_COMM_PARAMETERS #define GOOSE_SV_COMM_PARAMETERS
typedef struct sCommParameters { typedef struct sCommParameters
{
uint8_t vlanPriority; uint8_t vlanPriority;
uint16_t vlanId; uint16_t vlanId;
uint16_t appId; 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] 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.
*
* \return the new SV publisher instance. * \return the new SV publisher instance.
*/ */
LIB61850_API SVPublisher LIB61850_API SVPublisher
@ -84,8 +86,10 @@ SVPublisher_create(CommParameters* parameters, const char* interfaceId);
* \brief Create a new IEC61850-9-2 Sampled Values publisher. * \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] 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 * \param[in] useVlanTags enable(true)/disable(false) VLAN tagging
*
* \return the new SV publisher instance. * \return the new SV publisher instance.
*/ */
LIB61850_API SVPublisher LIB61850_API SVPublisher
@ -107,7 +111,8 @@ SVPublisher_createRemote(RSession session, uint16_t appId);
* *
* \param[in] svID * \param[in] svID
* \param[in] datset * \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. * \return the new ASDU instance.
*/ */
LIB61850_API SVPublisher_ASDU 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. * \brief Reserve memory for a signed 8-bit integer in the ASDU.
* *
* \param[in] self the Sampled Values ASDU instance. * \param[in] self the Sampled Values ASDU instance.
*
* \return the offset in bytes within the ASDU data block. * \return the offset in bytes within the ASDU data block.
*/ */
LIB61850_API int 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. * \brief Reserve memory for a signed 32-bit integer in the ASDU.
* *
* \param[in] self the Sampled Values ASDU instance. * \param[in] self the Sampled Values ASDU instance.
*
* \return the offset in bytes within the ASDU data block. * \return the offset in bytes within the ASDU data block.
*/ */
LIB61850_API int 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. * \brief Reserve memory for a signed 64-bit integer in the ASDU.
* *
* \param[in] self the Sampled Values ASDU instance. * \param[in] self the Sampled Values ASDU instance.
*
* \return the offset in bytes of the new element within the ASDU data block. * \return the offset in bytes of the new element within the ASDU data block.
*/ */
LIB61850_API int 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. * \brief Reserve memory for a single precision floating point number in the ASDU.
* *
* \param[in] self the Sampled Values ASDU instance. * \param[in] self the Sampled Values ASDU instance.
*
* \return the offset in bytes of the new element within the ASDU data block. * \return the offset in bytes of the new element within the ASDU data block.
*/ */
LIB61850_API int 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. * \brief Reserve memory for a double precision floating point number in the ASDU.
* *
* \param[in] self the Sampled Values ASDU instance. * \param[in] self the Sampled Values ASDU instance.
*
* \return the offset in bytes of the new element within the ASDU data block. * \return the offset in bytes of the new element within the ASDU data block.
*/ */
LIB61850_API int 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 * \brief Reserve memory for a 64 bit time stamp in the ASDU
* *
* \param[in] self the Sampled Values ASDU instance. * \param[in] self the Sampled Values ASDU instance.
*
* \return the offset in bytes of the new element within the ASDU data block. * \return the offset in bytes of the new element within the ASDU data block.
*/ */
LIB61850_API int 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) * NOTE: Quality is encoded as BITSTRING (4 byte)
* *
* \param[in] self the Sampled Values ASDU instance. * \param[in] self the Sampled Values ASDU instance.
*
* \return the offset in bytes of the new element within the ASDU data block. * \return the offset in bytes of the new element within the ASDU data block.
*/ */
LIB61850_API int LIB61850_API int
@ -310,9 +322,9 @@ SVPublisher_ASDU_getSmpCnt(SVPublisher_ASDU self);
/** /**
* \brief Increment the sample count attribute of the ASDU. * \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 parameter SmpCnt shall contain the values of a counter, which is incremented each time a new sample of the
* The sample values shall be kept in the right order. * analogue value is taken. The sample values shall be kept in the right order. If the counter is used to indicate time
* If the counter is used to indicate time consistency of various sampled values, the counter shall be reset by an external synchronization event. * consistency of various sampled values, the counter shall be reset by an external synchronization event.
* *
* \param[in] self the Sampled Values ASDU instance. * \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. * \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. * The attribute SmpMod shall specify if the sample rate is defined in units of samples per nominal period, samples per
* If it is missing, the default value is samples per period. * 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 * NOTE: Function has to be called before calling \ref SVPublisher_setupComplete
* *
* \param[in] self the Sampled Values ASDU instance. * \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 LIB61850_API void
SVPublisher_ASDU_setSmpMod(SVPublisher_ASDU self, uint8_t smpMod); 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 LIB61850_API void
SVPublisher_ASDU_setSmpSynch(SVPublisher_ASDU self, uint16_t smpSynch); 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 #ifndef DEPRECATED
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#define DEPRECATED __attribute__((deprecated)) #define DEPRECATED __attribute__((deprecated))
#else #else
#define DEPRECATED #define DEPRECATED
#endif #endif
#endif #endif

Loading…
Cancel
Save