From 7ec38e961580abc136c948aa602058e098023789 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 20 Oct 2018 14:10:01 +0200 Subject: [PATCH] - IEC 61850 client: added async client side RCB handling --- src/iec61850/client/client_control.c | 6 +- src/iec61850/client/client_report.c | 2 +- src/iec61850/client/client_report_control.c | 527 +++++++++++++++++- src/iec61850/client/ied_connection.c | 189 ++++--- src/iec61850/inc/iec61850_client.h | 2 +- .../inc_private/ied_connection_private.h | 26 +- 6 files changed, 647 insertions(+), 105 deletions(-) diff --git a/src/iec61850/client/client_control.c b/src/iec61850/client/client_control.c index 173a9be4..c748d79e 100644 --- a/src/iec61850/client/client_control.c +++ b/src/iec61850/client/client_control.c @@ -219,7 +219,7 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection if (DEBUG_IED_CLIENT) printf("IED_CLIENT: Detected edition %i control\n", self->edition); - private_IedConnection_addControlClient(connection, self); + iedConnection_addControlClient(connection, self); free_varspec: MmsVariableSpecification_destroy(ctlVarSpec); @@ -235,7 +235,7 @@ ControlObjectClient_destroy(ControlObjectClient self) { GLOBAL_FREEMEM(self->objectReference); - private_IedConnection_removeControlClient(self->connection, self); + iedConnection_removeControlClient(self->connection, self); if (self->ctlVal != NULL) MmsValue_delete(self->ctlVal); @@ -752,7 +752,7 @@ ControlObjectClient_getLastApplError(ControlObjectClient self) } void -private_ControlObjectClient_invokeCommandTerminationHandler(ControlObjectClient self) +controlObjectClient_invokeCommandTerminationHandler(ControlObjectClient self) { if (self->commandTerminationHandler != NULL) self->commandTerminationHandler(self->commandTerminaionHandlerParameter, self); diff --git a/src/iec61850/client/client_report.c b/src/iec61850/client/client_report.c index 1a294ca0..b2a76df9 100644 --- a/src/iec61850/client/client_report.c +++ b/src/iec61850/client/client_report.c @@ -343,7 +343,7 @@ IedConnection_triggerGIReport(IedConnection self, IedClientError* error, const c } void -private_IedConnection_handleReport(IedConnection self, MmsValue* value) +iedConnection_handleReport(IedConnection self, MmsValue* value) { MmsValue* rptIdValue = MmsValue_getElement(value, 0); diff --git a/src/iec61850/client/client_report_control.c b/src/iec61850/client/client_report_control.c index 262c60ce..a4672efc 100644 --- a/src/iec61850/client/client_report_control.c +++ b/src/iec61850/client/client_report_control.c @@ -3,7 +3,7 @@ * * Implementation of the ClientReportControlBlock class * - * Copyright 2013, 2014 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -388,11 +388,81 @@ updateOrClone(MmsValue** valuePtr, MmsValue* values, int index) *valuePtr = MmsValue_clone(MmsValue_getElement(values, index)); } +static bool +checkElementType(MmsValue* value, int index, MmsType type) +{ + MmsValue* element = MmsValue_getElement(value, index); + + if (element == NULL) + return false; + + if (MmsValue_getType(element) == type) + return true; + else + return false; +} + bool -private_ClientReportControlBlock_updateValues(ClientReportControlBlock self, MmsValue* values) +clientReportControlBlock_updateValues(ClientReportControlBlock self, MmsValue* values) { + /* check type and return false if type is wrong */ + + if (MmsValue_getType(values) != MMS_STRUCTURE) + return false; + int rcbElementCount = MmsValue_getArraySize(values); + if (self->isBuffered) { + if ((rcbElementCount < 13) || (rcbElementCount > 15)) { + return false; + } + + if (!checkElementType(values, 0, MMS_VISIBLE_STRING)) return false; + if (!checkElementType(values, 1, MMS_BOOLEAN)) return false; + if (!checkElementType(values, 2, MMS_VISIBLE_STRING)) return false; + if (!checkElementType(values, 3, MMS_UNSIGNED)) return false; + if (!checkElementType(values, 4, MMS_BIT_STRING)) return false; + if (!checkElementType(values, 5, MMS_UNSIGNED)) return false; + if (!checkElementType(values, 6, MMS_UNSIGNED)) return false; + if (!checkElementType(values, 7, MMS_BIT_STRING)) return false; + if (!checkElementType(values, 8, MMS_UNSIGNED)) return false; + if (!checkElementType(values, 9, MMS_BOOLEAN)) return false; + if (!checkElementType(values, 10, MMS_BOOLEAN)) return false; + if (!checkElementType(values, 11, MMS_OCTET_STRING)) return false; + if (!checkElementType(values, 12, MMS_BINARY_TIME)) return false; + + if (rcbElementCount == 14) { + if (!checkElementType(values, 13, MMS_OCTET_STRING)) return false; + } + else if (rcbElementCount == 15) { + if (!checkElementType(values, 13, MMS_INTEGER)) return false; + if (!checkElementType(values, 14, MMS_OCTET_STRING)) return false; + } + } + else { + if ((rcbElementCount < 11) || (rcbElementCount > 12)) { + return false; + } + + if (!checkElementType(values, 0, MMS_VISIBLE_STRING)) return false; + if (!checkElementType(values, 1, MMS_BOOLEAN)) return false; + if (!checkElementType(values, 2, MMS_BOOLEAN)) return false; + if (!checkElementType(values, 3, MMS_VISIBLE_STRING)) return false; + if (!checkElementType(values, 4, MMS_UNSIGNED)) return false; + if (!checkElementType(values, 5, MMS_BIT_STRING)) return false; + if (!checkElementType(values, 6, MMS_UNSIGNED)) return false; + if (!checkElementType(values, 7, MMS_UNSIGNED)) return false; + if (!checkElementType(values, 8, MMS_BIT_STRING)) return false; + if (!checkElementType(values, 9, MMS_UNSIGNED)) return false; + if (!checkElementType(values, 10, MMS_BOOLEAN)) return false; + + if (rcbElementCount == 12) { + if (!checkElementType(values, 11, MMS_OCTET_STRING)) return false; + } + } + + /* when type is correct, update local copy */ + updateOrClone(&(self->rptId), values, 0); updateOrClone(&(self->rptEna), values, 1); @@ -441,14 +511,120 @@ private_ClientReportControlBlock_updateValues(ClientReportControlBlock self, Mms return true; } -typedef void -(*IedConnection_GetRCBValuesHandler) (int invokeId, void* parameter, IedClientError err, ClientReportControlBlock rcb); +static void +readObjectHandlerInternal(int invokeId, void* parameter, MmsError err, MmsValue* value) +{ + IedConnection self = (IedConnection) parameter; + + IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); + + if (call) { + + IedConnection_GetRCBValuesHandler handler = (IedConnection_GetRCBValuesHandler) call->callback; + ClientReportControlBlock updateRcb = (ClientReportControlBlock) call->specificParameter; + char* rcbReference = (char*) call->specificParameter2; + + + if (err != MMS_ERROR_NONE) { + handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(err), NULL); + } + else { + + if (value == NULL) { + handler(invokeId, call->callbackParameter, IED_ERROR_OBJECT_DOES_NOT_EXIST, NULL); + } + else { + if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) { + if (DEBUG_IED_CLIENT) + printf("DEBUG_IED_CLIENT: getRCBValues returned data-access-error!\n"); + + handler(invokeId, call->callbackParameter, iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)), NULL); + } + else { + + ClientReportControlBlock returnRcb = updateRcb; + + if (returnRcb == NULL) + returnRcb = ClientReportControlBlock_create(rcbReference); + + if (clientReportControlBlock_updateValues(returnRcb, value)) { + handler(invokeId, call->callbackParameter, IED_ERROR_OK, returnRcb); + } + else { + if (DEBUG_IED_CLIENT) + printf("DEBUG_IED_CLIENT: getRCBValues returned wrong type!\n"); + + handler(invokeId, call->callbackParameter, IED_ERROR_TYPE_INCONSISTENT , NULL); + + if (updateRcb == NULL) + ClientReportControlBlock_destroy(returnRcb); + } + + } + + MmsValue_delete(value); + } + + } + + GLOBAL_FREEMEM(rcbReference); + + iedConnection_releaseOutstandingCall(self, call); + } + else { + if (DEBUG_IED_CLIENT) + printf("IED_CLIENT: internal error - no matching outstanding call!\n"); + } +} uint32_t -IedConnection_getRCBValuesAsync(IedConnection self, IedClientError* error, const char* rcbReference, +IedConnection_getRCBValuesAsync(IedConnection self, IedClientError* error, const char* rcbReference, ClientReportControlBlock updateRcb, IedConnection_GetRCBValuesHandler handler, void* parameter) { - //TODO implement + *error = IED_ERROR_OK; + + char domainId[65]; + char itemId[65]; + + char* domainName = MmsMapping_getMmsDomainFromObjectReference(rcbReference, domainId); + + if (domainName == NULL) { + *error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT; + return 0; + } + + strcpy(itemId, rcbReference + strlen(domainId) + 1); + + StringUtils_replace(itemId, '.', '$'); + + IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); + + if (call == NULL) { + *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; + return 0; + } + + call->callback = handler; + call->callbackParameter = parameter; + call->specificParameter = updateRcb; + call->specificParameter2 = StringUtils_copyString(rcbReference); + + if (DEBUG_IED_CLIENT) + printf("DEBUG_IED_CLIENT: readRCBValues for %s\n", rcbReference); + + MmsError err = MMS_ERROR_NONE; + + call->invokeId = MmsConnection_readVariableAsync(self->connection, &err, domainId, itemId, readObjectHandlerInternal, self); + + *error = iedConnection_mapMmsErrorToIedError(err); + + if (err != MMS_ERROR_NONE) { + GLOBAL_FREEMEM(call->specificParameter2); + iedConnection_releaseOutstandingCall(self, call); + return 0; + } + + return call->invokeId; } ClientReportControlBlock @@ -510,7 +686,7 @@ IedConnection_getRCBValues(IedConnection self, IedClientError* error, const char if (returnRcb == NULL) returnRcb = ClientReportControlBlock_create(rcbReference); - private_ClientReportControlBlock_updateValues(returnRcb, rcb); + clientReportControlBlock_updateValues(returnRcb, rcb); MmsValue_delete(rcb); @@ -519,11 +695,346 @@ IedConnection_getRCBValues(IedConnection self, IedClientError* error, const char return returnRcb; } +static void +writeMultipleVariablesHandler(int invokeId, void* parameter, MmsError mmsError, LinkedList /* */ accessResults) +{ + IedConnection self = (IedConnection) parameter; + + IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); + + if (call) { + + IedConnection_WriteObjectHandler handler = (IedConnection_WriteObjectHandler) call->callback; + + if (accessResults != NULL) { + + IedClientError error = IED_ERROR_OK; + + LinkedList accessResult = LinkedList_getNext(accessResults); + + while (accessResult != NULL) { + MmsValue* dataAccessError = (MmsValue*) accessResult->data; + + if (MmsValue_getDataAccessError(dataAccessError) != DATA_ACCESS_ERROR_SUCCESS) { + error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(dataAccessError)); + break; + } + + accessResult = LinkedList_getNext(accessResult); + } + + LinkedList_destroyDeep(accessResults, (LinkedListValueDeleteFunction) MmsValue_delete); + + handler(invokeId, call->callbackParameter, error); + } + else { + handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(mmsError)); + } + + iedConnection_releaseOutstandingCall(self, call); + } + else { + if (DEBUG_IED_CLIENT) + printf("IED_CLIENT: internal error - no matching outstanding call!\n"); + } +} + +struct sWriteRcbVariablesParameter +{ + LinkedList itemIds; + LinkedList values; + LinkedList currentItemId; + LinkedList currentValue; + char* domainId; + uint32_t originalInvokeId; +}; + +static void +releaseWriteCall(IedConnection self, IedConnectionOutstandingCall call, struct sWriteRcbVariablesParameter* param) +{ + GLOBAL_FREEMEM(param->domainId); + + LinkedList_destroy(param->itemIds); + LinkedList_destroyStatic(param->values); + + GLOBAL_FREEMEM(param); + + iedConnection_releaseOutstandingCall(self, call); +} + +static void +writeVariableHandler(int invokeId, void* parameter, MmsError mmsError, MmsDataAccessError accessError) +{ + IedConnection self = (IedConnection) parameter; + + IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); + + if (call) { + + IedConnection_WriteObjectHandler handler = (IedConnection_WriteObjectHandler) call->callback; + + struct sWriteRcbVariablesParameter* param = (struct sWriteRcbVariablesParameter*) call->specificParameter2; + + if ((mmsError != MMS_ERROR_NONE) || (accessError != DATA_ACCESS_ERROR_SUCCESS)) { + + IedClientError err; + + if (mmsError != MMS_ERROR_NONE) + err = iedConnection_mapMmsErrorToIedError(mmsError); + else + err = iedConnection_mapDataAccessErrorToIedError(accessError); + + handler(param->originalInvokeId, call->callbackParameter, err); + + releaseWriteCall(self, call, param); + } + + + param->currentItemId = LinkedList_getNext(param->currentItemId); + + if (param->currentItemId == NULL) { + handler(param->originalInvokeId, call->callbackParameter, IED_ERROR_OK); + + releaseWriteCall(self, call, param); + } + else { + param->currentValue = LinkedList_getNext(param->currentValue); + + char* itemId = (char*) LinkedList_getData(param->currentItemId); + MmsValue* value = (MmsValue*) LinkedList_getData(param->currentValue); + + MmsError writeError; + + call->invokeId = MmsConnection_writeVariableAsync(self->connection, &writeError, param->domainId, itemId, value, writeVariableHandler, self); + + if (writeError != MMS_ERROR_NONE) { + handler(param->originalInvokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(writeError)); + + releaseWriteCall(self, call, param); + } + } + } + else { + if (DEBUG_IED_CLIENT) + printf("IED_CLIENT: internal error - no matching outstanding call!\n"); + } +} + uint32_t IedConnection_setRCBValuesAsync(IedConnection self, IedClientError* error, ClientReportControlBlock rcb, uint32_t parametersMask, bool singleRequest, IedConnection_WriteObjectHandler handler, void* parameter) { - //TODO implement + uint32_t invokeId = 0; + + bool isBuffered = ClientReportControlBlock_isBuffered(rcb); + bool sendGILast = false; /* GI should be sent last when RptEna=TRUE is included */ + + char domainId[65]; + char itemId[129]; + + char* rcbReference = ClientReportControlBlock_getObjectReference(rcb); + + MmsMapping_getMmsDomainFromObjectReference(rcbReference, domainId); + + strcpy(itemId, rcbReference + strlen(domainId) + 1); + + StringUtils_replace(itemId, '.', '$'); + + if (DEBUG_IED_CLIENT) + printf("DEBUG_IED_CLIENT: setRCBValues for %s\n", rcbReference); + + int itemIdLen = strlen(itemId); + + /* prepare data to send -> create the list of requested itemIds references */ + LinkedList itemIds = LinkedList_create(); + LinkedList values = LinkedList_create(); + + /* add resv/resvTms as first element and rptEna as last element */ + if (parametersMask & RCB_ELEMENT_RESV) { + if (isBuffered) + goto error_invalid_parameter; + + strcpy(itemId + itemIdLen, "$Resv"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, rcb->resv); + } + + if (parametersMask & RCB_ELEMENT_RESV_TMS) { + if (!isBuffered) + goto error_invalid_parameter; + + strcpy(itemId + itemIdLen, "$ResvTms"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, rcb->resvTms); + } + + if (parametersMask & RCB_ELEMENT_RPT_ID) { + strcpy(itemId + itemIdLen, "$RptID"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, rcb->rptId); + } + + if (parametersMask & RCB_ELEMENT_DATSET) { + strcpy(itemId + itemIdLen, "$DatSet"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, rcb->datSet); + } + + if (parametersMask & RCB_ELEMENT_ENTRY_ID) { + strcpy(itemId + itemIdLen, "$EntryID"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, rcb->entryId); + } + + if (parametersMask & RCB_ELEMENT_OPT_FLDS) { + strcpy(itemId + itemIdLen, "$OptFlds"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, rcb->optFlds); + } + + if (parametersMask & RCB_ELEMENT_BUF_TM) { + strcpy(itemId + itemIdLen, "$BufTm"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, rcb->bufTm); + } + + if (parametersMask & RCB_ELEMENT_TRG_OPS) { + strcpy(itemId + itemIdLen, "$TrgOps"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, rcb->trgOps); + } + + if (parametersMask & RCB_ELEMENT_INTG_PD) { + strcpy(itemId + itemIdLen, "$IntgPd"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, rcb->intgPd); + } + + if (parametersMask & RCB_ELEMENT_GI) { + + if (parametersMask & RCB_ELEMENT_RPT_ENA) { + if (MmsValue_getBoolean(rcb->rptEna)) + sendGILast = true; + } + + if (sendGILast == false) { + strcpy(itemId + itemIdLen, "$GI"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, rcb->gi); + } + } + + if (parametersMask & RCB_ELEMENT_PURGE_BUF) { + if (!isBuffered) + goto error_invalid_parameter; + + strcpy(itemId + itemIdLen, "$PurgeBuf"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, rcb->purgeBuf); + } + + if (parametersMask & RCB_ELEMENT_TIME_OF_ENTRY) { + if (!isBuffered) + goto error_invalid_parameter; + + strcpy(itemId + itemIdLen, "$TimeofEntry"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, rcb->timeOfEntry); + } + + if (parametersMask & RCB_ELEMENT_RPT_ENA) { + strcpy(itemId + itemIdLen, "$RptEna"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, rcb->rptEna); + } + + if (sendGILast) { + strcpy(itemId + itemIdLen, "$GI"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, rcb->gi); + } + + IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); + + if (call == NULL) { + *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; + goto exit_function; + } + + call->callback = handler; + call->callbackParameter = parameter; + call->specificParameter = rcb; + + MmsError err; + + if (singleRequest) { + + invokeId = MmsConnection_writeMultipleVariablesAsync(self->connection, &err, domainId, itemIds, values, writeMultipleVariablesHandler, self); + + *error = iedConnection_mapMmsErrorToIedError(err); + + if (err != MMS_ERROR_NONE) { + iedConnection_releaseOutstandingCall(self, call); + } + + goto exit_function; + } + else { + + struct sWriteRcbVariablesParameter* param = (struct sWriteRcbVariablesParameter*) GLOBAL_MALLOC(sizeof(struct sWriteRcbVariablesParameter)); + + call->specificParameter2 = param; + + param->itemIds = itemIds; + param->values = values; + + param->currentItemId = LinkedList_getNext(itemIds); + param->currentValue = LinkedList_getNext(values); + param->domainId = StringUtils_copyString(domainId); + + char* itemId = (char*) LinkedList_getData(param->currentItemId); + MmsValue* value = (MmsValue*) LinkedList_getData(param->currentValue); + + call->invokeId = MmsConnection_writeVariableAsync(self->connection, &err, domainId, itemId, value, writeVariableHandler, self); + + param->originalInvokeId = call->invokeId; + + invokeId = call->invokeId; + + *error = iedConnection_mapMmsErrorToIedError(err); + + if (err != MMS_ERROR_NONE) { + iedConnection_releaseOutstandingCall(self, call); + GLOBAL_FREEMEM(param->domainId); + GLOBAL_FREEMEM(param); + goto exit_function; + } + else + return invokeId; + } + +error_invalid_parameter: + *error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT; + +exit_function: + LinkedList_destroy(itemIds); + LinkedList_destroyStatic(values); + + return invokeId; } void diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index ea56bdf7..ad9d4fae 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -153,6 +153,61 @@ iedConnection_mapDataAccessErrorToIedError(MmsDataAccessError mmsError) } } +IedConnectionOutstandingCall +iedConnection_allocateOutstandingCall(IedConnection self) +{ + Semaphore_wait(self->outstandingCallsLock); + + IedConnectionOutstandingCall call = NULL; + + int i = 0; + + for (i = 0; i < OUTSTANDING_CALLS; i++) { + if (self->outstandingCalls[i].used == false) { + self->outstandingCalls[i].used = true; + call = &(self->outstandingCalls[i]); + break; + } + } + + Semaphore_post(self->outstandingCallsLock); + + return call; +} + +void +iedConnection_releaseOutstandingCall(IedConnection self, IedConnectionOutstandingCall call) +{ + Semaphore_wait(self->outstandingCallsLock); + + call->used = false; + + Semaphore_post(self->outstandingCallsLock); +} + +IedConnectionOutstandingCall +iedConnection_lookupOutstandingCall(IedConnection self, uint32_t invokeId) +{ + Semaphore_wait(self->outstandingCallsLock); + + IedConnectionOutstandingCall call = NULL; + + int i = 0; + + for (i = 0; i < OUTSTANDING_CALLS; i++) { + + printf("%d: used: %d invokeId: %d\n", i, self->outstandingCalls[i].used, self->outstandingCalls[i].invokeId); + + if ((self->outstandingCalls[i].used) && (self->outstandingCalls[i].invokeId == invokeId)) { + call = &(self->outstandingCalls[i]); + break; + } + } + + Semaphore_post(self->outstandingCallsLock); + + return call; +} static ICLogicalDevice* ICLogicalDevice_create(char* name) @@ -234,7 +289,7 @@ ClientDataSet_getDataSetSize(ClientDataSet self) } bool -private_IedConnection_doesControlObjectMatch(const char* objRef, const char* cntrlObj) +iedConnection_doesControlObjectMatch(const char* objRef, const char* cntrlObj) { int i = 0; @@ -418,7 +473,7 @@ handleLastApplErrorMessage(IedConnection self, MmsValue* lastApplError) const char* objectRef = ControlObjectClient_getObjectReference(object); - if (private_IedConnection_doesControlObjectMatch(objectRef, MmsValue_toString(cntrlObj))) { + if (iedConnection_doesControlObjectMatch(objectRef, MmsValue_toString(cntrlObj))) { ControlObjectClient_setLastApplError(object, self->lastApplError); } @@ -438,7 +493,7 @@ informationReportHandler(void* parameter, char* domainName, if (domainName == NULL) { if (isVariableListName) { - private_IedConnection_handleReport(self, value); + iedConnection_handleReport(self, value); } else { if (strcmp(variableListName, "LastApplError") == 0) @@ -461,7 +516,7 @@ informationReportHandler(void* parameter, char* domainName, char* objectRef = ControlObjectClient_getObjectReference(object); if (doesReportMatchControlObject(domainName, variableListName, objectRef)) - private_ControlObjectClient_invokeCommandTerminationHandler(object); + controlObjectClient_invokeCommandTerminationHandler(object); control = LinkedList_getNext(control); } @@ -718,62 +773,6 @@ IedConnection_destroy(IedConnection self) GLOBAL_FREEMEM(self); } -static IedConnectionOutstandingCall -allocateOutstandingCall(IedConnection self) -{ - Semaphore_wait(self->outstandingCallsLock); - - IedConnectionOutstandingCall call = NULL; - - int i = 0; - - for (i = 0; i < OUTSTANDING_CALLS; i++) { - if (self->outstandingCalls[i].used == false) { - self->outstandingCalls[i].used = true; - call = &(self->outstandingCalls[i]); - break; - } - } - - Semaphore_post(self->outstandingCallsLock); - - return call; -} - -static void -releaseOutstandingCall(IedConnection self, IedConnectionOutstandingCall call) -{ - Semaphore_wait(self->outstandingCallsLock); - - call->used = false; - - Semaphore_post(self->outstandingCallsLock); -} - -static IedConnectionOutstandingCall -lookupOutstandingCall(IedConnection self, uint32_t invokeId) -{ - Semaphore_wait(self->outstandingCallsLock); - - IedConnectionOutstandingCall call = NULL; - - int i = 0; - - for (i = 0; i < OUTSTANDING_CALLS; i++) { - - printf("%d: used: %d invokeId: %d\n", i, self->outstandingCalls[i].used, self->outstandingCalls[i].invokeId); - - if ((self->outstandingCalls[i].used) && (self->outstandingCalls[i].invokeId == invokeId)) { - call = &(self->outstandingCalls[i]); - break; - } - } - - Semaphore_post(self->outstandingCallsLock); - - return call; -} - MmsVariableSpecification* IedConnection_getVariableSpecification(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc) @@ -813,7 +812,7 @@ getAccessAttrHandler(int invokeId, void* parameter, MmsError err, MmsVariableSpe { IedConnection self = (IedConnection) parameter; - IedConnectionOutstandingCall call = lookupOutstandingCall(self, invokeId); + IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); if (call) { @@ -821,7 +820,7 @@ getAccessAttrHandler(int invokeId, void* parameter, MmsError err, MmsVariableSpe handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(err), typeSpec); - releaseOutstandingCall(self, call); + iedConnection_releaseOutstandingCall(self, call); } else { if (DEBUG_IED_CLIENT) @@ -841,7 +840,7 @@ IedConnection_getVariableSpecificationAsync(IedConnection self, IedClientError* char* domainId; char* itemId; - MmsError mmsError; + MmsError err; MmsVariableSpecification* varSpec = NULL; domainId = MmsMapping_getMmsDomainFromObjectReference(dataAttributeReference, domainIdBuffer); @@ -852,7 +851,7 @@ IedConnection_getVariableSpecificationAsync(IedConnection self, IedClientError* goto cleanup_and_exit; } - IedConnectionOutstandingCall call = allocateOutstandingCall(self); + IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); if (call == NULL) { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; @@ -862,11 +861,14 @@ IedConnection_getVariableSpecificationAsync(IedConnection self, IedClientError* call->callback = handler; call->callbackParameter = parameter; - call->invokeId = MmsConnection_getVariableAccessAttributesAsync(self->connection, &mmsError, domainId, itemId, getAccessAttrHandler, self); + call->invokeId = MmsConnection_getVariableAccessAttributesAsync(self->connection, &err, domainId, itemId, getAccessAttrHandler, self); invokeId = call->invokeId; - *error = iedConnection_mapMmsErrorToIedError(mmsError); + *error = iedConnection_mapMmsErrorToIedError(err); + + if (err != MMS_ERROR_NONE) + iedConnection_releaseOutstandingCall(self, call); cleanup_and_exit: @@ -892,7 +894,7 @@ readObjectHandlerInternal(int invokeId, void* parameter, MmsError err, MmsValue* { IedConnection self = (IedConnection) parameter; - IedConnectionOutstandingCall call = lookupOutstandingCall(self, invokeId); + IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); if (call) { @@ -900,7 +902,7 @@ readObjectHandlerInternal(int invokeId, void* parameter, MmsError err, MmsValue* handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(err), value); - releaseOutstandingCall(self, call); + iedConnection_releaseOutstandingCall(self, call); } else { if (DEBUG_IED_CLIENT) @@ -928,7 +930,7 @@ IedConnection_readObjectAsync(IedConnection self, IedClientError* error, const c return 0; } - IedConnectionOutstandingCall call = allocateOutstandingCall(self); + IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); if (call == NULL) { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; @@ -975,7 +977,7 @@ IedConnection_readObjectAsync(IedConnection self, IedClientError* error, const c if (err != MMS_ERROR_NONE) *error = iedConnection_mapMmsErrorToIedError(err); - releaseOutstandingCall(self, call); + iedConnection_releaseOutstandingCall(self, call); return 0; } @@ -1295,7 +1297,7 @@ writeVariableHandler(int invokeId, void* parameter, MmsError err, MmsDataAccessE { IedConnection self = (IedConnection) parameter; - IedConnectionOutstandingCall call = lookupOutstandingCall(self, invokeId); + IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); if (call) { @@ -1308,7 +1310,7 @@ writeVariableHandler(int invokeId, void* parameter, MmsError err, MmsDataAccessE handler(invokeId, call->callbackParameter, iedError); - releaseOutstandingCall(self, call); + iedConnection_releaseOutstandingCall(self, call); } else { if (DEBUG_IED_CLIENT) @@ -1320,6 +1322,8 @@ uint32_t IedConnection_writeObjectAsync(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc, MmsValue* value, IedConnection_WriteObjectHandler handler, void* parameter) { + *error = IED_ERROR_OK; + char domainIdBuffer[65]; char itemIdBuffer[65]; @@ -1334,7 +1338,7 @@ IedConnection_writeObjectAsync(IedConnection self, IedClientError* error, const return 0; } - IedConnectionOutstandingCall call = allocateOutstandingCall(self); + IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); if (call == NULL) { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; @@ -1380,6 +1384,11 @@ IedConnection_writeObjectAsync(IedConnection self, IedClientError* error, const *error = iedConnection_mapMmsErrorToIedError(err); + if (err != MMS_ERROR_NONE) { + iedConnection_releaseOutstandingCall(self, call); + return 0; + } + return call->invokeId; } @@ -1650,7 +1659,7 @@ fileDirectoryHandler(int invokeId, void* parameter, MmsError err, char* filename { IedConnection self = (IedConnection) parameter; - IedConnectionOutstandingCall call = lookupOutstandingCall(self, invokeId); + IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); if (call) { @@ -1659,7 +1668,7 @@ fileDirectoryHandler(int invokeId, void* parameter, MmsError err, char* filename handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(err), filename, size, lastModfified, moreFollows); if (filename == NULL) - releaseOutstandingCall(self, call); + iedConnection_releaseOutstandingCall(self, call); } else { if (DEBUG_IED_CLIENT) @@ -1674,7 +1683,7 @@ IedConnection_getFileDirectoryAsync(IedConnection self, IedClientError* error, c MmsError err = MMS_ERROR_NONE; - IedConnectionOutstandingCall call = allocateOutstandingCall(self); + IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); if (call == NULL) { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; @@ -1684,12 +1693,16 @@ IedConnection_getFileDirectoryAsync(IedConnection self, IedClientError* error, c call->callback = handler; call->callbackParameter = parameter; - call->invokeId = MmsConnection_getFileDirectoryAsync(self->connection, &err, directoryName, continueAfter, fileDirectoryHandler, self); *error = iedConnection_mapMmsErrorToIedError(err); + if (err != MMS_ERROR_NONE) { + iedConnection_releaseOutstandingCall(self, call); + return 0; + } + return call->invokeId; } @@ -2964,7 +2977,7 @@ readJournalHandler(int invokeId, void* parameter, MmsError err, LinkedList /* callbackParameter, iedConnection_mapMmsErrorToIedError(err), journalEntries, moreFollows); - releaseOutstandingCall(self, call); + iedConnection_releaseOutstandingCall(self, call); } else { if (DEBUG_IED_CLIENT) @@ -2996,7 +3009,7 @@ IedConnection_queryLogByTimeAsync(IedConnection self, IedClientError* error, con logName[0] = 0; logName++; - IedConnectionOutstandingCall call = allocateOutstandingCall(self); + IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); if (call == NULL) { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; @@ -3022,6 +3035,11 @@ IedConnection_queryLogByTimeAsync(IedConnection self, IedClientError* error, con *error = iedConnection_mapMmsErrorToIedError(err); + if (err != MMS_ERROR_NONE) { + iedConnection_releaseOutstandingCall(self, call); + return 0; + } + return call->invokeId; } else { @@ -3047,7 +3065,7 @@ IedConnection_queryLogAfterAsync(IedConnection self, IedClientError* error, cons logName[0] = 0; logName++; - IedConnectionOutstandingCall call = allocateOutstandingCall(self); + IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); if (call == NULL) { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; @@ -3069,6 +3087,11 @@ IedConnection_queryLogAfterAsync(IedConnection self, IedClientError* error, cons *error = iedConnection_mapMmsErrorToIedError(err); + if (err != MMS_ERROR_NONE) { + iedConnection_releaseOutstandingCall(self, call); + return 0; + } + return call->invokeId; } else { @@ -3131,13 +3154,13 @@ IedConnection_getLastApplError(IedConnection self) } void -private_IedConnection_addControlClient(IedConnection self, ControlObjectClient control) +iedConnection_addControlClient(IedConnection self, ControlObjectClient control) { LinkedList_add(self->clientControls, control); } void -private_IedConnection_removeControlClient(IedConnection self, ControlObjectClient control) +iedConnection_removeControlClient(IedConnection self, ControlObjectClient control) { LinkedList_remove(self->clientControls, control); } diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h index 4bf6fcd7..ea56d804 100644 --- a/src/iec61850/inc/iec61850_client.h +++ b/src/iec61850/inc/iec61850_client.h @@ -964,7 +964,7 @@ typedef void (*IedConnection_GetRCBValuesHandler) (int invokeId, void* parameter, IedClientError err, ClientReportControlBlock rcb); LIB61850_API uint32_t -IedConnection_getRCBValuesAsync(IedConnection self, IedClientError* error, const char* rcbReference, +IedConnection_getRCBValuesAsync(IedConnection self, IedClientError* error, const char* rcbReference, ClientReportControlBlock updateRcb, IedConnection_GetRCBValuesHandler handler, void* parameter); /** Describes the reason for the inclusion of the element in the report */ diff --git a/src/iec61850/inc_private/ied_connection_private.h b/src/iec61850/inc_private/ied_connection_private.h index cb5cb385..f5eb8964 100644 --- a/src/iec61850/inc_private/ied_connection_private.h +++ b/src/iec61850/inc_private/ied_connection_private.h @@ -37,6 +37,8 @@ struct sIedConnectionOutstandingCall { uint32_t invokeId; void* callback; void* callbackParameter; + void* specificParameter; /* function/service specific parameter */ + void* specificParameter2; /* function/service specific parameter */ }; struct sIedConnection @@ -81,23 +83,20 @@ struct sClientReportControlBlock { MmsValue* owner; }; -LIB61850_INTERNAL IedClientError -private_IedConnection_mapMmsErrorToIedError(MmsError mmsError); - LIB61850_INTERNAL bool -private_IedConnection_doesControlObjectMatch(const char* objRef, const char* cntrlObj); +iedConnection_doesControlObjectMatch(const char* objRef, const char* cntrlObj); LIB61850_INTERNAL void -private_IedConnection_addControlClient(IedConnection self, ControlObjectClient control); +iedConnection_addControlClient(IedConnection self, ControlObjectClient control); LIB61850_INTERNAL void -private_IedConnection_removeControlClient(IedConnection self, ControlObjectClient control); +iedConnection_removeControlClient(IedConnection self, ControlObjectClient control); LIB61850_INTERNAL bool -private_ClientReportControlBlock_updateValues(ClientReportControlBlock self, MmsValue* values); +clientReportControlBlock_updateValues(ClientReportControlBlock self, MmsValue* values); LIB61850_INTERNAL void -private_IedConnection_handleReport(IedConnection self, MmsValue* value); +iedConnection_handleReport(IedConnection self, MmsValue* value); LIB61850_INTERNAL IedClientError iedConnection_mapMmsErrorToIedError(MmsError mmsError); @@ -105,6 +104,15 @@ iedConnection_mapMmsErrorToIedError(MmsError mmsError); LIB61850_INTERNAL IedClientError iedConnection_mapDataAccessErrorToIedError(MmsDataAccessError mmsError); +LIB61850_INTERNAL IedConnectionOutstandingCall +iedConnection_allocateOutstandingCall(IedConnection self); + +LIB61850_INTERNAL void +iedConnection_releaseOutstandingCall(IedConnection self, IedConnectionOutstandingCall call); + +LIB61850_INTERNAL IedConnectionOutstandingCall +iedConnection_lookupOutstandingCall(IedConnection self, uint32_t invokeId); + LIB61850_INTERNAL ClientReport ClientReport_create(void); @@ -112,7 +120,7 @@ LIB61850_INTERNAL void ClientReport_destroy(ClientReport self); LIB61850_INTERNAL void -private_ControlObjectClient_invokeCommandTerminationHandler(ControlObjectClient self); +controlObjectClient_invokeCommandTerminationHandler(ControlObjectClient self); /* some declarations that are shared with server side ! */