From 80ce9c896795e56f7c6f8f970f0978ae92ce1ea3 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 8 Sep 2018 10:33:19 +0200 Subject: [PATCH] - MMS client: added asynchronous read and write functions --- src/mms/inc/mms_client_connection.h | 119 +++- src/mms/inc_private/iso_client_connection.h | 12 +- src/mms/inc_private/mms_client_internal.h | 21 +- src/mms/iso_client/iso_client_connection.c | 8 + .../iso_mms/client/mms_client_connection.c | 525 ++++++++++++++++-- src/mms/iso_mms/client/mms_client_write.c | 6 +- 6 files changed, 625 insertions(+), 66 deletions(-) diff --git a/src/mms/inc/mms_client_connection.h b/src/mms/inc/mms_client_connection.h index f4d631b3..517111b2 100644 --- a/src/mms/inc/mms_client_connection.h +++ b/src/mms/inc/mms_client_connection.h @@ -373,6 +373,24 @@ MmsConnection_getVariableListNamesAssociationSpecific(MmsConnection self, MmsErr MmsValue* MmsConnection_readVariable(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId); + +typedef void +(*MmsConnection_ReadVariableHandler) (int invokeId, void* parameter, MmsError mmsError, MmsValue* value); + +/** + * \brief Read a single variable from the server (asynchronous version) + * + * \param self MmsConnection instance to operate on + * \param mmsError user provided variable to store error code + * \param domainId the domain name of the variable to be read or NULL to read a VMD specific named variable + * \param itemId name of the variable to be read + * + * \return invoke ID of the request when the request was sent successfully + */ +uint32_t +MmsConnection_readVariableAsync(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, + MmsConnection_ReadVariableHandler handler, void* parameter); + /** * \brief Read one or more elements of a single array variable from the server. * @@ -391,6 +409,26 @@ MmsValue* MmsConnection_readArrayElements(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, uint32_t startIndex, uint32_t numberOfElements); +/** + * \brief Read one or more elements of a single array variable from the server (asynchronous version) + * + * NOTE: The MmsValue object received by the callback function is either a simple or complex type if numberOfElements is 0, or an array + * containing the selected array elements of numberOfElements > 0. + * + * \param self MmsConnection instance to operate on + * \param mmsError user provided variable to store error code + * \param domainId the domain name of the variable to be read + * \param itemId name of the variable to be read + * \param startIndex index of element to read or start index if a element range is to be read + * \param numberOfElements Number of elements to read or 0 if a single element is to be read + * + * \return invoke ID of the request when the request was sent successfully + */ +uint32_t +MmsConnection_readArrayElementsAsync(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, + uint32_t startIndex, uint32_t numberOfElements, + MmsConnection_ReadVariableHandler handler, void* parameter); + /** * \brief Read a single element (with optional component specification) from the server @@ -408,6 +446,12 @@ MmsValue* MmsConnection_readSingleArrayElementWithComponent(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, uint32_t index, const char* componentId); +uint32_t +MmsConnection_readSingleArrayElementWithComponentAsync(MmsConnection self, MmsError* mmsError, + const char* domainId, const char* itemId, + uint32_t index, const char* componentId, + MmsConnection_ReadVariableHandler handler, void* parameter); + /** * \brief Read multiple variables of a domain from the server with one request message. * @@ -424,6 +468,11 @@ MmsValue* MmsConnection_readMultipleVariables(MmsConnection self, MmsError* mmsError, const char* domainId, LinkedList /**/ items); +uint32_t +MmsConnection_readMultipleVariablesAsync(MmsConnection self, MmsError* mmsError, + const char* domainId, LinkedList /**/items, + MmsConnection_ReadVariableHandler handler, void* parameter); + /** * \brief Write a single variable to the server. * @@ -441,6 +490,14 @@ MmsDataAccessError MmsConnection_writeVariable(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, MmsValue* value); +typedef void +(*MmsConnection_WriteVariableHandler) (int invokeId, void* parameter, MmsError mmsError, MmsDataAccessError accessError); + +uint32_t +MmsConnection_writeVariableAsync(MmsConnection self, MmsError* mmsError, + const char* domainId, const char* itemId, MmsValue* value, + MmsConnection_WriteVariableHandler handler, void* parameter); + /** * \brief Write a single array element or a sub array to an array type variable * @@ -464,6 +521,17 @@ MmsConnection_writeArrayElements(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, int index, int numberOfElements, MmsValue* value); +uint32_t +MmsConnection_writeArrayElementsAsync(MmsConnection self, MmsError* mmsError, + const char* domainId, const char* itemId, int index, int numberOfElements, + MmsValue* value, + MmsConnection_WriteVariableHandler handler, void* parameter); + + +typedef void +(*MmsConnection_WriteMultipleVariablesHandler) (int invokeId, void* parameter, MmsError mmsError, LinkedList /* */ accessResults); + + /** * \brief Write multiple variables to the server. * @@ -473,12 +541,12 @@ MmsConnection_writeArrayElements(MmsConnection self, MmsError* mmsError, * object that contains the AccessResults of the single variable write attempts. It is up to the user to free this * objects properly (e.g. with LinkedList_destroyDeep(accessResults, MmsValue_delete)). * - * \param self MmsConnection instance to operate on - * \param mmsError user provided variable to store error code - * \param domainId the common domain name of all variables to be written - * \param items a linked list containing the names of the variables to be written. The names are C strings. - * \param values values of the variables to be written - * \param (OUTPUT) the MmsValue objects of type MMS_DATA_ACCESS_ERROR representing the write success of a single variable + * \param[in] self MmsConnection instance to operate on + * \param[out] mmsError user provided variable to store error code + * \param[in] domainId the common domain name of all variables to be written + * \param[in] items a linked list containing the names of the variables to be written. The names are C strings. + * \param[out] values values of the variables to be written + * \param[out] the MmsValue objects of type MMS_DATA_ACCESS_ERROR representing the write success of a single variable * write. */ void @@ -486,6 +554,11 @@ MmsConnection_writeMultipleVariables(MmsConnection self, MmsError* mmsError, con LinkedList /**/ items, LinkedList /* */ values, LinkedList* /* */ accessResults); +uint32_t +MmsConnection_writeMultipleVariablesAsync(MmsConnection self, MmsError* mmsError, const char* domainId, + LinkedList /**/ items, LinkedList /* */ values, + MmsConnection_WriteMultipleVariablesHandler handler, void* parameter); + /** * \brief Write named variable list values to the server. * @@ -494,18 +567,24 @@ MmsConnection_writeMultipleVariables(MmsConnection self, MmsError* mmsError, con * the user to free this objects properly (e.g. with LinkedList_destroyDeep(accessResults, MmsValue_delete)). * If accessResult is the to NULL the result will not be stored. * - * \param self MmsConnection instance to operate on - * \param mmsError user provided variable to store error code - * \param isAssociationSpecifc true if the named variable list is an association specific named variable list - * \param domainId the common domain name of all variables to be written - * \param values values of the variables to be written - * \param (OUTPUT) the MmsValue objects of type MMS_DATA_ACCESS_ERROR representing the write success of a single variable + * \param[in] self MmsConnection instance to operate on + * \param[out] mmsError user provided variable to store error code + * \param[in] isAssociationSpecifc true if the named variable list is an association specific named variable list + * \param[in] domainId the common domain name of all variables to be written + * \param[out] values values of the variables to be written + * \param[out] the MmsValue objects of type MMS_DATA_ACCESS_ERROR representing the write success of a single variable * write. */ void MmsConnection_writeNamedVariableList(MmsConnection self, MmsError* mmsError, bool isAssociationSpecific, const char* domainId, const char* itemId, LinkedList /* */values, - /* OUTPUT */LinkedList* /* */accessResults); + LinkedList* /* */accessResults); + + +uint32_t +MmsConnection_writeNamedVariableListAsync(MmsConnection self, MmsError* mmsError, bool isAssociationSpecific, + const char* domainId, const char* itemId, LinkedList /* */values, + MmsConnection_WriteMultipleVariablesHandler handler, void* parameter); /** * \brief Get the variable access attributes of a MMS named variable of the server @@ -539,7 +618,12 @@ MmsConnection_getVariableAccessAttributes(MmsConnection self, MmsError* mmsError */ MmsValue* MmsConnection_readNamedVariableListValues(MmsConnection self, MmsError* mmsError, const char* domainId, - const char* listName, bool specWithResult); + const char* listName, bool specWithResult); + +uint32_t +MmsConnection_readNamedVariableListValuesAsync(MmsConnection self, MmsError* mmsError, + const char* domainId, const char* listName, bool specWithResult, + MmsConnection_ReadVariableHandler handler, void* parameter); /** @@ -556,7 +640,12 @@ MmsConnection_readNamedVariableListValues(MmsConnection self, MmsError* mmsError */ MmsValue* MmsConnection_readNamedVariableListValuesAssociationSpecific(MmsConnection self, MmsError* mmsError, - const char* listName, bool specWithResult); + const char* listName, bool specWithResult); + +uint32_t +MmsConnection_readNamedVariableListValuesAssociationSpecificAsync(MmsConnection self, MmsError* mmsError, + const char* listName, bool specWithResult, + MmsConnection_ReadVariableHandler handler, void* parameter); /** * \brief Define a new VMD or domain scoped named variable list at the server. diff --git a/src/mms/inc_private/iso_client_connection.h b/src/mms/inc_private/iso_client_connection.h index 2c6fae34..88ab924d 100644 --- a/src/mms/inc_private/iso_client_connection.h +++ b/src/mms/inc_private/iso_client_connection.h @@ -5,7 +5,7 @@ * protocol stack. It is used as an abstraction layer to isolate the MMS code from the lower * protocol layers. * - * Copyright 2013, 2014 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -40,7 +40,8 @@ typedef enum ISO_IND_ASSOCIATION_SUCCESS, ISO_IND_ASSOCIATION_FAILED, ISO_IND_CLOSED, - ISO_IND_DATA + ISO_IND_DATA, + ISO_IND_TICK } IsoIndication; typedef void* @@ -85,6 +86,13 @@ IsoClientConnection_close(IsoClientConnection self); ByteBuffer* IsoClientConnection_allocateTransmitBuffer(IsoClientConnection self); +/** + * This function is used to release the transmit buffer in case a formerly allocated transmit buffer cannot + * be sent. + */ +void +IsoClientConnection_releaseTransmitBuffer(IsoClientConnection self); + /* * The client should release the receive buffer in order for the IsoClientConnection to * reuse the buffer! If this function is not called then the reception of messages is diff --git a/src/mms/inc_private/mms_client_internal.h b/src/mms/inc_private/mms_client_internal.h index 9ffdb731..1415e736 100644 --- a/src/mms/inc_private/mms_client_internal.h +++ b/src/mms/inc_private/mms_client_internal.h @@ -61,6 +61,25 @@ typedef enum { #define CONCLUDE_STATE_REJECTED 2 #define CONCLUDE_STATE_ACCEPTED 3 +typedef enum { + MMS_CALL_TYPE_NONE, + MMS_CALL_TYPE_READ_VARIABLE, + MMS_CALL_TYPE_WRITE_VARIABLE, + MMS_CALL_TYPE_WRITE_MULTIPLE_VARIABLES +} eMmsOutstandingCallType; + +struct sMmsOutstandingCall +{ + bool isUsed; + uint32_t invokeId; + eMmsOutstandingCallType type; + void* userCallback; + void* userParameter; + uint64_t timeout; +}; + +typedef struct sMmsOutstandingCall* MmsOutstandingCall; + /* private instance variables */ struct sMmsConnection { Semaphore lastInvokeIdLock; @@ -73,7 +92,7 @@ struct sMmsConnection { volatile MmsError lastResponseError; Semaphore outstandingCallsLock; - uint32_t* outstandingCalls; + MmsOutstandingCall outstandingCalls; uint32_t requestTimeout; uint32_t connectTimeout; diff --git a/src/mms/iso_client/iso_client_connection.c b/src/mms/iso_client/iso_client_connection.c index 4e5a315a..1cb2d246 100644 --- a/src/mms/iso_client/iso_client_connection.c +++ b/src/mms/iso_client/iso_client_connection.c @@ -144,6 +144,8 @@ connectionHandlingThread(IsoClientConnection self) packetState = TPKT_ERROR; break; } + + self->callback(ISO_IND_TICK, self->callbackParameter, NULL); } if (packetState == TPKT_ERROR) @@ -655,6 +657,12 @@ IsoClientConnection_allocateTransmitBuffer(IsoClientConnection self) return self->transmitPayloadBuffer; } +void +IsoClientConnection_releaseTransmitBuffer(IsoClientConnection self) +{ + Semaphore_post(self->transmitBufferMutex); +} + void IsoClientConnection_releaseReceiveBuffer(IsoClientConnection self) { diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c index b6a05546..1a152e53 100644 --- a/src/mms/iso_mms/client/mms_client_connection.c +++ b/src/mms/iso_mms/client/mms_client_connection.c @@ -285,7 +285,7 @@ getNextInvokeId(MmsConnection self) return nextInvokeId; } -static bool +static MmsOutstandingCall checkForOutstandingCall(MmsConnection self, uint32_t invokeId) { int i = 0; @@ -293,27 +293,34 @@ checkForOutstandingCall(MmsConnection self, uint32_t invokeId) Semaphore_wait(self->outstandingCallsLock); for (i = 0; i < OUTSTANDING_CALLS; i++) { - if (self->outstandingCalls[i] == invokeId) { - Semaphore_post(self->outstandingCallsLock); - return true; + if (self->outstandingCalls[i].isUsed) { + if (self->outstandingCalls[i].invokeId == invokeId) { + Semaphore_post(self->outstandingCallsLock); + return &(self->outstandingCalls[i]); + } } } Semaphore_post(self->outstandingCallsLock); - return false; + return NULL; } static bool -addToOutstandingCalls(MmsConnection self, uint32_t invokeId) +addToOutstandingCalls(MmsConnection self, uint32_t invokeId, eMmsOutstandingCallType type, void* userCallback, void* userParameter) { int i = 0; Semaphore_wait(self->outstandingCallsLock); for (i = 0; i < OUTSTANDING_CALLS; i++) { - if (self->outstandingCalls[i] == 0) { - self->outstandingCalls[i] = invokeId; + if (self->outstandingCalls[i].isUsed == false) { + self->outstandingCalls[i].isUsed = true; + self->outstandingCalls[i].invokeId = invokeId; + self->outstandingCalls[i].timeout = Hal_getTimeInMs() + self->requestTimeout; + self->outstandingCalls[i].type = type; + self->outstandingCalls[i].userCallback = userCallback; + self->outstandingCalls[i].userParameter = userParameter; Semaphore_post(self->outstandingCallsLock); return true; } @@ -332,18 +339,55 @@ removeFromOutstandingCalls(MmsConnection self, uint32_t invokeId) Semaphore_wait(self->outstandingCallsLock); for (i = 0; i < OUTSTANDING_CALLS; i++) { - if (self->outstandingCalls[i] == invokeId) { - self->outstandingCalls[i] = 0; - break; + if (self->outstandingCalls[i].isUsed) { + if (self->outstandingCalls[i].invokeId == invokeId) { + self->outstandingCalls[i].isUsed = false; + break; + } } } Semaphore_post(self->outstandingCallsLock); } +static void +sendMessage(MmsConnection self, ByteBuffer* message) +{ +#if (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) + if (self->rawMmsMessageHandler != NULL) { + MmsRawMessageHandler handler = (MmsRawMessageHandler) self->rawMmsMessageHandler; + handler(self->rawMmsMessageHandlerParameter, message->buffer, message->size, false); + } +#endif /* (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) */ + + IsoClientConnection_sendMessage(self->isoClient, message); +} + +static MmsError +sendAsyncRequest(MmsConnection self, uint32_t invokeId, ByteBuffer* message, eMmsOutstandingCallType type, + void* userCallback, void* userParameter) +{ + if (addToOutstandingCalls(self, invokeId, type, userCallback, userParameter) == false) { + + /* message cannot be sent - release resources */ + IsoClientConnection_releaseTransmitBuffer(self->isoClient); + + return MMS_ERROR_OUTSTANDING_CALL_LIMIT; + } + + sendMessage(self, message); + + return MMS_ERROR_NONE; +} + static ByteBuffer* sendRequestAndWaitForResponse(MmsConnection self, uint32_t invokeId, ByteBuffer* message, MmsError* mmsError) { + if (addToOutstandingCalls(self, invokeId, MMS_CALL_TYPE_NONE, NULL, NULL) == false) { + *mmsError = MMS_ERROR_OUTSTANDING_CALL_LIMIT; + return NULL; + } + ByteBuffer* receivedMessage = NULL; uint64_t currentTime = Hal_getTimeInMs(); @@ -352,21 +396,9 @@ sendRequestAndWaitForResponse(MmsConnection self, uint32_t invokeId, ByteBuffer* bool success = false; - if (addToOutstandingCalls(self, invokeId) == false) { - *mmsError = MMS_ERROR_OUTSTANDING_CALL_LIMIT; - return NULL; - } - *mmsError = MMS_ERROR_NONE; -#if (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) - if (self->rawMmsMessageHandler != NULL) { - MmsRawMessageHandler handler = (MmsRawMessageHandler) self->rawMmsMessageHandler; - handler(self->rawMmsMessageHandlerParameter, message->buffer, message->size, false); - } -#endif /* (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) */ - - IsoClientConnection_sendMessage(self->isoClient, message); + sendMessage(self, message); while (currentTime < waitUntilTime) { uint32_t receivedInvokeId; @@ -742,6 +774,67 @@ mmsMsg_parseRejectPDU(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t* invo return -1; } +static void +handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, MmsOutstandingCall outstandingCall, MmsError err) +{ + + if (outstandingCall->type == MMS_CALL_TYPE_READ_VARIABLE) { + + MmsConnection_ReadVariableHandler handler = + (MmsConnection_ReadVariableHandler) outstandingCall->userCallback; + + if (err != MMS_ERROR_NONE) + handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL); + else { + if (response) { + MmsValue* value = mmsClient_parseReadResponse(response, NULL, false); + + handler(outstandingCall->invokeId, outstandingCall->userParameter, MMS_ERROR_NONE, value); + } + + } + } + else if (outstandingCall->type == MMS_CALL_TYPE_WRITE_VARIABLE) { + + MmsConnection_WriteVariableHandler handler = + (MmsConnection_WriteVariableHandler) outstandingCall->userCallback; + + if (err != MMS_ERROR_NONE) { + handler(outstandingCall->invokeId, outstandingCall->userParameter, err, DATA_ACCESS_ERROR_NO_RESPONSE); + } + else { + if (response) { + MmsDataAccessError daError = mmsClient_parseWriteResponse(response, bufPos, &err); + + handler(outstandingCall->invokeId, outstandingCall->userParameter, err, daError); + } + } + + } + else if (outstandingCall->type == MMS_CALL_TYPE_WRITE_MULTIPLE_VARIABLES) { + + MmsConnection_WriteMultipleVariablesHandler handler = + (MmsConnection_WriteMultipleVariablesHandler) outstandingCall->userCallback; + + if (err != MMS_ERROR_NONE) { + handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL); + } + else { + if (response) { + LinkedList accessResults = NULL; + + mmsClient_parseWriteMultipleItemsResponse(response, bufPos, &err, -1, &accessResults); + + handler(outstandingCall->invokeId, outstandingCall->userParameter, err, accessResults); + } + } + } + + removeFromOutstandingCalls(self, outstandingCall->invokeId); + + releaseResponse(self); +} + static void mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) { @@ -750,6 +843,33 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: mmsIsoCallback called with indication %i\n", indication); + if (indication == ISO_IND_TICK) { + //TODO check timeouts + + uint64_t currentTime = Hal_getTimeInMs(); + + int i = 0; + + Semaphore_wait(self->outstandingCallsLock); + + for (i = 0; i < OUTSTANDING_CALLS; i++) { + if (self->outstandingCalls[i].isUsed) { + if (currentTime > self->outstandingCalls[i].timeout) { + + if (self->outstandingCalls[i].type != MMS_CALL_TYPE_NONE) + handleAsyncResponse(self, NULL, 0, &(self->outstandingCalls[i]), MMS_ERROR_SERVICE_TIMEOUT); + + self->outstandingCalls[i].isUsed = false; + break; + } + } + } + + Semaphore_post(self->outstandingCallsLock); + + return; + } + if (indication == ISO_IND_CLOSED) { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: mmsIsoCallback: Connection lost or closed by client!\n"); @@ -849,15 +969,25 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) goto exit_with_error; } else { - if (checkForOutstandingCall(self, invokeId)) { - /* wait for application thread to handle last received response */ - waitUntilLastResponseHasBeenProcessed(self); + MmsOutstandingCall call = checkForOutstandingCall(self, invokeId); + + if (call) { - Semaphore_wait(self->lastResponseLock); - self->lastResponseError = convertServiceErrorToMmsError(serviceError); - self->responseInvokeId = invokeId; - Semaphore_post(self->lastResponseLock); + MmsError err = convertServiceErrorToMmsError(serviceError); + + if (call->type != MMS_CALL_TYPE_NONE) { + handleAsyncResponse(self, NULL, 0, call, err); + } + else { + /* wait for application thread to handle last received response */ + waitUntilLastResponseHasBeenProcessed(self); + + Semaphore_wait(self->lastResponseLock); + self->lastResponseError = err; + self->responseInvokeId = invokeId; + Semaphore_post(self->lastResponseLock); + } } else { if (DEBUG_MMS_CLIENT) @@ -880,15 +1010,24 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: reject PDU invokeID: %i type: %i reason: %i\n", (int) invokeId, rejectType, rejectReason); - if (checkForOutstandingCall(self, invokeId)) { + MmsOutstandingCall call = checkForOutstandingCall(self, invokeId); + + if (call) { - /* wait for application thread to handle last received response */ - waitUntilLastResponseHasBeenProcessed(self); + MmsError err = convertRejectCodesToMmsError(rejectType, rejectReason); - Semaphore_wait(self->lastResponseLock); - self->lastResponseError = convertRejectCodesToMmsError(rejectType, rejectReason); - self->responseInvokeId = invokeId; - Semaphore_post(self->lastResponseLock); + if (call->type != MMS_CALL_TYPE_NONE) { + handleAsyncResponse(self, NULL, 0, call, err); + } + else { + /* wait for application thread to handle last received response */ + waitUntilLastResponseHasBeenProcessed(self); + + Semaphore_wait(self->lastResponseLock); + self->lastResponseError = err; + self->responseInvokeId = invokeId; + Semaphore_post(self->lastResponseLock); + } } else { IsoClientConnection_releaseReceiveBuffer(self->isoClient); @@ -923,15 +1062,22 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) bufPos += invokeIdLength; - if (checkForOutstandingCall(self, invokeId)) { + MmsOutstandingCall call = checkForOutstandingCall(self, invokeId); - waitUntilLastResponseHasBeenProcessed(self); + if (call) { - Semaphore_wait(self->lastResponseLock); - self->lastResponse = payload; - self->lastResponseBufPos = bufPos; - self->responseInvokeId = invokeId; - Semaphore_post(self->lastResponseLock); + if (call->type != MMS_CALL_TYPE_NONE) { + handleAsyncResponse(self, payload, bufPos, call, MMS_ERROR_NONE); + } + else { + waitUntilLastResponseHasBeenProcessed(self); + + Semaphore_wait(self->lastResponseLock); + self->lastResponse = payload; + self->lastResponseBufPos = bufPos; + self->responseInvokeId = invokeId; + Semaphore_post(self->lastResponseLock); + } } else { if (DEBUG_MMS_CLIENT) @@ -1106,7 +1252,7 @@ MmsConnection_create() self->lastResponseError = MMS_ERROR_NONE; - self->outstandingCalls = (uint32_t*) GLOBAL_CALLOC(OUTSTANDING_CALLS, sizeof(uint32_t)); + self->outstandingCalls = (MmsOutstandingCall) GLOBAL_CALLOC(OUTSTANDING_CALLS, sizeof(struct sMmsOutstandingCall)); self->isoParameters = IsoConnectionParameters_create(); @@ -1589,6 +1735,33 @@ MmsConnection_readVariable(MmsConnection self, MmsError* mmsError, return value; } +uint32_t +MmsConnection_readVariableAsync(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, + MmsConnection_ReadVariableHandler handler, void* parameter) +{ + uint32_t invokeId = 0; + + if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (mmsError) + *mmsError = MMS_ERROR_CONNECTION_LOST; + goto exit_function; + } + + ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); + + invokeId = getNextInvokeId(self); + + mmsClient_createReadRequest(invokeId, domainId, itemId, payload); + + MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_READ_VARIABLE, handler, parameter); + + if (mmsError) + *mmsError = err; + +exit_function: + return invokeId; +} + MmsValue* MmsConnection_readArrayElements(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, @@ -1619,6 +1792,35 @@ exit_function: return value; } +uint32_t +MmsConnection_readArrayElementsAsync(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, + uint32_t startIndex, uint32_t numberOfElements, + MmsConnection_ReadVariableHandler handler, void* parameter) +{ + uint32_t invokeId = 0; + + if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (mmsError) + *mmsError = MMS_ERROR_CONNECTION_LOST; + goto exit_function; + } + + ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); + + invokeId = getNextInvokeId(self); + + mmsClient_createReadRequestAlternateAccessIndex(invokeId, domainId, itemId, startIndex, + numberOfElements, payload); + + MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_READ_VARIABLE, handler, parameter); + + if (mmsError) + *mmsError = err; + +exit_function: + return invokeId; +} + MmsValue* MmsConnection_readSingleArrayElementWithComponent(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, uint32_t index, const char* componentId) @@ -1648,6 +1850,36 @@ exit_function: return value; } +uint32_t +MmsConnection_readSingleArrayElementWithComponentAsync(MmsConnection self, MmsError* mmsError, + const char* domainId, const char* itemId, + uint32_t index, const char* componentId, + MmsConnection_ReadVariableHandler handler, void* parameter) +{ + uint32_t invokeId = 0; + + if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (mmsError) + *mmsError = MMS_ERROR_CONNECTION_LOST; + goto exit_function; + } + + ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); + + invokeId = getNextInvokeId(self); + + mmsClient_createReadRequestAlternateAccessSingleIndexComponent(invokeId, domainId, itemId, index, componentId, + payload); + + MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_READ_VARIABLE, handler, parameter); + + if (mmsError) + *mmsError = err; + +exit_function: + return invokeId; +} + MmsValue* MmsConnection_readMultipleVariables(MmsConnection self, MmsError* mmsError, const char* domainId, LinkedList /**/items) @@ -1676,6 +1908,35 @@ MmsConnection_readMultipleVariables(MmsConnection self, MmsError* mmsError, return value; } +uint32_t +MmsConnection_readMultipleVariablesAsync(MmsConnection self, MmsError* mmsError, + const char* domainId, LinkedList /**/items, + MmsConnection_ReadVariableHandler handler, void* parameter) +{ + uint32_t invokeId = 0; + + if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (mmsError) + *mmsError = MMS_ERROR_CONNECTION_LOST; + goto exit_function; + } + + ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); + + invokeId = getNextInvokeId(self); + + mmsClient_createReadRequestMultipleValues(invokeId, domainId, items, payload); + + MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_READ_VARIABLE, handler, parameter); + + if (mmsError) + *mmsError = err; + +exit_function: + return invokeId; +} + + MmsValue* MmsConnection_readNamedVariableListValues(MmsConnection self, MmsError* mmsError, const char* domainId, const char* listName, @@ -1706,6 +1967,36 @@ MmsConnection_readNamedVariableListValues(MmsConnection self, MmsError* mmsError return value; } +uint32_t +MmsConnection_readNamedVariableListValuesAsync(MmsConnection self, MmsError* mmsError, + const char* domainId, const char* listName, bool specWithResult, + MmsConnection_ReadVariableHandler handler, void* parameter) +{ + uint32_t invokeId = 0; + + if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (mmsError) + *mmsError = MMS_ERROR_CONNECTION_LOST; + goto exit_function; + } + + ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); + + invokeId = getNextInvokeId(self); + + mmsClient_createReadNamedVariableListRequest(invokeId, domainId, listName, + payload, specWithResult); + + MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_READ_VARIABLE, handler, parameter); + + if (mmsError) + *mmsError = err; + +exit_function: + return invokeId; +} + + MmsValue* MmsConnection_readNamedVariableListValuesAssociationSpecific( MmsConnection self, MmsError* mmsError, @@ -1737,6 +2028,35 @@ MmsConnection_readNamedVariableListValuesAssociationSpecific( return value; } +uint32_t +MmsConnection_readNamedVariableListValuesAssociationSpecificAsync(MmsConnection self, MmsError* mmsError, + const char* listName, bool specWithResult, + MmsConnection_ReadVariableHandler handler, void* parameter) +{ + uint32_t invokeId = 0; + + if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (mmsError) + *mmsError = MMS_ERROR_CONNECTION_LOST; + goto exit_function; + } + + ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); + + invokeId = getNextInvokeId(self); + + mmsClient_createReadAssociationSpecificNamedVariableListRequest(invokeId, listName, + payload, specWithResult); + + MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_READ_VARIABLE, handler, parameter); + + if (mmsError) + *mmsError = err; + +exit_function: + return invokeId; +} + LinkedList /* */ MmsConnection_readNamedVariableListDirectory(MmsConnection self, MmsError* mmsError, const char* domainId, const char* listName, bool* deletable) @@ -2317,6 +2637,34 @@ MmsConnection_writeVariable(MmsConnection self, MmsError* mmsError, return retVal; } +uint32_t +MmsConnection_writeVariableAsync(MmsConnection self, MmsError* mmsError, + const char* domainId, const char* itemId, MmsValue* value, + MmsConnection_WriteVariableHandler handler, void* parameter) +{ + uint32_t invokeId = 0; + + if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (mmsError) + *mmsError = MMS_ERROR_CONNECTION_LOST; + goto exit_function; + } + + ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); + + invokeId = getNextInvokeId(self); + + mmsClient_createWriteRequest(invokeId, domainId, itemId, value, payload); + + MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_WRITE_VARIABLE, handler, parameter); + + if (mmsError) + *mmsError = err; + +exit_function: + return invokeId; +} + void MmsConnection_writeMultipleVariables(MmsConnection self, MmsError* mmsError, const char* domainId, LinkedList /**/items, @@ -2342,6 +2690,34 @@ MmsConnection_writeMultipleVariables(MmsConnection self, MmsError* mmsError, con releaseResponse(self); } +uint32_t +MmsConnection_writeMultipleVariablesAsync(MmsConnection self, MmsError* mmsError, const char* domainId, + LinkedList /**/ items, LinkedList /* */ values, + MmsConnection_WriteMultipleVariablesHandler handler, void* parameter) +{ + uint32_t invokeId = 0; + + if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (mmsError) + *mmsError = MMS_ERROR_CONNECTION_LOST; + goto exit_function; + } + + ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); + + invokeId = getNextInvokeId(self); + + mmsClient_createWriteMultipleItemsRequest(invokeId, domainId, items, values, payload); + + MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_WRITE_MULTIPLE_VARIABLES, handler, parameter); + + if (mmsError) + *mmsError = err; + +exit_function: + return invokeId; +} + MmsDataAccessError MmsConnection_writeArrayElements(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, int index, int numberOfElements, @@ -2365,6 +2741,35 @@ MmsConnection_writeArrayElements(MmsConnection self, MmsError* mmsError, return retVal; } +uint32_t +MmsConnection_writeArrayElementsAsync(MmsConnection self, MmsError* mmsError, + const char* domainId, const char* itemId, int index, int numberOfElements, + MmsValue* value, + MmsConnection_WriteVariableHandler handler, void* parameter) +{ + uint32_t invokeId = 0; + + if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (mmsError) + *mmsError = MMS_ERROR_CONNECTION_LOST; + goto exit_function; + } + + ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); + + invokeId = getNextInvokeId(self); + + mmsClient_createWriteRequestArray(invokeId, domainId, itemId, index, numberOfElements, value, payload); + + MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_WRITE_VARIABLE, handler, parameter); + + if (mmsError) + *mmsError = err; + +exit_function: + return invokeId; +} + void MmsConnection_writeNamedVariableList(MmsConnection self, MmsError* mmsError, bool isAssociationSpecific, const char* domainId, const char* itemId, LinkedList /* */values, @@ -2389,6 +2794,34 @@ MmsConnection_writeNamedVariableList(MmsConnection self, MmsError* mmsError, boo releaseResponse(self); } +uint32_t +MmsConnection_writeNamedVariableListAsync(MmsConnection self, MmsError* mmsError, bool isAssociationSpecific, + const char* domainId, const char* itemId, LinkedList /* */values, + MmsConnection_WriteMultipleVariablesHandler handler, void* parameter) +{ + uint32_t invokeId = 0; + + if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (mmsError) + *mmsError = MMS_ERROR_CONNECTION_LOST; + goto exit_function; + } + + ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); + + invokeId = getNextInvokeId(self); + + mmsClient_createWriteRequestNamedVariableList(invokeId, isAssociationSpecific, domainId, itemId, values, payload); + + MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_WRITE_MULTIPLE_VARIABLES, handler, parameter); + + if (mmsError) + *mmsError = err; + +exit_function: + return invokeId; +} + void MmsServerIdentity_destroy(MmsServerIdentity* self) { diff --git a/src/mms/iso_mms/client/mms_client_write.c b/src/mms/iso_mms/client/mms_client_write.c index 3a6ecbb4..e60fd4b1 100644 --- a/src/mms/iso_mms/client/mms_client_write.c +++ b/src/mms/iso_mms/client/mms_client_write.c @@ -119,8 +119,10 @@ mmsClient_parseWriteMultipleItemsResponse(ByteBuffer* message, int32_t bufPos, M numberOfAccessResults++; } - if (itemCount != numberOfAccessResults) - goto exit_with_error; + if (itemCount != -1) { + if (itemCount != numberOfAccessResults) + goto exit_with_error; + } } else *mmsError = MMS_ERROR_PARSING_RESPONSE;