- removed old sv code
parent
6e260b88a4
commit
61714b5907
@ -1,28 +0,0 @@
|
|||||||
IEC61850 DEFINITIONS ::= BEGIN
|
|
||||||
IMPORTS Data FROM ISO-IEC-9506-2
|
|
||||||
IEC 61850-9-2 Specific Protocol ::= CHOICE {
|
|
||||||
savPdu [APPLICATION 0] IMPLICIT SavPdu,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SavPdu ::= SEQUENCE {
|
|
||||||
noASDU [0] IMPLICIT INTEGER(1..65535),
|
|
||||||
security [1] ANY OPTIONAL,
|
|
||||||
asdu [2] IMPLICIT SEQUENCE OF ASDU
|
|
||||||
}
|
|
||||||
|
|
||||||
ASDU ::= SEQUENCE {
|
|
||||||
svID [0] IMPLICIT VisibleString,
|
|
||||||
datset [1] IMPLICIT VisibleString OPTIONAL,
|
|
||||||
smpCnt [2] IMPLICIT OCTET STRING (SIZE(2)),
|
|
||||||
confRev [3] IMPLICIT OCTET STRING (SIZE(4)),
|
|
||||||
refrTm [4] IMPLICIT UtcTime OPTIONAL,
|
|
||||||
smpSynch [5] IMPLICIT OCTET STRING (SIZE(1)),
|
|
||||||
smpRate [6] IMPLICIT OCTET STRING (SIZE(2)) OPTIONAL,
|
|
||||||
sample [7] IMPLICIT OCTET STRING (SIZE(n)),
|
|
||||||
smpMod [8] IMPLICIT OCTET STRING (SIZE(2)) OPTIONAL
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,348 +0,0 @@
|
|||||||
/*
|
|
||||||
* sv_publisher.c
|
|
||||||
*
|
|
||||||
* Copyright 2013 Michael Zillgith
|
|
||||||
*
|
|
||||||
* This file is part of libIEC61850.
|
|
||||||
*
|
|
||||||
* libIEC61850 is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* libIEC61850 is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* See COPYING file for the complete license text.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "stack_config.h"
|
|
||||||
#include "libiec61850_platform_includes.h"
|
|
||||||
|
|
||||||
#define CONFIG_SV_DEFAULT_DST_ADDRESS CONFIG_GOOSE_DEFAULT_DST_ADDRESS
|
|
||||||
|
|
||||||
#define CONFIG_SV_DEFAULT_PRIORITY 4
|
|
||||||
#define CONFIG_SV_DEFAULT_VLAN_ID 0
|
|
||||||
#define CONFIG_SV_DEFAULT_APPID 0x4000
|
|
||||||
|
|
||||||
#define SV_MAX_MESSAGE_SIZE 1518
|
|
||||||
|
|
||||||
typedef struct sSampledValuesPublisher* SampledValuesPublisher;
|
|
||||||
|
|
||||||
struct sSampledValuesPublisher {
|
|
||||||
uint8_t* buffer;
|
|
||||||
uint16_t appId;
|
|
||||||
EthernetSocket ethernetSocket;
|
|
||||||
int lengthField; /* can probably be removed since packets have fixed size! */
|
|
||||||
int payloadStart;
|
|
||||||
|
|
||||||
char* id;
|
|
||||||
char* dataSetRef; /* date set reference */
|
|
||||||
|
|
||||||
uint16_t smpCount; /* sample counter - reset by sync */
|
|
||||||
uint32_t confRev; /* Configuration revision according to CB */
|
|
||||||
uint8_t smpSynch; /* Synchronization status */
|
|
||||||
uint16_t smpRate;
|
|
||||||
|
|
||||||
uint64_t refreshTime; /* local buffer refresh time */
|
|
||||||
MmsValue* refTime;
|
|
||||||
|
|
||||||
bool hasDataSetName; /* optional fields in sv asdu */
|
|
||||||
bool hasRefreshTime;
|
|
||||||
bool hasSampleRate;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
preparePacketBuffer(SampledValuesPublisher self, CommParameters* parameters, char* interfaceID)
|
|
||||||
{
|
|
||||||
//TODO can GOOSE code be reused?
|
|
||||||
|
|
||||||
uint8_t srcAddr[6];
|
|
||||||
|
|
||||||
if (interfaceID != NULL)
|
|
||||||
Ethernet_getInterfaceMACAddress(interfaceID, srcAddr);
|
|
||||||
else
|
|
||||||
Ethernet_getInterfaceMACAddress(CONFIG_ETHERNET_INTERFACE_ID, srcAddr);
|
|
||||||
|
|
||||||
uint8_t defaultDstAddr[] = CONFIG_SV_DEFAULT_DST_ADDRESS;
|
|
||||||
|
|
||||||
uint8_t* dstAddr;
|
|
||||||
uint8_t priority;
|
|
||||||
uint16_t vlanId;
|
|
||||||
uint16_t appId;
|
|
||||||
|
|
||||||
if (parameters == NULL) {
|
|
||||||
dstAddr = defaultDstAddr;
|
|
||||||
priority = CONFIG_SV_DEFAULT_PRIORITY;
|
|
||||||
vlanId = CONFIG_SV_DEFAULT_VLAN_ID;
|
|
||||||
appId = CONFIG_SV_DEFAULT_APPID;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
dstAddr = parameters->dstAddress;
|
|
||||||
priority = parameters->vlanPriority;
|
|
||||||
vlanId = parameters->vlanId;
|
|
||||||
appId = parameters->appId;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interfaceID != NULL)
|
|
||||||
self->ethernetSocket = Ethernet_createSocket(interfaceID, dstAddr);
|
|
||||||
else
|
|
||||||
self->ethernetSocket = Ethernet_createSocket(CONFIG_ETHERNET_INTERFACE_ID, dstAddr);
|
|
||||||
|
|
||||||
self->buffer = (uint8_t*) GLOBAL_MALLOC(SV_MAX_MESSAGE_SIZE);
|
|
||||||
|
|
||||||
memcpy(self->buffer, dstAddr, 6);
|
|
||||||
memcpy(self->buffer + 6, srcAddr, 6);
|
|
||||||
|
|
||||||
int bufPos = 12;
|
|
||||||
|
|
||||||
/* Priority tag - IEEE 802.1Q */
|
|
||||||
self->buffer[bufPos++] = 0x81;
|
|
||||||
self->buffer[bufPos++] = 0x00;
|
|
||||||
|
|
||||||
uint8_t tci1 = priority << 5;
|
|
||||||
tci1 += vlanId / 256;
|
|
||||||
|
|
||||||
uint8_t tci2 = vlanId % 256;
|
|
||||||
|
|
||||||
self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */
|
|
||||||
self->buffer[bufPos++] = tci2; /* VLAN-ID */
|
|
||||||
|
|
||||||
/* EtherType Sampled Values */
|
|
||||||
self->buffer[bufPos++] = 0x88;
|
|
||||||
self->buffer[bufPos++] = 0xBa;
|
|
||||||
|
|
||||||
/* APPID */
|
|
||||||
self->buffer[bufPos++] = appId / 256;
|
|
||||||
self->buffer[bufPos++] = appId % 256;
|
|
||||||
|
|
||||||
self->lengthField = bufPos;
|
|
||||||
|
|
||||||
/* Length */
|
|
||||||
self->buffer[bufPos++] = 0x00;
|
|
||||||
self->buffer[bufPos++] = 0x08;
|
|
||||||
|
|
||||||
/* Reserved1 */
|
|
||||||
self->buffer[bufPos++] = 0x00;
|
|
||||||
self->buffer[bufPos++] = 0x00;
|
|
||||||
|
|
||||||
/* Reserved2 */
|
|
||||||
self->buffer[bufPos++] = 0x00;
|
|
||||||
self->buffer[bufPos++] = 0x00;
|
|
||||||
|
|
||||||
self->payloadStart = bufPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
encodeUInt16WithTLFixedSize(uint8_t tag, uint16_t value, uint8_t* buffer, int bufPos)
|
|
||||||
{
|
|
||||||
uint8_t* valueArray = (uint8_t*) &value;
|
|
||||||
|
|
||||||
buffer[bufPos++] = tag;
|
|
||||||
buffer[bufPos++] = 2;
|
|
||||||
|
|
||||||
#if (ORDER_LITTLE_ENDIAN == 1)
|
|
||||||
buffer[bufPos++] = valueArray[1];
|
|
||||||
buffer[bufPos++] = valueArray[0];
|
|
||||||
#else
|
|
||||||
buffer[bufPos++] = valueArray[0];
|
|
||||||
buffer[bufPos++] = valueArray[1];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return bufPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
encodeUInt32WithTLFixedSize(uint8_t tag, uint32_t value, uint8_t* buffer, int bufPos)
|
|
||||||
{
|
|
||||||
uint8_t* valueArray = (uint8_t*) &value;
|
|
||||||
|
|
||||||
buffer[bufPos++] = tag;
|
|
||||||
buffer[bufPos++] = 2;
|
|
||||||
|
|
||||||
#if (ORDER_LITTLE_ENDIAN == 1)
|
|
||||||
buffer[bufPos++] = valueArray[3];
|
|
||||||
buffer[bufPos++] = valueArray[2];
|
|
||||||
buffer[bufPos++] = valueArray[1];
|
|
||||||
buffer[bufPos++] = valueArray[0];
|
|
||||||
#else
|
|
||||||
buffer[bufPos++] = valueArray[0];
|
|
||||||
buffer[bufPos++] = valueArray[1];
|
|
||||||
buffer[bufPos++] = valueArray[2];
|
|
||||||
buffer[bufPos++] = valueArray[3];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return bufPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
determineEncodedMmsValueSize(MmsValue* value)
|
|
||||||
{
|
|
||||||
switch (MmsValue_getType(value)) {
|
|
||||||
case MMS_ARRAY:
|
|
||||||
case MMS_STRUCTURE: {
|
|
||||||
int compCount = value->value.structure.size;
|
|
||||||
int i;
|
|
||||||
int encodedSize = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < compCount; i++) {
|
|
||||||
encodedSize += determineEncodedMmsValueSize(value->value.structure.components[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return encodedSize;
|
|
||||||
}
|
|
||||||
case MMS_INTEGER:
|
|
||||||
return value->value.integer->maxSize;
|
|
||||||
break;
|
|
||||||
case MMS_UNSIGNED:
|
|
||||||
return value->value.unsignedInteger->maxSize;
|
|
||||||
case MMS_FLOAT:
|
|
||||||
return value->value.floatingPoint.formatWidth / 8;
|
|
||||||
case MMS_BIT_STRING:
|
|
||||||
return 4;
|
|
||||||
case MMS_UTC_TIME:
|
|
||||||
return 8;
|
|
||||||
case MMS_BINARY_TIME:
|
|
||||||
return 6;
|
|
||||||
case MMS_BOOLEAN:
|
|
||||||
return 1;
|
|
||||||
default:
|
|
||||||
printf("Data type not supported for sampled values service!\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
encodeMmsValue(MmsValue* value, uint8_t* buffer, int bufPos)
|
|
||||||
{
|
|
||||||
switch (MmsValue_getType(value)) {
|
|
||||||
case MMS_ARRAY:
|
|
||||||
case MMS_STRUCTURE: {
|
|
||||||
int compCount = value->value.structure.size;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < compCount; i++) {
|
|
||||||
bufPos = encodeMmsValue(value->value.structure.components[i], buffer, bufPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
return bufPos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
createSVPayload(SampledValuesPublisher self, LinkedList dataSetValues, uint8_t* buffer, int maxPayloadSize) {
|
|
||||||
|
|
||||||
/* Step 1 - calculate length fields */
|
|
||||||
uint32_t asduLength = 0;
|
|
||||||
|
|
||||||
asduLength += BerEncoder_determineEncodedStringSize(self->id);
|
|
||||||
|
|
||||||
if (self->hasDataSetName)
|
|
||||||
asduLength += BerEncoder_determineEncodedStringSize(self->dataSetRef);
|
|
||||||
|
|
||||||
asduLength += 4 + 6 + 3 + 4; /* for smpCnt + confRev + smpSynch + smpMod */
|
|
||||||
|
|
||||||
if (self->hasRefreshTime)
|
|
||||||
asduLength += 10; /* for refrTim */
|
|
||||||
|
|
||||||
if (self->hasSampleRate)
|
|
||||||
asduLength += 4; /* for smpRate */
|
|
||||||
|
|
||||||
uint32_t numberOfDataSetEntries = LinkedList_size(dataSetValues);
|
|
||||||
|
|
||||||
asduLength += 2 + BerEncoder_UInt32determineEncodedSize(numberOfDataSetEntries);
|
|
||||||
|
|
||||||
uint32_t dataSetSize = 0;
|
|
||||||
|
|
||||||
LinkedList element = LinkedList_getNext(dataSetValues);
|
|
||||||
|
|
||||||
while (element != NULL) {
|
|
||||||
MmsValue* dataSetEntry = (MmsValue*) element->data;
|
|
||||||
|
|
||||||
dataSetSize += determineEncodedMmsValueSize(dataSetEntry);
|
|
||||||
|
|
||||||
element = LinkedList_getNext(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t allDataSize = dataSetSize + BerEncoder_determineLengthSize(dataSetSize) + 1;
|
|
||||||
|
|
||||||
asduLength += allDataSize;
|
|
||||||
|
|
||||||
uint32_t sequenceOfAsduLength = 1 + BerEncoder_DetermineLengthSize(asduLength) + asduLength;
|
|
||||||
|
|
||||||
uint32_t apduLength = 1 + BerEncoder_DetermineLengthSize(sequenceOfAsduLength) + sequenceOfAsduLength;
|
|
||||||
|
|
||||||
//TODO add length of ASDU header
|
|
||||||
|
|
||||||
if (apduLength > maxPayloadSize)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* Step 2 - encode to buffer */
|
|
||||||
|
|
||||||
int bufPos = 0;
|
|
||||||
|
|
||||||
/* Encode SV PDU */
|
|
||||||
bufPos = BerEncoder_encodeTL(0x60, apduLength, buffer, bufPos);
|
|
||||||
|
|
||||||
/* Encode No. of ASDUs */
|
|
||||||
bufPos = encodeUInt16WithTLFixedSize(0x80, 1, buffer, bufPos);
|
|
||||||
//bufPos = BerEncoder_encodeUInt32WithTL(0x80, 1, buffer, bufPos);
|
|
||||||
|
|
||||||
/* Encode Sequence of ASDUs */
|
|
||||||
bufPos = BerEncoder_encodeTL(0xa2, sequenceOfAsduLength, buffer, bufPos);
|
|
||||||
|
|
||||||
/* Encode ASDU */
|
|
||||||
bufPos = BerEncoder_encodeTL(0x30, asduLength, buffer, bufPos);
|
|
||||||
|
|
||||||
/* Encode svID */
|
|
||||||
bufPos = BerEncoder_encodeStringWithTag(0x80, self->id, buffer, bufPos);
|
|
||||||
|
|
||||||
/* Encode dataSetRef */
|
|
||||||
if (self->hasDataSetName)
|
|
||||||
bufPos = BerEncoder_encodeStringWithTag(0x81, self->dataSetRef, buffer, bufPos);
|
|
||||||
|
|
||||||
/* Encode smpCnt */
|
|
||||||
bufPos = encodeUInt16WithTLFixedSize(0x82, self->smpCount, buffer, bufPos);
|
|
||||||
|
|
||||||
/* Encode confRev */
|
|
||||||
bufPos = encodeUInt32WithTLFixedSize(0x83, self->confRev, buffer, bufPos);
|
|
||||||
|
|
||||||
/* Encode refreshTime */
|
|
||||||
if (self->hasRefreshTime) {
|
|
||||||
MmsValue_setUtcTimeMs(self->refTime, self->refreshTime);
|
|
||||||
bufPos = BerEncoder_encodeOctetString(0x84, self->refTime->value.utcTime, 8, buffer, bufPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Encode smpSynch */
|
|
||||||
bufPos = BerEncoder_encodeTL(0x85, dataSetSize, buffer, bufPos);
|
|
||||||
buffer[bufPos++] = self->smpSynch;
|
|
||||||
|
|
||||||
/* Encode smpRate */
|
|
||||||
if (self->hasSampleRate) {
|
|
||||||
bufPos = encodeUInt16WithTLFixedSize(0x86, self->smpRate, buffer, bufPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Encode sequence of data */
|
|
||||||
bufPos = BerEncoder_encodeTL(0x87, dataSetSize, buffer, bufPos);
|
|
||||||
|
|
||||||
/* Encode data set entries */
|
|
||||||
element = LinkedList_getNext(dataSetValues);
|
|
||||||
|
|
||||||
while (element != NULL) {
|
|
||||||
MmsValue* dataSetEntry = (MmsValue*) element->data;
|
|
||||||
|
|
||||||
bufPos = mmsServer_encodeAccessResult(dataSetEntry, buffer, bufPos, true);
|
|
||||||
|
|
||||||
element = LinkedList_getNext(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
return bufPos;
|
|
||||||
}
|
|
Loading…
Reference in New Issue