diff --git a/src/iec61850/client/client_report_control.c b/src/iec61850/client/client_report_control.c index 7f3377ab..16a8d0cb 100644 --- a/src/iec61850/client/client_report_control.c +++ b/src/iec61850/client/client_report_control.c @@ -522,7 +522,7 @@ readObjectHandlerInternal(uint32_t invokeId, void* parameter, MmsError err, MmsV IedConnection_GetRCBValuesHandler handler = (IedConnection_GetRCBValuesHandler) call->callback; ClientReportControlBlock updateRcb = (ClientReportControlBlock) call->specificParameter; - char* rcbReference = (char*) call->specificParameter2; + char* rcbReference = (char*) call->specificParameter2.pointer; if (err != MMS_ERROR_NONE) { @@ -607,7 +607,7 @@ IedConnection_getRCBValuesAsync(IedConnection self, IedClientError* error, const call->callback = handler; call->callbackParameter = parameter; call->specificParameter = updateRcb; - call->specificParameter2 = StringUtils_copyString(rcbReference); + call->specificParameter2.pointer = StringUtils_copyString(rcbReference); if (DEBUG_IED_CLIENT) printf("DEBUG_IED_CLIENT: readRCBValues for %s\n", rcbReference); @@ -619,7 +619,7 @@ IedConnection_getRCBValuesAsync(IedConnection self, IedClientError* error, const *error = iedConnection_mapMmsErrorToIedError(err); if (err != MMS_ERROR_NONE) { - GLOBAL_FREEMEM(call->specificParameter2); + GLOBAL_FREEMEM(call->specificParameter2.pointer); iedConnection_releaseOutstandingCall(self, call); return 0; } @@ -773,7 +773,7 @@ writeVariableHandler(uint32_t invokeId, void* parameter, MmsError mmsError, MmsD IedConnection_WriteObjectHandler handler = (IedConnection_WriteObjectHandler) call->callback; - struct sWriteRcbVariablesParameter* param = (struct sWriteRcbVariablesParameter*) call->specificParameter2; + struct sWriteRcbVariablesParameter* param = (struct sWriteRcbVariablesParameter*) call->specificParameter2.pointer; if ((mmsError != MMS_ERROR_NONE) || (accessError != DATA_ACCESS_ERROR_SUCCESS)) { @@ -997,7 +997,7 @@ IedConnection_setRCBValuesAsync(IedConnection self, IedClientError* error, Clien struct sWriteRcbVariablesParameter* param = (struct sWriteRcbVariablesParameter*) GLOBAL_MALLOC(sizeof(struct sWriteRcbVariablesParameter)); - call->specificParameter2 = param; + call->specificParameter2.pointer = param; param->itemIds = itemIds; param->values = values; diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index 28faef96..0a980b2f 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -1908,6 +1908,162 @@ IedConnection_getFile(IedConnection self, IedClientError* error, const char* fil return clientFileReadHandler.byteReceived; } + +static void +mmsConnectionFileCloseHandler (uint32_t invokeId, void* parameter, MmsError mmsError, bool success) +{ + IedConnection self = (IedConnection) parameter; + + IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); + + if (call) { + IedConnection_GetFileAsyncHandler handler = (IedConnection_GetFileAsyncHandler) call->callback; + + iedConnection_releaseOutstandingCall(self, call); + } + else { + if (DEBUG_IED_CLIENT) + printf("IED_CLIENT: internal error - no matching outstanding call!\n"); + } +} + +static void +mmsConnectionFileReadHandler (uint32_t invokeId, void* parameter, MmsError mmsError, uint8_t* buffer, uint32_t byteReceived, + bool moreFollows) +{ + IedConnection self = (IedConnection) parameter; + + IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); + + if (call) { + + IedConnection_GetFileAsyncHandler handler = (IedConnection_GetFileAsyncHandler) call->callback; + + if (mmsError != MMS_ERROR_NONE) { + IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError); + + handler(call->specificParameter2.getFileInfo.originalInvokeId, call->callbackParameter, err, invokeId, NULL, 0, false); + + /* close file */ + MmsConnection_fileCloseAsync(self->connection, &mmsError, call->specificParameter2.getFileInfo.frsmId, mmsConnectionFileCloseHandler, self); + + if (mmsError != MMS_ERROR_NONE) + iedConnection_releaseOutstandingCall(self, call); + } + else { + bool cont = handler(call->specificParameter2.getFileInfo.originalInvokeId, call->callbackParameter, IED_ERROR_OK, invokeId, buffer, byteReceived, moreFollows); + + if ((moreFollows == false) || (cont == false)) { + /* close file */ + MmsConnection_fileCloseAsync(self->connection, &mmsError, call->specificParameter2.getFileInfo.frsmId, mmsConnectionFileCloseHandler, self); + + if (mmsError != MMS_ERROR_NONE) + iedConnection_releaseOutstandingCall(self, call); + } + else { + /* send next read request */ + + call->invokeId = MmsConnection_fileReadAsync(self->connection, &mmsError, call->specificParameter2.getFileInfo.frsmId, + mmsConnectionFileReadHandler, self); + + if (mmsError != MMS_ERROR_NONE) { + IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError); + + handler(invokeId, call->callbackParameter, err, invokeId, NULL, 0, false); + + /* close file */ + MmsConnection_fileCloseAsync(self->connection, &mmsError, call->specificParameter2.getFileInfo.frsmId, mmsConnectionFileCloseHandler, self); + + if (mmsError != MMS_ERROR_NONE) { + iedConnection_releaseOutstandingCall(self, call); + } + + } + } + } + + } + else { + if (DEBUG_IED_CLIENT) + printf("IED_CLIENT: internal error - no matching outstanding call!\n"); + } +} + +static void +mmsConnectionFileOpenHandler (uint32_t invokeId, void* parameter, MmsError mmsError, int32_t frsmId, uint32_t fileSize, uint64_t lastModified) +{ + IedConnection self = (IedConnection) parameter; + + IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); + + if (call) { + + IedConnection_GetFileAsyncHandler handler = (IedConnection_GetFileAsyncHandler) call->callback; + + call->specificParameter2.getFileInfo.originalInvokeId = invokeId; + + if (mmsError != MMS_ERROR_NONE) { + IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError); + + handler(invokeId, call->callbackParameter, err, invokeId, NULL, 0, false); + + iedConnection_releaseOutstandingCall(self, call); + } + else { + + call->specificParameter2.getFileInfo.frsmId = frsmId; + call->specificParameter2.getFileInfo.originalInvokeId = invokeId; + call->invokeId = MmsConnection_fileReadAsync(self->connection, &mmsError, frsmId, mmsConnectionFileReadHandler, self); + + if (mmsError != MMS_ERROR_NONE) { + IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError); + + handler(invokeId, call->callbackParameter, err, invokeId, NULL, 0, false); + + /* close file */ + MmsConnection_fileCloseAsync(self->connection, &mmsError, frsmId, mmsConnectionFileCloseHandler, self); + + if (mmsError != MMS_ERROR_NONE) + iedConnection_releaseOutstandingCall(self, call); + } + } + + } + else { + if (DEBUG_IED_CLIENT) + printf("IED_CLIENT: internal error - no matching outstanding call!\n"); + } +} + + +uint32_t +IedConnection_getFileAsync(IedConnection self, IedClientError* error, const char* fileName, IedConnection_GetFileAsyncHandler handler, + void* parameter) +{ + MmsError err = MMS_ERROR_NONE; + + IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); + + if (call == NULL) { + *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; + return 0; + } + + call->callback = handler; + call->callbackParameter = parameter; + + call->invokeId = MmsConnection_fileOpenAsync(self->connection, &err, fileName, 0, mmsConnectionFileOpenHandler, self); + + *error = iedConnection_mapMmsErrorToIedError(err); + + if (err != MMS_ERROR_NONE) { + iedConnection_releaseOutstandingCall(self, call); + return 0; + } + + return call->invokeId; +} + void IedConnection_setFilestoreBasepath(IedConnection self, const char* basepath) { @@ -3019,7 +3175,7 @@ getDataSetHandlerInternal(uint32_t invokeId, void* parameter, MmsError err, MmsV IedConnection_ReadDataSetHandler handler = (IedConnection_ReadDataSetHandler) call->callback; ClientDataSet dataSet = (ClientDataSet) call->specificParameter; - char* dataSetReference = (char*) call->specificParameter2; + char* dataSetReference = (char*) call->specificParameter2.pointer; if (value != NULL) { @@ -3108,9 +3264,9 @@ IedConnection_readDataSetValuesAsync(IedConnection self, IedClientError* error, call->specificParameter = dataSet; if (dataSet == NULL) - call->specificParameter2 = StringUtils_copyString(dataSetReference); + call->specificParameter2.pointer = StringUtils_copyString(dataSetReference); else - call->specificParameter2 = NULL; + call->specificParameter2.pointer = NULL; MmsError err = MMS_ERROR_NONE; @@ -3126,7 +3282,7 @@ IedConnection_readDataSetValuesAsync(IedConnection self, IedClientError* error, if (err != MMS_ERROR_NONE) *error = iedConnection_mapMmsErrorToIedError(err); - GLOBAL_FREEMEM(call->specificParameter2); + GLOBAL_FREEMEM(call->specificParameter2.pointer); iedConnection_releaseOutstandingCall(self, call); diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h index 46de92a1..89d649f4 100644 --- a/src/iec61850/inc/iec61850_client.h +++ b/src/iec61850/inc/iec61850_client.h @@ -2658,6 +2658,48 @@ LIB61850_API uint32_t IedConnection_getFile(IedConnection self, IedClientError* error, const char* fileName, IedClientGetFileHandler handler, void* handlerParameter); + +/** + * \brief User provided handler to receive the data of the asynchronous GetFile request + * + * This handler will be invoked whenever the clients receives a data block from + * the server. The API user has to copy the data to another location before returning. + * The other location could for example be a file in the clients file system. When the + * last data block is received the moreFollows parameter will be set to false. + * + * \param invokeId invoke ID of the message containing the received data + * \param parameter user provided parameter passed to the callback + * \param err error code in case of an error or IED_ERROR_OK + * \param originalInvokeId the invoke ID of the original (first) request. This is usually the request to open the file. + * \param buffer the buffer that contains the received file data + * \param bytesRead the number of bytes read into the buffer + * \param moreFollows indicates that more file data is following + */ +typedef bool +(*IedConnection_GetFileAsyncHandler) (uint32_t invokeId, void* parameter, IedClientError err, uint32_t originalInvokeId, + uint8_t* buffer, uint32_t bytesRead, bool moreFollows); + + +/** + * \brief Implementation of the GetFile ACSI service - asynchronous version + * + * Download a file from the server. + * + * NOTE: This function can cause several request messages to be sent until the complete file is received + * or the file transfer is canceled. It allocates a background task and an outstanding call slot. + * + * \param self the connection object + * \param error the error code if an error occurs + * \param fileName the name of the file to be read from the server + * \param hander callback handler that is called for each received data or error message + * \param paramter user provided callback parameter + * + * \return invokeId of the first sent request + */ +LIB61850_API uint32_t +IedConnection_getFileAsync(IedConnection self, IedClientError* error, const char* fileName, IedConnection_GetFileAsyncHandler handler, + void* parameter); + /** * \brief Set the virtual filestore basepath for the setFile service * diff --git a/src/iec61850/inc_private/ied_connection_private.h b/src/iec61850/inc_private/ied_connection_private.h index be74e3e9..4998813b 100644 --- a/src/iec61850/inc_private/ied_connection_private.h +++ b/src/iec61850/inc_private/ied_connection_private.h @@ -38,7 +38,16 @@ struct sIedConnectionOutstandingCall { void* callback; void* callbackParameter; void* specificParameter; /* function/service specific parameter */ - void* specificParameter2; /* function/service specific parameter */ + + union { + void* pointer; + struct { + int32_t frsmId; + uint32_t originalInvokeId; + } getFileInfo; + } specificParameter2; /* function/service specific parameter */ + + //void* specificParameter2; /* function/service specific parameter */ }; struct sIedConnection