- IedConnection: code format updates

v1.6
Michael Zillgith 4 weeks ago
parent 9b467fa60d
commit 8a4b7bb616

@ -1,7 +1,7 @@
/* /*
* client_control.c * client_control.c
* *
* Copyright 2013-2021 Michael Zillgith * Copyright 2013-2025 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -221,7 +221,8 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection
/* request control model from server */ /* request control model from server */
char reference[129]; char reference[129];
if (strlen(objectReference) < 120) { if (strlen(objectReference) < 120)
{
StringUtils_concatString(reference, 129, objectReference, ".ctlModel"); StringUtils_concatString(reference, 129, objectReference, ".ctlModel");
} }
else else
@ -261,19 +262,19 @@ exit_function:
void void
ControlObjectClient_destroy(ControlObjectClient self) ControlObjectClient_destroy(ControlObjectClient self)
{ {
if (self != NULL) if (self)
{ {
GLOBAL_FREEMEM(self->objectReference); GLOBAL_FREEMEM(self->objectReference);
iedConnection_removeControlClient(self->connection, self); iedConnection_removeControlClient(self->connection, self);
if (self->ctlVal != NULL) if (self->ctlVal)
MmsValue_delete(self->ctlVal); MmsValue_delete(self->ctlVal);
if (self->analogValue != NULL) if (self->analogValue)
MmsValue_delete(self->analogValue); MmsValue_delete(self->analogValue);
if (self->orIdent != NULL) if (self->orIdent)
GLOBAL_FREEMEM(self->orIdent); GLOBAL_FREEMEM(self->orIdent);
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
@ -316,7 +317,7 @@ ControlObjectClient_changeServerControlModel(ControlObjectClient self, ControlMo
MmsType MmsType
ControlObjectClient_getCtlValType(ControlObjectClient self) ControlObjectClient_getCtlValType(ControlObjectClient self)
{ {
if (self->analogValue != NULL) if (self->analogValue)
return MmsValue_getType(self->analogValue); return MmsValue_getType(self->analogValue);
else else
return MmsValue_getType(self->ctlVal); return MmsValue_getType(self->ctlVal);
@ -334,10 +335,10 @@ ControlObjectClient_getLastError(ControlObjectClient self)
void void
ControlObjectClient_setOrigin(ControlObjectClient self, const char* orIdent, int orCat) ControlObjectClient_setOrigin(ControlObjectClient self, const char* orIdent, int orCat)
{ {
if (self->orIdent != NULL) if (self->orIdent)
GLOBAL_FREEMEM(self->orIdent); GLOBAL_FREEMEM(self->orIdent);
if (orIdent != NULL) if (orIdent)
self->orIdent = StringUtils_copyString(orIdent); self->orIdent = StringUtils_copyString(orIdent);
else else
self->orIdent = NULL; self->orIdent = NULL;
@ -596,14 +597,16 @@ ControlObjectClient_operateAsync(ControlObjectClient self, IedClientError* err,
*err = IED_ERROR_OK; *err = IED_ERROR_OK;
uint32_t invokeId = 0; uint32_t invokeId = 0;
if (ctlVal == NULL) { if (ctlVal == NULL)
{
*err = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT; *err = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT;
goto exit_function; goto exit_function;
} }
IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self->connection); IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self->connection);
if (call == NULL) { if (call == NULL)
{
*err = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; *err = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
goto exit_function; goto exit_function;
} }
@ -636,10 +639,12 @@ ControlObjectClient_operateAsync(ControlObjectClient self, IedClientError* err,
*err = iedConnection_mapMmsErrorToIedError(mmsError); *err = iedConnection_mapMmsErrorToIedError(mmsError);
if (mmsError != MMS_ERROR_NONE) { if (mmsError != MMS_ERROR_NONE)
{
iedConnection_releaseOutstandingCall(self->connection, call); iedConnection_releaseOutstandingCall(self->connection, call);
} }
else { else
{
MmsValue_update(self->ctlVal, ctlVal); MmsValue_update(self->ctlVal, ctlVal);
self->opertime = operTime; self->opertime = operTime;
@ -667,8 +672,10 @@ prepareSBOwParameters(ControlObjectClient self, MmsValue* ctlVal)
MmsValue* selValParameters = MmsValue_createEmptyStructure(selValElementCount); MmsValue* selValParameters = MmsValue_createEmptyStructure(selValElementCount);
/* support simplified usage of APC controls - user doesn't need to create the structure */ /* support simplified usage of APC controls - user doesn't need to create the structure */
if (self->analogValue != NULL) { if (self->analogValue)
if (MmsValue_getType(ctlVal) != MMS_STRUCTURE) { {
if (MmsValue_getType(ctlVal) != MMS_STRUCTURE)
{
MmsValue_setElement(self->analogValue, 0, ctlVal); MmsValue_setElement(self->analogValue, 0, ctlVal);
ctlVal = self->analogValue; ctlVal = self->analogValue;
} }
@ -678,7 +685,8 @@ prepareSBOwParameters(ControlObjectClient self, MmsValue* ctlVal)
int index = 1; int index = 1;
if (self->hasTimeActivatedMode) { if (self->hasTimeActivatedMode)
{
MmsValue* operTm = MmsValue_newUtcTimeByMsTime(0); MmsValue* operTm = MmsValue_newUtcTimeByMsTime(0);
MmsValue_setElement(selValParameters, index++, operTm); MmsValue_setElement(selValParameters, index++, operTm);
} }
@ -688,7 +696,8 @@ prepareSBOwParameters(ControlObjectClient self, MmsValue* ctlVal)
self->ctlNum++; self->ctlNum++;
if (self->hasCtlNum) { if (self->hasCtlNum)
{
MmsValue* ctlNum = MmsValue_newUnsignedFromUint32(self->ctlNum); MmsValue* ctlNum = MmsValue_newUnsignedFromUint32(self->ctlNum);
MmsValue_setElement(selValParameters, index++, ctlNum); MmsValue_setElement(selValParameters, index++, ctlNum);
} }
@ -699,13 +708,15 @@ prepareSBOwParameters(ControlObjectClient self, MmsValue* ctlVal)
if (self->useConstantT) if (self->useConstantT)
self->constantT = timestamp; self->constantT = timestamp;
if (self->edition == 2) { if (self->edition == 2)
{
ctlTime = MmsValue_newUtcTimeByMsTime(timestamp); ctlTime = MmsValue_newUtcTimeByMsTime(timestamp);
if (self->connection) if (self->connection)
MmsValue_setUtcTimeQuality(ctlTime, self->connection->timeQuality); MmsValue_setUtcTimeQuality(ctlTime, self->connection->timeQuality);
} }
else { else
{
ctlTime = MmsValue_newBinaryTime(false); ctlTime = MmsValue_newBinaryTime(false);
MmsValue_setBinaryTime(ctlTime, timestamp); MmsValue_setBinaryTime(ctlTime, timestamp);
} }
@ -763,7 +774,8 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal)
self->lastMmsError = mmsError; self->lastMmsError = mmsError;
self->lastAccessError = writeResult; self->lastAccessError = writeResult;
if (mmsError != MMS_ERROR_NONE) { if (mmsError != MMS_ERROR_NONE)
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select-with-value failed!\n"); printf("IED_CLIENT: select-with-value failed!\n");
@ -771,8 +783,10 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal)
goto exit_function; goto exit_function;
} }
else { else
if (writeResult != DATA_ACCESS_ERROR_SUCCESS) { {
if (writeResult != DATA_ACCESS_ERROR_SUCCESS)
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select-with-value failed!\n"); printf("IED_CLIENT: select-with-value failed!\n");
@ -795,13 +809,13 @@ exit_function:
static void static void
internalSelWithValHandler(uint32_t invokeId, void* parameter, MmsError err, MmsDataAccessError accessError) internalSelWithValHandler(uint32_t invokeId, void* parameter, MmsError err, MmsDataAccessError accessError)
{ {
ControlObjectClient self = (ControlObjectClient) parameter; ControlObjectClient self = (ControlObjectClient)parameter;
IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self->connection, invokeId); IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self->connection, invokeId);
if (call) { if (call)
{
ControlObjectClient_ControlActionHandler handler = (ControlObjectClient_ControlActionHandler) call->callback; ControlObjectClient_ControlActionHandler handler = (ControlObjectClient_ControlActionHandler)call->callback;
IedClientError iedError = iedConnection_mapMmsErrorToIedError(err); IedClientError iedError = iedConnection_mapMmsErrorToIedError(err);
@ -810,18 +824,21 @@ internalSelWithValHandler(uint32_t invokeId, void* parameter, MmsError err, MmsD
self->lastMmsError = err; self->lastMmsError = err;
self->lastAccessError = accessError; self->lastAccessError = accessError;
if (iedError == IED_ERROR_OK) { if (iedError == IED_ERROR_OK)
{
iedError = iedConnection_mapDataAccessErrorToIedError(accessError); iedError = iedConnection_mapDataAccessErrorToIedError(accessError);
if (iedError == IED_ERROR_OK) if (iedError == IED_ERROR_OK)
success = true; success = true;
} }
if (success) { if (success)
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select-with-value+\n"); printf("IED_CLIENT: select-with-value+\n");
} }
else { else
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select-with-value failed!\n"); printf("IED_CLIENT: select-with-value failed!\n");
} }
@ -830,7 +847,8 @@ internalSelWithValHandler(uint32_t invokeId, void* parameter, MmsError err, MmsD
iedConnection_releaseOutstandingCall(self->connection, call); iedConnection_releaseOutstandingCall(self->connection, call);
} }
else { else
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: internal error - no matching outstanding call!\n"); printf("IED_CLIENT: internal error - no matching outstanding call!\n");
} }
@ -838,19 +856,21 @@ internalSelWithValHandler(uint32_t invokeId, void* parameter, MmsError err, MmsD
uint32_t uint32_t
ControlObjectClient_selectWithValueAsync(ControlObjectClient self, IedClientError* err, MmsValue* ctlVal, ControlObjectClient_selectWithValueAsync(ControlObjectClient self, IedClientError* err, MmsValue* ctlVal,
ControlObjectClient_ControlActionHandler handler, void* parameter) ControlObjectClient_ControlActionHandler handler, void* parameter)
{ {
*err = IED_ERROR_OK; *err = IED_ERROR_OK;
uint32_t invokeId = 0; uint32_t invokeId = 0;
if (ctlVal == NULL) { if (ctlVal == NULL)
{
*err = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT; *err = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT;
goto exit_function; goto exit_function;
} }
IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self->connection); IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self->connection);
if (call == NULL) { if (call == NULL)
{
*err = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; *err = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
goto exit_function; goto exit_function;
} }
@ -876,7 +896,8 @@ ControlObjectClient_selectWithValueAsync(ControlObjectClient self, IedClientErro
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select with value: %s/%s\n", domainId, itemId); printf("IED_CLIENT: select with value: %s/%s\n", domainId, itemId);
MmsConnection_writeVariableAsync(self->connection->connection, &(call->invokeId), &mmsError, domainId, itemId, selValParameters, internalSelWithValHandler, self); MmsConnection_writeVariableAsync(self->connection->connection, &(call->invokeId), &mmsError, domainId, itemId,
selValParameters, internalSelWithValHandler, self);
invokeId = call->invokeId; invokeId = call->invokeId;
@ -885,10 +906,12 @@ ControlObjectClient_selectWithValueAsync(ControlObjectClient self, IedClientErro
*err = iedConnection_mapMmsErrorToIedError(mmsError); *err = iedConnection_mapMmsErrorToIedError(mmsError);
if (mmsError != MMS_ERROR_NONE) { if (mmsError != MMS_ERROR_NONE)
{
iedConnection_releaseOutstandingCall(self->connection, call); iedConnection_releaseOutstandingCall(self->connection, call);
} }
else { else
{
MmsValue_update(self->ctlVal, ctlVal); MmsValue_update(self->ctlVal, ctlVal);
} }
@ -919,8 +942,8 @@ ControlObjectClient_select(ControlObjectClient self)
MmsError mmsError; MmsError mmsError;
MmsValue* value = MmsConnection_readVariable(IedConnection_getMmsConnection(self->connection), MmsValue* value =
&mmsError, domainId, itemId); MmsConnection_readVariable(IedConnection_getMmsConnection(self->connection), &mmsError, domainId, itemId);
bool selected = false; bool selected = false;
@ -929,30 +952,36 @@ ControlObjectClient_select(ControlObjectClient self)
self->lastMmsError = mmsError; self->lastMmsError = mmsError;
self->lastAccessError = DATA_ACCESS_ERROR_SUCCESS; self->lastAccessError = DATA_ACCESS_ERROR_SUCCESS;
if (value == NULL) { if (value == NULL)
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select: read SBO failed!\n"); printf("IED_CLIENT: select: read SBO failed!\n");
goto exit_function; goto exit_function;
} }
if (MmsValue_getType(value) == MMS_VISIBLE_STRING) { if (MmsValue_getType(value) == MMS_VISIBLE_STRING)
if (strcmp(MmsValue_toString(value), "") == 0) { {
if (strcmp(MmsValue_toString(value), "") == 0)
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("select-response-\n"); printf("select-response-\n");
} }
else { else
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("select-response+: (%s)\n", MmsValue_toString(value)); printf("select-response+: (%s)\n", MmsValue_toString(value));
selected = true; selected = true;
} }
} }
else if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) { else if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR)
{
self->lastAccessError = MmsValue_getDataAccessError(value); self->lastAccessError = MmsValue_getDataAccessError(value);
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select returned data-access-error: %i\n", self->lastAccessError); printf("IED_CLIENT: select returned data-access-error: %i\n", self->lastAccessError);
} }
else { else
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select: unexpected response from server!\n"); printf("IED_CLIENT: select: unexpected response from server!\n");
} }
@ -970,8 +999,8 @@ internalSelectHandler(uint32_t invokeId, void* parameter, MmsError err, MmsValue
IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self->connection, invokeId); IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self->connection, invokeId);
if (call) { if (call)
{
ControlObjectClient_ControlActionHandler handler = (ControlObjectClient_ControlActionHandler) call->callback; ControlObjectClient_ControlActionHandler handler = (ControlObjectClient_ControlActionHandler) call->callback;
IedClientError iedError = iedConnection_mapMmsErrorToIedError(err); IedClientError iedError = iedConnection_mapMmsErrorToIedError(err);
@ -983,15 +1012,16 @@ internalSelectHandler(uint32_t invokeId, void* parameter, MmsError err, MmsValue
self->ctlNum++; self->ctlNum++;
if (iedError == IED_ERROR_OK) { if (iedError == IED_ERROR_OK)
{
if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) { if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR)
{
MmsDataAccessError dataAccessError = MmsValue_getDataAccessError(value); MmsDataAccessError dataAccessError = MmsValue_getDataAccessError(value);
self->lastAccessError = dataAccessError; self->lastAccessError = dataAccessError;
iedError = iedConnection_mapDataAccessErrorToIedError(dataAccessError); iedError = iedConnection_mapDataAccessErrorToIedError(dataAccessError);
} }
else if (MmsValue_getType(value) == MMS_VISIBLE_STRING) { else if (MmsValue_getType(value) == MMS_VISIBLE_STRING)
{
char domainId[65]; char domainId[65];
char itemId[65]; char itemId[65];
@ -1001,17 +1031,20 @@ internalSelectHandler(uint32_t invokeId, void* parameter, MmsError err, MmsValue
StringUtils_appendString(itemId, 65, "$SBO"); StringUtils_appendString(itemId, 65, "$SBO");
if (strcmp(MmsValue_toString(value), "") == 0) { if (strcmp(MmsValue_toString(value), "") == 0)
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("select-response-\n"); printf("select-response-\n");
} }
else { else
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("select-response+: (%s)\n", MmsValue_toString(value)); printf("select-response+: (%s)\n", MmsValue_toString(value));
success = true; success = true;
} }
} }
else { else
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select: unexpected response from server!\n"); printf("IED_CLIENT: select: unexpected response from server!\n");
} }
@ -1021,7 +1054,8 @@ internalSelectHandler(uint32_t invokeId, void* parameter, MmsError err, MmsValue
iedConnection_releaseOutstandingCall(self->connection, call); iedConnection_releaseOutstandingCall(self->connection, call);
} }
else { else
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: internal error - no matching outstanding call!\n"); printf("IED_CLIENT: internal error - no matching outstanding call!\n");
} }
@ -1048,7 +1082,8 @@ ControlObjectClient_selectAsync(ControlObjectClient self, IedClientError* err, C
IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self->connection); IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self->connection);
if (call == NULL) { if (call == NULL)
{
*err = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; *err = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
return 0; return 0;
} }
@ -1068,7 +1103,8 @@ ControlObjectClient_selectAsync(ControlObjectClient self, IedClientError* err, C
*err = iedConnection_mapMmsErrorToIedError(mmsError); *err = iedConnection_mapMmsErrorToIedError(mmsError);
if (mmsError != MMS_ERROR_NONE) { if (mmsError != MMS_ERROR_NONE)
{
iedConnection_releaseOutstandingCall(self->connection, call); iedConnection_releaseOutstandingCall(self->connection, call);
} }
@ -1089,7 +1125,8 @@ createCancelParameters(ControlObjectClient self)
int index = 1; int index = 1;
if (self->hasTimeActivatedMode) { if (self->hasTimeActivatedMode)
{
MmsValue* operTm = MmsValue_newUtcTimeByMsTime(self->opertime); MmsValue* operTm = MmsValue_newUtcTimeByMsTime(self->opertime);
MmsValue_setElement(cancelParameters, index++, operTm); MmsValue_setElement(cancelParameters, index++, operTm);
} }
@ -1110,13 +1147,15 @@ createCancelParameters(ControlObjectClient self)
MmsValue* ctlTime; MmsValue* ctlTime;
if (self->edition == 2) { if (self->edition == 2)
{
ctlTime = MmsValue_newUtcTimeByMsTime(timestamp); ctlTime = MmsValue_newUtcTimeByMsTime(timestamp);
if (self->connection) if (self->connection)
MmsValue_setUtcTimeQuality(ctlTime, self->connection->timeQuality); MmsValue_setUtcTimeQuality(ctlTime, self->connection->timeQuality);
} }
else { else
{
ctlTime = MmsValue_newBinaryTime(false); ctlTime = MmsValue_newBinaryTime(false);
MmsValue_setBinaryTime(ctlTime, timestamp); MmsValue_setBinaryTime(ctlTime, timestamp);
} }
@ -1158,13 +1197,16 @@ ControlObjectClient_cancel(ControlObjectClient self)
MmsValue_setElement(cancelParameters, 0, NULL); MmsValue_setElement(cancelParameters, 0, NULL);
MmsValue_delete(cancelParameters); MmsValue_delete(cancelParameters);
if (mmsError != MMS_ERROR_NONE) { if (mmsError != MMS_ERROR_NONE)
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: cancel failed!\n"); printf("IED_CLIENT: cancel failed!\n");
return false; return false;
} }
else { else
if (writeResult != DATA_ACCESS_ERROR_SUCCESS) { {
if (writeResult != DATA_ACCESS_ERROR_SUCCESS)
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: cancel failed!\n"); printf("IED_CLIENT: cancel failed!\n");
return false; return false;
@ -1177,13 +1219,13 @@ ControlObjectClient_cancel(ControlObjectClient self)
static void static void
internalCancelHandler(uint32_t invokeId, void* parameter, MmsError err, MmsDataAccessError accessError) internalCancelHandler(uint32_t invokeId, void* parameter, MmsError err, MmsDataAccessError accessError)
{ {
ControlObjectClient self = (ControlObjectClient) parameter; ControlObjectClient self = (ControlObjectClient)parameter;
IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self->connection, invokeId); IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self->connection, invokeId);
if (call) { if (call)
{
ControlObjectClient_ControlActionHandler handler = (ControlObjectClient_ControlActionHandler) call->callback; ControlObjectClient_ControlActionHandler handler = (ControlObjectClient_ControlActionHandler)call->callback;
IedClientError iedError = iedConnection_mapMmsErrorToIedError(err); IedClientError iedError = iedConnection_mapMmsErrorToIedError(err);
@ -1192,18 +1234,21 @@ internalCancelHandler(uint32_t invokeId, void* parameter, MmsError err, MmsDataA
self->lastMmsError = err; self->lastMmsError = err;
self->lastAccessError = accessError; self->lastAccessError = accessError;
if (iedError == IED_ERROR_OK) { if (iedError == IED_ERROR_OK)
{
iedError = iedConnection_mapDataAccessErrorToIedError(accessError); iedError = iedConnection_mapDataAccessErrorToIedError(accessError);
if (iedError == IED_ERROR_OK) if (iedError == IED_ERROR_OK)
success = true; success = true;
} }
if (success) { if (success)
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: cancel+\n"); printf("IED_CLIENT: cancel+\n");
} }
else { else
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: cancel failed!\n"); printf("IED_CLIENT: cancel failed!\n");
} }
@ -1212,21 +1257,24 @@ internalCancelHandler(uint32_t invokeId, void* parameter, MmsError err, MmsDataA
iedConnection_releaseOutstandingCall(self->connection, call); iedConnection_releaseOutstandingCall(self->connection, call);
} }
else { else
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: internal error - no matching outstanding call!\n"); printf("IED_CLIENT: internal error - no matching outstanding call!\n");
} }
} }
uint32_t uint32_t
ControlObjectClient_cancelAsync(ControlObjectClient self, IedClientError* err, ControlObjectClient_ControlActionHandler handler, void* parameter) ControlObjectClient_cancelAsync(ControlObjectClient self, IedClientError* err,
ControlObjectClient_ControlActionHandler handler, void* parameter)
{ {
*err = IED_ERROR_OK; *err = IED_ERROR_OK;
uint32_t invokeId = 0; uint32_t invokeId = 0;
IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self->connection); IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self->connection);
if (call == NULL) { if (call == NULL)
{
*err = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; *err = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
goto exit_function; goto exit_function;
} }
@ -1252,7 +1300,8 @@ ControlObjectClient_cancelAsync(ControlObjectClient self, IedClientError* err, C
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select with value: %s/%s\n", domainId, itemId); printf("IED_CLIENT: select with value: %s/%s\n", domainId, itemId);
MmsConnection_writeVariableAsync(self->connection->connection, &(call->invokeId), &mmsError, domainId, itemId, cancelParameters, internalCancelHandler, self); MmsConnection_writeVariableAsync(self->connection->connection, &(call->invokeId), &mmsError, domainId, itemId,
cancelParameters, internalCancelHandler, self);
invokeId = call->invokeId; invokeId = call->invokeId;
@ -1261,7 +1310,8 @@ ControlObjectClient_cancelAsync(ControlObjectClient self, IedClientError* err, C
*err = iedConnection_mapMmsErrorToIedError(mmsError); *err = iedConnection_mapMmsErrorToIedError(mmsError);
if (mmsError != MMS_ERROR_NONE) { if (mmsError != MMS_ERROR_NONE)
{
iedConnection_releaseOutstandingCall(self->connection, call); iedConnection_releaseOutstandingCall(self->connection, call);
} }
@ -1326,6 +1376,6 @@ ControlObjectClient_setCtlNum(ControlObjectClient self, uint8_t ctlNum)
void void
controlObjectClient_invokeCommandTerminationHandler(ControlObjectClient self) controlObjectClient_invokeCommandTerminationHandler(ControlObjectClient self)
{ {
if (self->commandTerminationHandler != NULL) if (self->commandTerminationHandler)
self->commandTerminationHandler(self->commandTerminaionHandlerParameter, self); self->commandTerminationHandler(self->commandTerminaionHandlerParameter, self);
} }

@ -3,7 +3,7 @@
* *
* Implementation of the ClientReportControlBlock class * Implementation of the ClientReportControlBlock class
* *
* Copyright 2014-2024 Michael Zillgith * Copyright 2014-2025 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -1145,6 +1145,12 @@ IedConnection_setGoCBValuesAsync(IedConnection self, IedClientError* error, Clie
{ {
struct sWriteGoCBVariablesParameter* param = (struct sWriteGoCBVariablesParameter*) GLOBAL_MALLOC(sizeof(struct sWriteGoCBVariablesParameter)); struct sWriteGoCBVariablesParameter* param = (struct sWriteGoCBVariablesParameter*) GLOBAL_MALLOC(sizeof(struct sWriteGoCBVariablesParameter));
if (param == NULL)
{
*error = IED_ERROR_UNKNOWN;
goto exit_function;
}
call->specificParameter2.pointer = param; call->specificParameter2.pointer = param;
param->itemIds = itemIds; param->itemIds = itemIds;

@ -3,7 +3,7 @@
* *
* Client implementation for IEC 61850 reporting. * Client implementation for IEC 61850 reporting.
* *
* Copyright 2013-2024 Michael Zillgith * Copyright 2013-2025 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -93,7 +93,10 @@ ClientReport_create()
{ {
ClientReport self = (ClientReport) GLOBAL_CALLOC(1, sizeof(struct sClientReport)); ClientReport self = (ClientReport) GLOBAL_CALLOC(1, sizeof(struct sClientReport));
self->dataSetSize = -1; if (self)
{
self->dataSetSize = -1;
}
return self; return self;
} }
@ -101,27 +104,30 @@ ClientReport_create()
void void
ClientReport_destroy(ClientReport self) ClientReport_destroy(ClientReport self)
{ {
if (self->entryId) if (self)
MmsValue_delete(self->entryId); {
if (self->entryId)
MmsValue_delete(self->entryId);
GLOBAL_FREEMEM(self->rcbReference); GLOBAL_FREEMEM(self->rcbReference);
if (self->rptId != NULL) if (self->rptId)
GLOBAL_FREEMEM(self->rptId); GLOBAL_FREEMEM(self->rptId);
if (self->dataSetValues != NULL) if (self->dataSetValues)
MmsValue_delete(self->dataSetValues); MmsValue_delete(self->dataSetValues);
if (self->dataReferences != NULL) if (self->dataReferences)
MmsValue_delete(self->dataReferences); MmsValue_delete(self->dataReferences);
if (self->reasonForInclusion != NULL) if (self->reasonForInclusion)
GLOBAL_FREEMEM(self->reasonForInclusion); GLOBAL_FREEMEM(self->reasonForInclusion);
if (self->dataSetName != NULL) if (self->dataSetName)
GLOBAL_FREEMEM((void*) self->dataSetName); GLOBAL_FREEMEM((void*) self->dataSetName);
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
}
} }
char* char*
@ -163,7 +169,6 @@ ClientReport_getTimestamp(ClientReport self)
return self->timestamp; return self->timestamp;
} }
bool bool
ClientReport_hasSeqNum(ClientReport self) ClientReport_hasSeqNum(ClientReport self)
{ {
@ -223,11 +228,14 @@ ClientReport_getDataReference(ClientReport self, int elementIndex)
{ {
char* dataReference = NULL; char* dataReference = NULL;
if (self->dataReferences != NULL) { if (self->dataReferences)
{
MmsValue* dataRefValue = MmsValue_getElement(self->dataReferences, elementIndex); MmsValue* dataRefValue = MmsValue_getElement(self->dataReferences, elementIndex);
if (dataRefValue != NULL) { if (dataRefValue)
if (MmsValue_getType(dataRefValue) == MMS_VISIBLE_STRING) { {
if (MmsValue_getType(dataRefValue) == MMS_VISIBLE_STRING)
{
return MmsValue_toString(dataRefValue); return MmsValue_toString(dataRefValue);
} }
} }
@ -271,8 +279,9 @@ lookupReportHandler(IedConnection self, const char* rcbReference)
{ {
LinkedList element = LinkedList_getNext(self->enabledReports); LinkedList element = LinkedList_getNext(self->enabledReports);
while (element != NULL) { while (element)
ClientReport report = (ClientReport) element->data; {
ClientReport report = (ClientReport)element->data;
if (strcmp(report->rcbReference, rcbReference) == 0) if (strcmp(report->rcbReference, rcbReference) == 0)
return report; return report;
@ -288,21 +297,23 @@ uninstallReportHandler(IedConnection self, const char* rcbReference)
{ {
ClientReport report = lookupReportHandler(self, rcbReference); ClientReport report = lookupReportHandler(self, rcbReference);
if (report != NULL) { if (report)
{
LinkedList_remove(self->enabledReports, report); LinkedList_remove(self->enabledReports, report);
ClientReport_destroy(report); ClientReport_destroy(report);
} }
} }
void void
IedConnection_installReportHandler(IedConnection self, const char* rcbReference, const char* rptId, ReportCallbackFunction handler, IedConnection_installReportHandler(IedConnection self, const char* rcbReference, const char* rptId,
void* handlerParameter) ReportCallbackFunction handler, void* handlerParameter)
{ {
Semaphore_wait(self->reportHandlerMutex); Semaphore_wait(self->reportHandlerMutex);
ClientReport report = lookupReportHandler(self, rcbReference); ClientReport report = lookupReportHandler(self, rcbReference);
if (report != NULL) { if (report)
{
uninstallReportHandler(self, rcbReference); uninstallReportHandler(self, rcbReference);
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
@ -310,21 +321,25 @@ IedConnection_installReportHandler(IedConnection self, const char* rcbReference,
} }
report = ClientReport_create(); report = ClientReport_create();
report->callback = handler;
report->callbackParameter = handlerParameter;
report->rcbReference = StringUtils_copyString(rcbReference);
if (rptId != NULL) if (report)
report->rptId = StringUtils_copyString(rptId); {
else report->callback = handler;
report->rptId = NULL; report->callbackParameter = handlerParameter;
report->rcbReference = StringUtils_copyString(rcbReference);
LinkedList_add(self->enabledReports, report); if (rptId)
report->rptId = StringUtils_copyString(rptId);
else
report->rptId = NULL;
Semaphore_post(self->reportHandlerMutex); LinkedList_add(self->enabledReports, report);
if (DEBUG_IED_CLIENT) Semaphore_post(self->reportHandlerMutex);
printf("DEBUG_IED_CLIENT: Installed new report callback handler for %s\n", rcbReference);
if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: Installed new report callback handler for %s\n", rcbReference);
}
} }
void void
@ -361,13 +376,15 @@ IedConnection_triggerGIReport(IedConnection self, IedClientError* error, const c
MmsValue_delete(gi); MmsValue_delete(gi);
if (mmsError != MMS_ERROR_NONE) { if (mmsError != MMS_ERROR_NONE)
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: failed to trigger GI for %s!\n", rcbReference); printf("DEBUG_IED_CLIENT: failed to trigger GI for %s!\n", rcbReference);
*error = iedConnection_mapMmsErrorToIedError(mmsError); *error = iedConnection_mapMmsErrorToIedError(mmsError);
} }
else { else
{
*error = IED_ERROR_OK; *error = IED_ERROR_OK;
} }
} }
@ -379,7 +396,8 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
MmsValue* rptIdValue = MmsValue_getElement(value, 0); MmsValue* rptIdValue = MmsValue_getElement(value, 0);
if ((rptIdValue == NULL) || (MmsValue_getType(rptIdValue) != MMS_VISIBLE_STRING)) { if ((rptIdValue == NULL) || (MmsValue_getType(rptIdValue) != MMS_VISIBLE_STRING))
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: received malformed report (RptId)\n"); printf("IED_CLIENT: received malformed report (RptId)\n");
@ -389,19 +407,22 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
LinkedList element = LinkedList_getNext(self->enabledReports); LinkedList element = LinkedList_getNext(self->enabledReports);
ClientReport matchingReport = NULL; ClientReport matchingReport = NULL;
while (element != NULL) { while (element)
ClientReport report = (ClientReport) element->data; {
ClientReport report = (ClientReport)element->data;
char defaultRptId[130]; char defaultRptId[130];
char* rptId = report->rptId; char* rptId = report->rptId;
if ((rptId == NULL) || (strlen(rptId) == 0)) { if ((rptId == NULL) || (strlen(rptId) == 0))
{
StringUtils_concatString(defaultRptId, 130, report->rcbReference, ""); StringUtils_concatString(defaultRptId, 130, report->rcbReference, "");
StringUtils_replace(defaultRptId, '.', '$'); StringUtils_replace(defaultRptId, '.', '$');
rptId = defaultRptId; rptId = defaultRptId;
} }
if (strcmp(MmsValue_toString(rptIdValue), rptId) == 0) { if (strcmp(MmsValue_toString(rptIdValue), rptId) == 0)
{
matchingReport = report; matchingReport = report;
break; break;
} }
@ -421,12 +442,13 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
matchingReport->hasBufOverflow = false; matchingReport->hasBufOverflow = false;
matchingReport->hasSubSequenceNumber = false; matchingReport->hasSubSequenceNumber = false;
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: received report with ID %s\n", MmsValue_toString(rptIdValue)); printf("IED_CLIENT: received report with ID %s\n", MmsValue_toString(rptIdValue));
MmsValue* optFlds = MmsValue_getElement(value, 1); MmsValue* optFlds = MmsValue_getElement(value, 1);
if ((optFlds == NULL) || (MmsValue_getType(optFlds) != MMS_BIT_STRING)) { if ((optFlds == NULL) || (MmsValue_getType(optFlds) != MMS_BIT_STRING))
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: received malformed report (OptFlds)\n"); printf("IED_CLIENT: received malformed report (OptFlds)\n");
@ -436,29 +458,31 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
int inclusionIndex = 2; int inclusionIndex = 2;
/* has sequence-number */ /* has sequence-number */
if (MmsValue_getBitStringBit(optFlds, 1) == true) { if (MmsValue_getBitStringBit(optFlds, 1) == true)
{
MmsValue* seqNum = MmsValue_getElement(value, inclusionIndex); MmsValue* seqNum = MmsValue_getElement(value, inclusionIndex);
if ((seqNum == NULL) || (MmsValue_getType(seqNum) != MMS_UNSIGNED)) { if ((seqNum == NULL) || (MmsValue_getType(seqNum) != MMS_UNSIGNED))
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: received malformed report (seqNum)\n"); printf("IED_CLIENT: received malformed report (seqNum)\n");
goto exit_function; goto exit_function;
} }
matchingReport->seqNum = (uint16_t) MmsValue_toUint32(seqNum); matchingReport->seqNum = (uint16_t)MmsValue_toUint32(seqNum);
matchingReport->hasSequenceNumber = true; matchingReport->hasSequenceNumber = true;
inclusionIndex++; inclusionIndex++;
} }
/* has report-timestamp */ /* has report-timestamp */
if (MmsValue_getBitStringBit(optFlds, 2) == true) { if (MmsValue_getBitStringBit(optFlds, 2) == true)
{
MmsValue* timeStampValue = MmsValue_getElement(value, inclusionIndex); MmsValue* timeStampValue = MmsValue_getElement(value, inclusionIndex);
if ((timeStampValue == NULL) || (MmsValue_getType(timeStampValue) != MMS_BINARY_TIME)) { if ((timeStampValue == NULL) || (MmsValue_getType(timeStampValue) != MMS_BINARY_TIME))
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: received malformed report (timeStamp)\n"); printf("IED_CLIENT: received malformed report (timeStamp)\n");
@ -469,18 +493,20 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
matchingReport->timestamp = MmsValue_getBinaryTimeAsUtcMs(timeStampValue); matchingReport->timestamp = MmsValue_getBinaryTimeAsUtcMs(timeStampValue);
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: report has timestamp %llu\n", (unsigned long long) matchingReport->timestamp); printf("IED_CLIENT: report has timestamp %llu\n", (unsigned long long)matchingReport->timestamp);
inclusionIndex++; inclusionIndex++;
} }
/* check if data set name is present */ /* check if data set name is present */
if (MmsValue_getBitStringBit(optFlds, 4) == true) { if (MmsValue_getBitStringBit(optFlds, 4) == true)
{
matchingReport->hasDataSetName = true; matchingReport->hasDataSetName = true;
MmsValue* dataSetName = MmsValue_getElement(value, inclusionIndex); MmsValue* dataSetName = MmsValue_getElement(value, inclusionIndex);
if ((dataSetName == NULL) || (MmsValue_getType(dataSetName) != MMS_VISIBLE_STRING)) { if ((dataSetName == NULL) || (MmsValue_getType(dataSetName) != MMS_VISIBLE_STRING))
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: received malformed report (DatSet)\n"); printf("IED_CLIENT: received malformed report (DatSet)\n");
@ -490,14 +516,17 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
int dataSetNameSize = MmsValue_getStringSize(dataSetName); int dataSetNameSize = MmsValue_getStringSize(dataSetName);
/* limit to prevent large memory allocation */ /* limit to prevent large memory allocation */
if (dataSetNameSize < 130) { if (dataSetNameSize < 130)
{
const char* dataSetNameStr = MmsValue_toString(dataSetName); const char* dataSetNameStr = MmsValue_toString(dataSetName);
if (matchingReport->dataSetName == NULL) { if (matchingReport->dataSetName == NULL)
matchingReport->dataSetName = (char*) GLOBAL_MALLOC(dataSetNameSize + 1); {
matchingReport->dataSetName = (char*)GLOBAL_MALLOC(dataSetNameSize + 1);
if (matchingReport->dataSetName == NULL) { if (matchingReport->dataSetName == NULL)
matchingReport->dataSetNameSize = 0; {
matchingReport->dataSetNameSize = 0;
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: failed to allocate memory\n"); printf("IED_CLIENT: failed to allocate memory\n");
@ -507,14 +536,17 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
matchingReport->dataSetNameSize = dataSetNameSize + 1; matchingReport->dataSetNameSize = dataSetNameSize + 1;
} }
else { else
if (matchingReport->dataSetNameSize < MmsValue_getStringSize(dataSetName) + 1) { {
GLOBAL_FREEMEM((void*) matchingReport->dataSetName); if (matchingReport->dataSetNameSize < MmsValue_getStringSize(dataSetName) + 1)
{
GLOBAL_FREEMEM((void*)matchingReport->dataSetName);
matchingReport->dataSetName = (char*) GLOBAL_MALLOC(dataSetNameSize + 1); matchingReport->dataSetName = (char*)GLOBAL_MALLOC(dataSetNameSize + 1);
if (matchingReport->dataSetName == NULL) { if (matchingReport->dataSetName == NULL)
matchingReport->dataSetNameSize = 0; {
matchingReport->dataSetNameSize = 0;
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: failed to allocate memory\n"); printf("IED_CLIENT: failed to allocate memory\n");
@ -528,7 +560,8 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
StringUtils_copyStringMax(matchingReport->dataSetName, dataSetNameSize + 1, dataSetNameStr); StringUtils_copyStringMax(matchingReport->dataSetName, dataSetNameSize + 1, dataSetNameStr);
} }
else { else
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: report DatSet name too large (%i)\n", dataSetNameSize); printf("IED_CLIENT: report DatSet name too large (%i)\n", dataSetNameSize);
@ -542,10 +575,12 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
printf("IED_CLIENT: Found enabled report!\n"); printf("IED_CLIENT: Found enabled report!\n");
/* check bufOvfl */ /* check bufOvfl */
if (MmsValue_getBitStringBit(optFlds, 6) == true) { if (MmsValue_getBitStringBit(optFlds, 6) == true)
{
MmsValue* bufOverflow = MmsValue_getElement(value, inclusionIndex); MmsValue* bufOverflow = MmsValue_getElement(value, inclusionIndex);
if ((bufOverflow == NULL) || (MmsValue_getType(bufOverflow) != MMS_BOOLEAN)) { if ((bufOverflow == NULL) || (MmsValue_getType(bufOverflow) != MMS_BOOLEAN))
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: received malformed report (BufOvfl)\n"); printf("IED_CLIENT: received malformed report (BufOvfl)\n");
@ -559,24 +594,28 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
} }
/* check for entryId */ /* check for entryId */
if (MmsValue_getBitStringBit(optFlds, 7) == true) { if (MmsValue_getBitStringBit(optFlds, 7) == true)
{
MmsValue* entryId = MmsValue_getElement(value, inclusionIndex); MmsValue* entryId = MmsValue_getElement(value, inclusionIndex);
if ((entryId == NULL) || (MmsValue_getType(entryId) != MMS_OCTET_STRING)) { if ((entryId == NULL) || (MmsValue_getType(entryId) != MMS_OCTET_STRING))
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: received malformed report (entryID)\n"); printf("IED_CLIENT: received malformed report (entryID)\n");
goto exit_function; goto exit_function;
} }
if (matchingReport->entryId != NULL) { if (matchingReport->entryId != NULL)
{
if (!MmsValue_update(matchingReport->entryId, entryId)) { if (!MmsValue_update(matchingReport->entryId, entryId))
{
MmsValue_delete(matchingReport->entryId); MmsValue_delete(matchingReport->entryId);
matchingReport->entryId = MmsValue_clone(entryId); matchingReport->entryId = MmsValue_clone(entryId);
} }
} }
else { else
{
matchingReport->entryId = MmsValue_clone(entryId); matchingReport->entryId = MmsValue_clone(entryId);
} }
@ -584,10 +623,12 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
} }
/* check for confRev */ /* check for confRev */
if (MmsValue_getBitStringBit(optFlds, 8) == true) { if (MmsValue_getBitStringBit(optFlds, 8) == true)
{
MmsValue* confRev = MmsValue_getElement(value, inclusionIndex); MmsValue* confRev = MmsValue_getElement(value, inclusionIndex);
if ((confRev == NULL) || (MmsValue_getType(confRev) != MMS_UNSIGNED)) { if ((confRev == NULL) || (MmsValue_getType(confRev) != MMS_UNSIGNED))
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: received malformed report (confRev)\n"); printf("IED_CLIENT: received malformed report (confRev)\n");
@ -601,46 +642,53 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
} }
/* handle segmentation fields (check ReportedOptFlds.segmentation) */ /* handle segmentation fields (check ReportedOptFlds.segmentation) */
if (MmsValue_getBitStringBit(optFlds, 9) == true) { if (MmsValue_getBitStringBit(optFlds, 9) == true)
{
MmsValue* subSeqNum = MmsValue_getElement(value, inclusionIndex); MmsValue* subSeqNum = MmsValue_getElement(value, inclusionIndex);
inclusionIndex++; inclusionIndex++;
if ((subSeqNum == NULL) || (MmsValue_getType(subSeqNum) != MMS_UNSIGNED)) { if ((subSeqNum == NULL) || (MmsValue_getType(subSeqNum) != MMS_UNSIGNED))
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: received malformed report (SubSeqNum)\n"); printf("IED_CLIENT: received malformed report (SubSeqNum)\n");
goto exit_function; goto exit_function;
} }
else { else
matchingReport->subSeqNum = (uint16_t) MmsValue_toUint32(subSeqNum); {
matchingReport->subSeqNum = (uint16_t)MmsValue_toUint32(subSeqNum);
} }
MmsValue* moreSegmentsFollow = MmsValue_getElement(value, inclusionIndex); MmsValue* moreSegmentsFollow = MmsValue_getElement(value, inclusionIndex);
inclusionIndex++; inclusionIndex++;
if ((moreSegmentsFollow == NULL) || (MmsValue_getType(moreSegmentsFollow) != MMS_BOOLEAN)) { if ((moreSegmentsFollow == NULL) || (MmsValue_getType(moreSegmentsFollow) != MMS_BOOLEAN))
if ((subSeqNum == NULL) || (MmsValue_getType(subSeqNum) != MMS_UNSIGNED)) { {
if ((subSeqNum == NULL) || (MmsValue_getType(subSeqNum) != MMS_UNSIGNED))
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: received malformed report (MoreSegmentsFollow)\n"); printf("IED_CLIENT: received malformed report (MoreSegmentsFollow)\n");
goto exit_function; goto exit_function;
} }
} }
else { else
{
matchingReport->moreSegementsFollow = MmsValue_getBoolean(moreSegmentsFollow); matchingReport->moreSegementsFollow = MmsValue_getBoolean(moreSegmentsFollow);
} }
matchingReport->hasSequenceNumber = true; matchingReport->hasSequenceNumber = true;
} }
else { else
{
matchingReport->subSeqNum = 0; matchingReport->subSeqNum = 0;
matchingReport->moreSegementsFollow = false; matchingReport->moreSegementsFollow = false;
} }
MmsValue* inclusion = MmsValue_getElement(value, inclusionIndex); MmsValue* inclusion = MmsValue_getElement(value, inclusionIndex);
if ((inclusion == NULL) || (MmsValue_getType(inclusion) != MMS_BIT_STRING)) { if ((inclusion == NULL) || (MmsValue_getType(inclusion) != MMS_BIT_STRING))
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: received malformed report (inclusion)\n"); printf("IED_CLIENT: received malformed report (inclusion)\n");
@ -649,11 +697,14 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
int dataSetSize = MmsValue_getBitStringSize(inclusion); int dataSetSize = MmsValue_getBitStringSize(inclusion);
if (matchingReport->dataSetSize == -1) { if (matchingReport->dataSetSize == -1)
{
matchingReport->dataSetSize = dataSetSize; matchingReport->dataSetSize = dataSetSize;
} }
else { else
if (dataSetSize != matchingReport->dataSetSize) { {
if (dataSetSize != matchingReport->dataSetSize)
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: received malformed report (inclusion has no plausible size)\n"); printf("IED_CLIENT: received malformed report (inclusion has no plausible size)\n");
@ -664,14 +715,13 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
int includedElements = MmsValue_getNumberOfSetBits(inclusion); int includedElements = MmsValue_getNumberOfSetBits(inclusion);
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: Report includes %i data set elements of %i\n", includedElements, printf("IED_CLIENT: Report includes %i data set elements of %i\n", includedElements, dataSetSize);
dataSetSize);
int valueIndex = inclusionIndex + 1; int valueIndex = inclusionIndex + 1;
/* parse data-references if required */ /* parse data-references if required */
if (MmsValue_getBitStringBit(optFlds, 5) == true) { if (MmsValue_getBitStringBit(optFlds, 5) == true)
{
if (matchingReport->dataReferences == NULL) if (matchingReport->dataReferences == NULL)
matchingReport->dataReferences = MmsValue_createEmptyArray(dataSetSize); matchingReport->dataReferences = MmsValue_createEmptyArray(dataSetSize);
@ -679,19 +729,23 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
int elementIndex; int elementIndex;
for (elementIndex = 0; elementIndex < dataSetSize; elementIndex++) { for (elementIndex = 0; elementIndex < dataSetSize; elementIndex++)
if (MmsValue_getBitStringBit(inclusion, elementIndex) == true) { {
if (MmsValue_getBitStringBit(inclusion, elementIndex) == true)
{
MmsValue* dataSetElement = MmsValue_getElement(matchingReport->dataReferences, elementIndex); MmsValue* dataSetElement = MmsValue_getElement(matchingReport->dataReferences, elementIndex);
if (dataSetElement == NULL) { if (dataSetElement == NULL)
{
MmsValue* dataRefValue = MmsValue_getElement(value, valueIndex); MmsValue* dataRefValue = MmsValue_getElement(value, valueIndex);
if ((dataRefValue == NULL) || (MmsValue_getType(dataRefValue) != MMS_VISIBLE_STRING)) { if ((dataRefValue == NULL) || (MmsValue_getType(dataRefValue) != MMS_VISIBLE_STRING))
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: report contains invalid data reference\n"); printf("IED_CLIENT: report contains invalid data reference\n");
} }
else { else
{
dataSetElement = MmsValue_clone(dataRefValue); dataSetElement = MmsValue_clone(dataRefValue);
MmsValue_setElement(matchingReport->dataReferences, elementIndex, dataSetElement); MmsValue_setElement(matchingReport->dataReferences, elementIndex, dataSetElement);
@ -705,16 +759,23 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
int i; int i;
if (matchingReport->dataSetValues == NULL) { if (matchingReport->dataSetValues == NULL)
{
matchingReport->dataSetValues = MmsValue_createEmptyArray(dataSetSize); matchingReport->dataSetValues = MmsValue_createEmptyArray(dataSetSize);
matchingReport->reasonForInclusion = (ReasonForInclusion*) matchingReport->reasonForInclusion =
GLOBAL_MALLOC(sizeof(ReasonForInclusion) * dataSetSize); (ReasonForInclusion*)GLOBAL_MALLOC(sizeof(ReasonForInclusion) * dataSetSize);
int elementIndex; if (matchingReport->reasonForInclusion)
{
for (elementIndex = 0; elementIndex < dataSetSize; elementIndex++) int elementIndex;
matchingReport->reasonForInclusion[elementIndex] = IEC61850_REASON_NOT_INCLUDED;
for (elementIndex = 0; elementIndex < dataSetSize; elementIndex++)
matchingReport->reasonForInclusion[elementIndex] = IEC61850_REASON_NOT_INCLUDED;
}
else
{
goto exit_function;
}
} }
MmsValue* dataSetValues = matchingReport->dataSetValues; MmsValue* dataSetValues = matchingReport->dataSetValues;
@ -724,14 +785,16 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
if (hasReasonForInclusion) if (hasReasonForInclusion)
matchingReport->hasReasonForInclusion = true; matchingReport->hasReasonForInclusion = true;
for (i = 0; i < dataSetSize; i++) { for (i = 0; i < dataSetSize; i++)
if (MmsValue_getBitStringBit(inclusion, i) == true) { {
if (MmsValue_getBitStringBit(inclusion, i) == true)
{
MmsValue* dataSetElement = MmsValue_getElement(dataSetValues, i); MmsValue* dataSetElement = MmsValue_getElement(dataSetValues, i);
MmsValue* newElementValue = MmsValue_getElement(value, valueIndex); MmsValue* newElementValue = MmsValue_getElement(value, valueIndex);
if (newElementValue == NULL) { if (newElementValue == NULL)
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: report is missing expected element value\n"); printf("IED_CLIENT: report is missing expected element value\n");
@ -746,10 +809,12 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: update element value type: %i\n", MmsValue_getType(newElementValue)); printf("IED_CLIENT: update element value type: %i\n", MmsValue_getType(newElementValue));
if (hasReasonForInclusion) { if (hasReasonForInclusion)
{
MmsValue* reasonForInclusion = MmsValue_getElement(value, includedElements + valueIndex); MmsValue* reasonForInclusion = MmsValue_getElement(value, includedElements + valueIndex);
if ((reasonForInclusion == NULL) || (MmsValue_getType(reasonForInclusion) != MMS_BIT_STRING)) { if ((reasonForInclusion == NULL) || (MmsValue_getType(reasonForInclusion) != MMS_BIT_STRING))
{
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: report contains invalid reason-for-inclusion\n"); printf("IED_CLIENT: report contains invalid reason-for-inclusion\n");
@ -759,7 +824,7 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
matchingReport->reasonForInclusion[i] = IEC61850_REASON_NOT_INCLUDED; matchingReport->reasonForInclusion[i] = IEC61850_REASON_NOT_INCLUDED;
if (MmsValue_getBitStringBit(reasonForInclusion, 1) == true) if (MmsValue_getBitStringBit(reasonForInclusion, 1) == true)
matchingReport->reasonForInclusion[i] |= (ReasonForInclusion) IEC61850_REASON_DATA_CHANGE; matchingReport->reasonForInclusion[i] |= (ReasonForInclusion)IEC61850_REASON_DATA_CHANGE;
if (MmsValue_getBitStringBit(reasonForInclusion, 2) == true) if (MmsValue_getBitStringBit(reasonForInclusion, 2) == true)
matchingReport->reasonForInclusion[i] |= IEC61850_REASON_QUALITY_CHANGE; matchingReport->reasonForInclusion[i] |= IEC61850_REASON_QUALITY_CHANGE;
if (MmsValue_getBitStringBit(reasonForInclusion, 3) == true) if (MmsValue_getBitStringBit(reasonForInclusion, 3) == true)
@ -769,18 +834,20 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
if (MmsValue_getBitStringBit(reasonForInclusion, 5) == true) if (MmsValue_getBitStringBit(reasonForInclusion, 5) == true)
matchingReport->reasonForInclusion[i] |= IEC61850_REASON_GI; matchingReport->reasonForInclusion[i] |= IEC61850_REASON_GI;
} }
else { else
{
matchingReport->reasonForInclusion[i] = IEC61850_REASON_UNKNOWN; matchingReport->reasonForInclusion[i] = IEC61850_REASON_UNKNOWN;
} }
valueIndex++; valueIndex++;
} }
else { else
{
matchingReport->reasonForInclusion[i] = IEC61850_REASON_NOT_INCLUDED; matchingReport->reasonForInclusion[i] = IEC61850_REASON_NOT_INCLUDED;
} }
} }
if (matchingReport->callback != NULL) if (matchingReport->callback)
matchingReport->callback(matchingReport->callbackParameter, matchingReport); matchingReport->callback(matchingReport->callbackParameter, matchingReport);
exit_function: exit_function:
@ -789,4 +856,3 @@ exit_function:
return; return;
} }

File diff suppressed because it is too large Load Diff

@ -1,7 +1,7 @@
/* /*
* client_sv_control.c * client_sv_control.c
* *
* Copyright 2015-2022 Michael Zillgith * Copyright 2015-2025 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -29,7 +29,8 @@
#include "libiec61850_platform_includes.h" #include "libiec61850_platform_includes.h"
struct sClientSVControlBlock { struct sClientSVControlBlock
{
IedConnection connection; IedConnection connection;
bool isMulticast; bool isMulticast;
char* reference; char* reference;
@ -45,26 +46,30 @@ ClientSVControlBlock_create(IedConnection connection, const char* reference)
IedClientError error; IedClientError error;
MmsValue* value = IedConnection_readObject(connection, &error, reference, IEC61850_FC_MS); MmsValue* value = IedConnection_readObject(connection, &error, reference, IEC61850_FC_MS);
if ((error == IED_ERROR_OK) && (MmsValue_getType(value) != MMS_DATA_ACCESS_ERROR)) { if ((error == IED_ERROR_OK) && (MmsValue_getType(value) != MMS_DATA_ACCESS_ERROR))
{
isMulticast = true; isMulticast = true;
MmsValue_delete(value); MmsValue_delete(value);
} }
else { else
{
MmsValue_delete(value); MmsValue_delete(value);
value = IedConnection_readObject(connection, &error, reference, IEC61850_FC_US); value = IedConnection_readObject(connection, &error, reference, IEC61850_FC_US);
if ((error == IED_ERROR_OK) && (MmsValue_getType(value) != MMS_DATA_ACCESS_ERROR)) if ((error == IED_ERROR_OK) && (MmsValue_getType(value) != MMS_DATA_ACCESS_ERROR))
MmsValue_delete(value); MmsValue_delete(value);
else { else
{
MmsValue_delete(value); MmsValue_delete(value);
return NULL; return NULL;
} }
} }
ClientSVControlBlock self = (ClientSVControlBlock) GLOBAL_CALLOC(1, sizeof(struct sClientSVControlBlock)); ClientSVControlBlock self = (ClientSVControlBlock)GLOBAL_CALLOC(1, sizeof(struct sClientSVControlBlock));
if (self) { if (self)
{
self->connection = connection; self->connection = connection;
self->reference = StringUtils_copyString(reference); self->reference = StringUtils_copyString(reference);
self->isMulticast = isMulticast; self->isMulticast = isMulticast;
@ -76,7 +81,8 @@ ClientSVControlBlock_create(IedConnection connection, const char* reference)
void void
ClientSVControlBlock_destroy(ClientSVControlBlock self) ClientSVControlBlock_destroy(ClientSVControlBlock self)
{ {
if (self) { if (self)
{
GLOBAL_FREEMEM(self->reference); GLOBAL_FREEMEM(self->reference);
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
} }
@ -110,7 +116,6 @@ setBooleanVariable(ClientSVControlBlock self, const char* varName, bool value)
else else
IedConnection_writeBooleanValue(self->connection, &(self->lastError), refBuf, IEC61850_FC_US, value); IedConnection_writeBooleanValue(self->connection, &(self->lastError), refBuf, IEC61850_FC_US, value);
if (self->lastError == IED_ERROR_OK) if (self->lastError == IED_ERROR_OK)
return true; return true;
else else
@ -288,27 +293,36 @@ ClientSVControlBlock_getDstAddress(ClientSVControlBlock self)
PhyComAddress retVal; PhyComAddress retVal;
memset(&retVal, 0, sizeof(retVal)); memset(&retVal, 0, sizeof(retVal));
if (dstAddrValue == NULL) goto exit_error; if (dstAddrValue == NULL)
goto exit_error;
if (MmsValue_getType(dstAddrValue) != MMS_STRUCTURE) { if (MmsValue_getType(dstAddrValue) != MMS_STRUCTURE)
if (DEBUG_IED_CLIENT) printf("IED_CLIENT: SVCB - addr has wrong type\n"); {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: SVCB - addr has wrong type\n");
goto exit_cleanup; goto exit_cleanup;
} }
if (MmsValue_getArraySize(dstAddrValue) != 4) { if (MmsValue_getArraySize(dstAddrValue) != 4)
if (DEBUG_IED_CLIENT) printf("IED_CLIENT: SVCB - addr has wrong type\n"); {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: SVCB - addr has wrong type\n");
goto exit_cleanup; goto exit_cleanup;
} }
MmsValue* addr = MmsValue_getElement(dstAddrValue, 0); MmsValue* addr = MmsValue_getElement(dstAddrValue, 0);
if (MmsValue_getType(addr) != MMS_OCTET_STRING) { if (MmsValue_getType(addr) != MMS_OCTET_STRING)
if (DEBUG_IED_CLIENT) printf("IED_CLIENT: SVCB - addr has wrong type\n"); {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: SVCB - addr has wrong type\n");
goto exit_cleanup; goto exit_cleanup;
} }
if (MmsValue_getOctetStringSize(addr) != 6) { if (MmsValue_getOctetStringSize(addr) != 6)
if (DEBUG_IED_CLIENT) printf("IED_CLIENT: SVCB - addr has wrong size\n"); {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: SVCB - addr has wrong size\n");
goto exit_cleanup; goto exit_cleanup;
} }
@ -318,8 +332,10 @@ ClientSVControlBlock_getDstAddress(ClientSVControlBlock self)
MmsValue* prio = MmsValue_getElement(dstAddrValue, 1); MmsValue* prio = MmsValue_getElement(dstAddrValue, 1);
if (MmsValue_getType(prio) != MMS_UNSIGNED) { if (MmsValue_getType(prio) != MMS_UNSIGNED)
if (DEBUG_IED_CLIENT) printf("IED_CLIENT: SVCB - prio has wrong type\n"); {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: SVCB - prio has wrong type\n");
goto exit_cleanup; goto exit_cleanup;
} }
@ -327,8 +343,10 @@ ClientSVControlBlock_getDstAddress(ClientSVControlBlock self)
MmsValue* vid = MmsValue_getElement(dstAddrValue, 2); MmsValue* vid = MmsValue_getElement(dstAddrValue, 2);
if (MmsValue_getType(vid) != MMS_UNSIGNED) { if (MmsValue_getType(vid) != MMS_UNSIGNED)
if (DEBUG_IED_CLIENT) printf("IED_CLIENT: SVCB - vid has wrong type\n"); {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: SVCB - vid has wrong type\n");
goto exit_cleanup; goto exit_cleanup;
} }
@ -336,18 +354,18 @@ ClientSVControlBlock_getDstAddress(ClientSVControlBlock self)
MmsValue* appID = MmsValue_getElement(dstAddrValue, 3); MmsValue* appID = MmsValue_getElement(dstAddrValue, 3);
if (MmsValue_getType(appID) != MMS_UNSIGNED) { if (MmsValue_getType(appID) != MMS_UNSIGNED)
if (DEBUG_IED_CLIENT) printf("IED_CLIENT: SVCB - appID has wrong type\n"); {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: SVCB - appID has wrong type\n");
goto exit_cleanup; goto exit_cleanup;
} }
retVal.appId = MmsValue_toUint32(appID); retVal.appId = MmsValue_toUint32(appID);
exit_cleanup: exit_cleanup:
MmsValue_delete(dstAddrValue); MmsValue_delete(dstAddrValue);
exit_error: exit_error:
return retVal; return retVal;
} }

Loading…
Cancel
Save