|
|
|
/*
|
|
|
|
* client_sv_control.c
|
|
|
|
*
|
|
|
|
* Copyright 2015-2022 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 "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 ((error == IED_ERROR_OK) && (MmsValue_getType(value) != MMS_DATA_ACCESS_ERROR)) {
|
|
|
|
isMulticast = true;
|
|
|
|
MmsValue_delete(value);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
MmsValue_delete(value);
|
|
|
|
|
|
|
|
value = IedConnection_readObject(connection, &error, reference, IEC61850_FC_US);
|
|
|
|
|
|
|
|
if ((error == IED_ERROR_OK) && (MmsValue_getType(value) != MMS_DATA_ACCESS_ERROR))
|
|
|
|
MmsValue_delete(value);
|
|
|
|
else {
|
|
|
|
MmsValue_delete(value);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ClientSVControlBlock self = (ClientSVControlBlock) GLOBAL_CALLOC(1, sizeof(struct sClientSVControlBlock));
|
|
|
|
|
|
|
|
if (self) {
|
|
|
|
self->connection = connection;
|
|
|
|
self->reference = StringUtils_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];
|
|
|
|
|
|
|
|
StringUtils_concatString(refBuf, 130, self->reference, ".");
|
|
|
|
StringUtils_appendString(refBuf, 130, 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 value)
|
|
|
|
{
|
|
|
|
return setBooleanVariable(self, "SvEna", value);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
ClientSVControlBlock_setResv(ClientSVControlBlock self, bool value)
|
|
|
|
{
|
|
|
|
if (self->isMulticast == false)
|
|
|
|
return setBooleanVariable(self, "SvEna", value);
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
readBooleanVariable(ClientSVControlBlock self, const char* varName)
|
|
|
|
{
|
|
|
|
char refBuf[130];
|
|
|
|
|
|
|
|
StringUtils_concatString(refBuf, 130, self->reference, ".");
|
|
|
|
StringUtils_appendString(refBuf, 130, 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];
|
|
|
|
|
|
|
|
StringUtils_concatString(refBuf, 130, self->reference, ".");
|
|
|
|
StringUtils_appendString(refBuf, 130, 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];
|
|
|
|
|
|
|
|
StringUtils_concatString(refBuf, 130, self->reference, ".");
|
|
|
|
StringUtils_appendString(refBuf, 130, 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];
|
|
|
|
|
|
|
|
StringUtils_concatString(refBuf, 130, self->reference, ".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");
|
|
|
|
}
|
|
|
|
|
|
|
|
PhyComAddress
|
|
|
|
ClientSVControlBlock_getDstAddress(ClientSVControlBlock self)
|
|
|
|
{
|
|
|
|
char refBuf[130];
|
|
|
|
|
|
|
|
StringUtils_concatString(refBuf, 130, self->reference, ".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);
|
|
|
|
|
|
|
|
PhyComAddress 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.dstAddress), 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.vlanPriority = 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.vlanId = 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;
|
|
|
|
}
|
|
|
|
|