From e90d5d44fa7e86adb1cde31019bce9a76fa2382e Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 25 Oct 2018 21:48:43 +0200 Subject: [PATCH] - IEC 61850 client: added async discovery functions --- .../client_example_async.c | 64 +++++++++++++++ src/iec61850/client/ied_connection.c | 82 ++++++++++++++++++- src/iec61850/inc/iec61850_client.h | 40 ++++++++- src/mms/inc/mms_client_connection.h | 37 +++++++-- .../iso_mms/client/mms_client_connection.c | 8 +- 5 files changed, 216 insertions(+), 15 deletions(-) diff --git a/examples/iec61850_client_example_async/client_example_async.c b/examples/iec61850_client_example_async/client_example_async.c index a6b4cafe..044444e3 100644 --- a/examples/iec61850_client_example_async/client_example_async.c +++ b/examples/iec61850_client_example_async/client_example_async.c @@ -67,6 +67,61 @@ getVarSpecHandler (uint32_t invokeId, void* parameter, IedClientError err, MmsVa } } +static void +getNameListHandler(uint32_t invokeId, void* parameter, IedClientError err, LinkedList nameList, bool moreFollows) +{ + if (err != IED_ERROR_OK) { + printf("Get name list error: %d\n", err); + } + else { + + char* ldName = (char*) parameter; + + LinkedList element = LinkedList_getNext(nameList); + + while (element) { + + char* variableName = (char*) LinkedList_getData(element); + + printf(" %s/%s\n", ldName, variableName); + + element = LinkedList_getNext(element); + } + + LinkedList_destroy(nameList); + + free(ldName); + } +} + +static void +getServerDirectoryHandler(uint32_t invokeId, void* parameter, IedClientError err, LinkedList nameList, bool moreFollows) +{ + IedConnection con = (IedConnection) parameter; + + if (err != IED_ERROR_OK) { + printf("Get server directory error: %d\n", err); + } + else { + LinkedList element = LinkedList_getNext(nameList); + + while (element) { + + char* ldName = (char*) LinkedList_getData(element); + + printf("LD: %s\n", ldName); + + IedClientError cerr; + + IedConnection_getLogicalDeviceVariablesAsync(con, &cerr, ldName, NULL, NULL, getNameListHandler, strdup(ldName)); + + element = LinkedList_getNext(element); + } + + LinkedList_destroy(nameList); + } +} + int main(int argc, char** argv) { char* hostname; @@ -102,6 +157,15 @@ int main(int argc, char** argv) { if (success) { + IedConnection_getServerDirectoryAsync(con, &error, NULL, NULL, getServerDirectoryHandler, con); + + if (error != IED_ERROR_OK) { + printf("read server directory error %i\n", error); + } + + Thread_sleep(1000); + + IedConnection_readObjectAsync(con, &error, "simpleIOGenericIO/GGIO1.AnIn1.mag.f", IEC61850_FC_MX, readObjectHandler, "simpleIOGenericIO/GGIO1.AnIn1.mag.f"); if (error != IED_ERROR_OK) { diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index 88e9e691..0716238c 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -875,18 +875,92 @@ cleanup_and_exit: return invokeId; } +static void +getNameListHandler(uint32_t invokeId, void* parameter, MmsError mmsError, LinkedList nameList, bool moreFollows) +{ + IedConnection self = (IedConnection) parameter; + + IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); + + if (call) { + + IedConnection_GetNameListHandler handler = (IedConnection_GetNameListHandler) call->callback; + + handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(mmsError), nameList, moreFollows); + + iedConnection_releaseOutstandingCall(self, call); + } + else { + if (DEBUG_IED_CLIENT) + printf("IED_CLIENT: internal error - no matching outstanding call!\n"); + } +} + uint32_t -IedConnection_getServerDirectoryAsync(IedConnection self, IedClientError* error, const char* continueAfter, bool getFileNames, +IedConnection_getServerDirectoryAsync(IedConnection self, IedClientError* error, const char* continueAfter, LinkedList result, IedConnection_GetNameListHandler handler, void* parameter) { - //TODO implement me + IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); + + if (call == NULL) { + *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; + return 0; + } + + call->callback = handler; + call->callbackParameter = parameter; + + MmsError err = MMS_ERROR_NONE; + + call->invokeId = MmsConnection_getDomainNamesAsync(self->connection, &err, continueAfter, result, getNameListHandler, self); + + if (err != MMS_ERROR_NONE) { + *error = iedConnection_mapMmsErrorToIedError(err); + + iedConnection_releaseOutstandingCall(self, call); + + return 0; + } + else { + *error = IED_ERROR_OK; + } + + return call->invokeId; } + + + uint32_t -IedConnection_getLogicalDeviceVariables(IedConnection self, IedClientError* error, const char* continueAfter, +IedConnection_getLogicalDeviceVariablesAsync(IedConnection self, IedClientError* error, const char* ldName, const char* continueAfter, LinkedList result, IedConnection_GetNameListHandler handler, void* parameter) { - //TODO implement me + IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); + + if (call == NULL) { + *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; + return 0; + } + + call->callback = handler; + call->callbackParameter = parameter; + + MmsError err = MMS_ERROR_NONE; + + call->invokeId = MmsConnection_getDomainVariableNamesAsync(self->connection, &err, ldName, continueAfter, result, getNameListHandler, self); + + if (err != MMS_ERROR_NONE) { + *error = iedConnection_mapMmsErrorToIedError(err); + + iedConnection_releaseOutstandingCall(self, call); + + return 0; + } + else { + *error = IED_ERROR_OK; + } + + return call->invokeId; } static void diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h index d59f05d1..594d97ee 100644 --- a/src/iec61850/inc/iec61850_client.h +++ b/src/iec61850/inc/iec61850_client.h @@ -2018,18 +2018,54 @@ IedConnection_getVariableSpecification(IedConnection self, IedClientError* error typedef void (*IedConnection_GetNameListHandler) (uint32_t invokeId, void* parameter, IedClientError err, LinkedList nameList, bool moreFollows); +/** + * \brief Get the server directory (logical devices name) - asynchronous version + * + * \param[in] self the connection object + * \param[out] error the error code if an error occurs + * \param[in] continueAfter the name of the last received element when the call is a continuation, or NULL for the first call + * \param[in] result list to store (append) the response names, or NULL to create a new list for the response names + * \param[in] handler will be called when response is received or timed out. + * \param[in] parameter + * + * \return the invoke ID of the request + */ LIB61850_API uint32_t -IedConnection_getServerDirectoryAsync(IedConnection self, IedClientError* error, const char* continueAfter, bool getFileNames, +IedConnection_getServerDirectoryAsync(IedConnection self, IedClientError* error, const char* continueAfter, LinkedList result, IedConnection_GetNameListHandler handler, void* parameter); +/** + * \brief Get the variables in the logical device - asynchronous version + * + * \param[in] self the connection object + * \param[out] error the error code if an error occurs + * \param[in] ldName the logical device name + * \param[in] continueAfter the name of the last received element when the call is a continuation, or NULL for the first call + * \param[in] result list to store (append) the response names, or NULL to create a new list for the response names + * \param[in] handler will be called when response is received or timed out. + * \param[in] parameter + * + * \return the invoke ID of the request + */ LIB61850_API uint32_t -IedConnection_getLogicalDeviceVariables(IedConnection self, IedClientError* error, const char* continueAfter, +IedConnection_getLogicalDeviceVariablesAsync(IedConnection self, IedClientError* error, const char* ldName, const char* continueAfter, LinkedList result, IedConnection_GetNameListHandler handler, void* parameter); typedef void (*IedConnection_GetVariableSpecificationHandler) (uint32_t invokeId, void* parameter, IedClientError err, MmsVariableSpecification* spec); +/** + * \brief Get the specification of a variable (data attribute or functional constraint data object) - asynchronous version + * + * \param[in] self the connection object + * \param[out] error the error code if an error occurs + * \param[in] dataAttributeReference the data attribute reference (FCDA or FCDO) + * \param[in] handler will be called when response is received or timed out. + * \param[in] parameter + * + * \return the invoke ID of the request + */ LIB61850_API uint32_t IedConnection_getVariableSpecificationAsync(IedConnection self, IedClientError* error, const char* dataAttributeReference, FunctionalConstraint fc, IedConnection_GetVariableSpecificationHandler handler, void* parameter); diff --git a/src/mms/inc/mms_client_connection.h b/src/mms/inc/mms_client_connection.h index 02fd0774..e96eecbe 100644 --- a/src/mms/inc/mms_client_connection.h +++ b/src/mms/inc/mms_client_connection.h @@ -363,8 +363,20 @@ MmsConnection_getVMDVariableNamesAsync(MmsConnection self, MmsError* mmsError, c LIB61850_API LinkedList /* */ MmsConnection_getDomainNames(MmsConnection self, MmsError* mmsError); +/** + * \brief Get the domain names of the server (asynchronous version). + * + * \param[in] self MmsConnection instance to operate on + * \param[out] mmsError user provided variable to store error code + * \param[in] continueAfter the name of the last received element when the call is a continuation, or NULL for the first call + * \param[in] result list to store (append) the response names, or NULL to create a new list for the response names + * \param[in] handler will be called when response is received or timed out. + * \param[in] parameter + * + * \return the invoke ID of the request + */ LIB61850_API uint32_t -MmsConnection_getDomainNamesAsync(MmsConnection self, MmsError* mmsError, const char* continueAfter, +MmsConnection_getDomainNamesAsync(MmsConnection self, MmsError* mmsError, const char* continueAfter, LinkedList result, MmsConnection_GetNameListHandler handler, void* parameter); /** @@ -372,18 +384,33 @@ MmsConnection_getDomainNamesAsync(MmsConnection self, MmsError* mmsError, const * * This will result in a domain specific GetNameList request. * - * \param self MmsConnection instance to operate on - * \param mmsError user provided variable to store error code - * \param domainId the domain name for the domain specific request + * \param[in] self MmsConnection instance to operate on + * \param[out] mmsError user provided variable to store error code + * \param[in] domainId the domain name for the domain specific request * * \return the of domain specific variable names or NULL if the request failed. */ LIB61850_API LinkedList /* */ MmsConnection_getDomainVariableNames(MmsConnection self, MmsError* mmsError, const char* domainId); +/** + * \brief Get the names of all variables present in a MMS domain of the server (asynchronous version). + * + * This will result in a domain specific GetNameList request. + * + * \param[in] self MmsConnection instance to operate on + * \param[out] mmsError user provided variable to store error code + * \param[in] domainId the domain name for the domain specific request + * \param[in] continueAfter the name of the last received element when the call is a continuation, or NULL for the first call + * \param[in] result list to store (append) the response names, or NULL to create a new list for the response names + * \param[in] handler will be called when response is received or timed out. + * \param[in] parameter + * + * \return the invoke ID of the request + */ LIB61850_API uint32_t MmsConnection_getDomainVariableNamesAsync(MmsConnection self, MmsError* mmsError, const char* domainId, - const char* continueAfter, MmsConnection_GetNameListHandler handler, void* parameter); + const char* continueAfter, LinkedList result, MmsConnection_GetNameListHandler handler, void* parameter); /** * \brief Get the names of all named variable lists present in a MMS domain or VMD scope of the server. diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c index 14a54efe..c409267f 100644 --- a/src/mms/iso_mms/client/mms_client_connection.c +++ b/src/mms/iso_mms/client/mms_client_connection.c @@ -1964,11 +1964,11 @@ MmsConnection_getDomainNames(MmsConnection self, MmsError* mmsError) } uint32_t -MmsConnection_getDomainNamesAsync(MmsConnection self, MmsError* mmsError, const char* continueAfter, +MmsConnection_getDomainNamesAsync(MmsConnection self, MmsError* mmsError, const char* continueAfter, LinkedList result, MmsConnection_GetNameListHandler handler, void* parameter) { return mmsClient_getNameListSingleRequestAsync(self, mmsError, NULL, MMS_OBJECT_CLASS_DOMAIN, false, - continueAfter, handler, parameter, NULL); + continueAfter, handler, parameter, result); } LinkedList /* */ @@ -1979,10 +1979,10 @@ MmsConnection_getDomainVariableNames(MmsConnection self, MmsError* mmsError, con uint32_t MmsConnection_getDomainVariableNamesAsync(MmsConnection self, MmsError* mmsError, const char* domainId, - const char* continueAfter, MmsConnection_GetNameListHandler handler, void* parameter) + const char* continueAfter, LinkedList result, MmsConnection_GetNameListHandler handler, void* parameter) { return mmsClient_getNameListSingleRequestAsync(self, mmsError, domainId, MMS_OBJECT_CLASS_NAMED_VARIABLE, false, - continueAfter, handler, parameter, NULL); + continueAfter, handler, parameter, result); } LinkedList /* */