- 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