From dda1bad0071b5609342f721edfd8030144283821 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Mon, 16 Nov 2015 10:51:45 +0100 Subject: [PATCH] - extended documentation for SV subscriber --- src/iec61850/client/client_sv_control.c | 358 ++++++++++++++++++++++++ src/sampled_values/sv_subscriber.h | 41 +++ 2 files changed, 399 insertions(+) create mode 100644 src/iec61850/client/client_sv_control.c diff --git a/src/iec61850/client/client_sv_control.c b/src/iec61850/client/client_sv_control.c new file mode 100644 index 00000000..6a2cfb6d --- /dev/null +++ b/src/iec61850/client/client_sv_control.c @@ -0,0 +1,358 @@ +/* + * client_sv_control.c + * + * Copyright 2015 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 . + * + * See COPYING file for the complete license text. + */ + +#include "iec61850_client.h" + +#include "stack_config.h" + +#include "ied_connection_private.h" + +#include "libiec61850_platform_includes.h" + +struct sClientSVControlBlock { + IedConnection connection; + bool isMulticast; + char* reference; + IedClientError lastError; +}; + +ClientSVControlBlock +ClientSVControlBlock_create(IedConnection connection, const char* reference) +{ + bool isMulticast = false; + + /* Check if CB exists and if it is a unicast or mulitcast CB */ + IedClientError error; + MmsValue* value = IedConnection_readObject(connection, &error, reference, IEC61850_FC_MS); + + if (value != NULL) { + isMulticast = true; + MmsValue_delete(value); + } + else { + value = IedConnection_readObject(connection, &error, reference, IEC61850_FC_US); + + if (value == NULL) + return NULL; + + MmsValue_delete(value); + } + + ClientSVControlBlock self = (ClientSVControlBlock) GLOBAL_CALLOC(1, sizeof(struct sClientSVControlBlock)); + + if (self) { + self->connection = connection; + self->reference = copyString(reference); + self->isMulticast = isMulticast; + } + + return self; +} + +void +ClientSVControlBlock_destroy(ClientSVControlBlock self) +{ + if (self) { + GLOBAL_FREEMEM(self->reference); + GLOBAL_FREEMEM(self); + } +} + + +IedClientError +ClientSVControlBlock_getLastComError(ClientSVControlBlock self) +{ + return self->lastError; +} + +bool +ClientSVControlBlock_isMulticast(ClientSVControlBlock self) +{ + return self->isMulticast; +} + +static bool +setBooleanVariable(ClientSVControlBlock self, const char* varName, bool value) +{ + char refBuf[130]; + + strcpy(refBuf, self->reference); + strcat(refBuf, "."); + strcat(refBuf, varName); + + self->lastError = IED_ERROR_OK; + + if (self->isMulticast) + IedConnection_writeBooleanValue(self->connection, &(self->lastError), refBuf, IEC61850_FC_MS, value); + else + IedConnection_writeBooleanValue(self->connection, &(self->lastError), refBuf, IEC61850_FC_US, value); + + + if (self->lastError == IED_ERROR_OK) + return true; + else + return false; +} + +bool +ClientSVControlBlock_setSvEna(ClientSVControlBlock self, bool svEna) +{ + return setBooleanVariable(self, "SvEna", svEna); +} + +bool +ClientSVControlBlock_setResv(ClientSVControlBlock self, bool svEna) +{ + if (self->isMulticast == false) + return setBooleanVariable(self, "SvEna", svEna); + else + return false; +} + +static bool +readBooleanVariable(ClientSVControlBlock self, const char* varName) +{ + char refBuf[130]; + + strcpy(refBuf, self->reference); + strcat(refBuf, "."); + strcat(refBuf, varName); + + self->lastError = IED_ERROR_OK; + + bool retVal; + + if (self->isMulticast) + retVal = IedConnection_readBooleanValue(self->connection, &(self->lastError), refBuf, IEC61850_FC_MS); + else + retVal = IedConnection_readBooleanValue(self->connection, &(self->lastError), refBuf, IEC61850_FC_US); + + return retVal; +} + +bool +ClientSVControlBlock_getSvEna(ClientSVControlBlock self) +{ + return readBooleanVariable(self, "SvEna"); +} + +bool +ClientSVControlBlock_getResv(ClientSVControlBlock self) +{ + return readBooleanVariable(self, "Resv"); +} + +static char* +readStringVariable(ClientSVControlBlock self, const char* varName) +{ + char refBuf[130]; + + strcpy(refBuf, self->reference); + strcat(refBuf, "."); + strcat(refBuf, varName); + + self->lastError = IED_ERROR_OK; + + char* retVal; + + if (self->isMulticast) + retVal = IedConnection_readStringValue(self->connection, &(self->lastError), refBuf, IEC61850_FC_MS); + else + retVal = IedConnection_readStringValue(self->connection, &(self->lastError), refBuf, IEC61850_FC_US); + + return retVal; +} + +char* +ClientSVControlBlock_getMsvID(ClientSVControlBlock self) +{ + return readStringVariable(self, "MsvID"); +} + +char* +ClientSVControlBlock_getDatSet(ClientSVControlBlock self) +{ + return readStringVariable(self, "DatSet"); +} + +static uint32_t +readUIntVariable(ClientSVControlBlock self, const char* varName) +{ + char refBuf[130]; + + strcpy(refBuf, self->reference); + strcat(refBuf, "."); + strcat(refBuf, varName); + + self->lastError = IED_ERROR_OK; + + uint32_t retVal; + + if (self->isMulticast) + retVal = IedConnection_readUnsigned32Value(self->connection, &(self->lastError), refBuf, IEC61850_FC_MS); + else + retVal = IedConnection_readUnsigned32Value(self->connection, &(self->lastError), refBuf, IEC61850_FC_US); + + return retVal; +} + +uint32_t +ClientSVControlBlock_getConfRev(ClientSVControlBlock self) +{ + return readUIntVariable(self, "ConfRev"); +} + +uint16_t +ClientSVControlBlock_getSmpRate(ClientSVControlBlock self) +{ + return readUIntVariable(self, "SmpRate"); +} + +int +ClientSVControlBlock_getOptFlds(ClientSVControlBlock self) +{ + char refBuf[130]; + + strcpy(refBuf, self->reference); + strcat(refBuf, "."); + strcat(refBuf, "OptFlds"); + + self->lastError = IED_ERROR_OK; + + MmsValue* optFlds; + + if (self->isMulticast) + optFlds = IedConnection_readObject(self->connection, &(self->lastError), refBuf, IEC61850_FC_MS); + else + optFlds = IedConnection_readObject(self->connection, &(self->lastError), refBuf, IEC61850_FC_US); + + if (optFlds == NULL) + return 0; + + int retVal = 0; + + if (MmsValue_getType(optFlds) == MMS_BIT_STRING) + retVal = MmsValue_getBitStringAsInteger(optFlds); + + MmsValue_delete(optFlds); + + return retVal; +} + +uint8_t +ClientSVControlBlock_getSmpMod(ClientSVControlBlock self) +{ + return readUIntVariable(self, "SmpMod"); +} + +int +ClientSVControlBlock_getNoASDU(ClientSVControlBlock self) +{ + return readUIntVariable(self, "noASDU"); +} + +DstAddress +ClientSVControlBlock_getDstAddress(ClientSVControlBlock self) +{ + char refBuf[130]; + + strcpy(refBuf, self->reference); + strcat(refBuf, "."); + strcat(refBuf, "DstAddress"); + + self->lastError = IED_ERROR_OK; + + MmsValue* dstAddrValue; + + if (self->isMulticast) + dstAddrValue = IedConnection_readObject(self->connection, &(self->lastError), refBuf, IEC61850_FC_MS); + else + dstAddrValue = IedConnection_readObject(self->connection, &(self->lastError), refBuf, IEC61850_FC_US); + + DstAddress retVal; + memset(&retVal, 0, sizeof(retVal)); + + if (dstAddrValue == NULL) goto exit_error; + + if (MmsValue_getType(dstAddrValue) != MMS_STRUCTURE) { + if (DEBUG_IED_CLIENT) printf("IED_CLIENT: SVCB - addr has wrong type\n"); + goto exit_cleanup; + } + + if (MmsValue_getArraySize(dstAddrValue) != 4) { + if (DEBUG_IED_CLIENT) printf("IED_CLIENT: SVCB - addr has wrong type\n"); + goto exit_cleanup; + } + + MmsValue* addr = MmsValue_getElement(dstAddrValue, 0); + + if (MmsValue_getType(addr) != MMS_OCTET_STRING) { + if (DEBUG_IED_CLIENT) printf("IED_CLIENT: SVCB - addr has wrong type\n"); + goto exit_cleanup; + } + + if (MmsValue_getOctetStringSize(addr) != 6) { + if (DEBUG_IED_CLIENT) printf("IED_CLIENT: SVCB - addr has wrong size\n"); + goto exit_cleanup; + } + + uint8_t* addrBuf = MmsValue_getOctetStringBuffer(addr); + + memcpy(&(retVal.addr), addrBuf, 6); + + MmsValue* prio = MmsValue_getElement(dstAddrValue, 1); + + if (MmsValue_getType(prio) != MMS_UNSIGNED) { + if (DEBUG_IED_CLIENT) printf("IED_CLIENT: SVCB - prio has wrong type\n"); + goto exit_cleanup; + } + + retVal.priority = MmsValue_toUint32(prio); + + MmsValue* vid = MmsValue_getElement(dstAddrValue, 2); + + if (MmsValue_getType(vid) != MMS_UNSIGNED) { + if (DEBUG_IED_CLIENT) printf("IED_CLIENT: SVCB - vid has wrong type\n"); + goto exit_cleanup; + } + + retVal.vid = MmsValue_toUint32(vid); + + MmsValue* appID = MmsValue_getElement(dstAddrValue, 3); + + if (MmsValue_getType(appID) != MMS_UNSIGNED) { + if (DEBUG_IED_CLIENT) printf("IED_CLIENT: SVCB - appID has wrong type\n"); + goto exit_cleanup; + } + + retVal.appId = MmsValue_toUint32(appID); + + +exit_cleanup: + MmsValue_delete(dstAddrValue); + +exit_error: + return retVal; +} + + diff --git a/src/sampled_values/sv_subscriber.h b/src/sampled_values/sv_subscriber.h index f15ca21a..a72075e1 100644 --- a/src/sampled_values/sv_subscriber.h +++ b/src/sampled_values/sv_subscriber.h @@ -32,6 +32,47 @@ extern "C" { /** * \defgroup sv_subscriber_api_group IEC 61850 sampled values (SV) subscriber API + * + * The sampled values (SV) subscriber API consists of three different objects. + * The \ref SVReceiver object is responsible for handling all SV Ethernet messages + * for a specific Ethernet interface. If you want to receive SV messages on multiple + * Ethernet interfaces you have to use several \ref SVReceiver instances. + * An \ref SVSubscriber object is associated to a SV data stream that is identified by its appID + * and destination Ethernet address. The \reg SVSubscriber object is used to install a callback + * handler that is invoked for each ASDU (application service data unit) received for the + * associated stream. An \ref SVClientASDU is an object that represents a single ASDU. Each ASDU contains + * some meta information that can be obtained by specific access functions like e.g. + * \ref SVClientASDU_getSmpCnt to access the "SmpCnt" (sample count) attribute of the ASDU. The actual + * measurement data contained in the ASDU does not consist of structured ASN.1 data but stored as raw binary + * data. Without a priori knowledge of the dataset associated with the ASDU data stream it is not + * possible to interpret the received data correctly. Therefore you have to provide the data access functions + * with an index value to indicate the data type and the start of the data in the data block of the ASDU. + * E.g. reading a data set consisting of two FLOAT32 values you can use two subsequent calls of + * \ref SVClientASDU_getFLOAT32 one with index = 0 and the second one with index = 4. + * + * | IEC 61850 type | required bytes | + * | -------------- | -------------- | + * | BOOLEAN | 1 byte | + * | INT8 | 1 byte | + * | INT16 | 2 byte | + * | INT32 | 4 byte | + * | INT64 | 8 byte | + * | INT8U | 1 byte | + * | INT16U | 2 byte | + * | INT24U | 3 byte | + * | INT32U | 4 byte | + * | FLOAT32 | 4 byte | + * | FLOAT64 | 8 byte | + * | ENUMERATED | 4 byte | + * | CODED ENUM | 4 byte | + * | OCTET STRING | 20 byte | + * | VISIBLE STRING | 35 byte | + * | TimeStamp | 8 byte | + * | EntryTime | 6 byte | + * | BITSTRING | 4 byte | + * + * The SV subscriber API can be used independent of the IEC 61850 client API. In order to access the SVCB via MMS you + * have to use the IEC 61850 client API. Please see \ref ClientSVControlBlock object in section \ref IEC61850_CLIENT_SV . */ /**@{*/