/* * 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 . * * 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; }