- 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
*
* 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 <stdbool.h>
#include "sv_publisher.h"
#include <stdbool.h>
#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);
}

@ -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

Loading…
Cancel
Save