tutorial_server_method_async.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
  2. * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
  3. /**
  4. * Adding Async Methods to Objects
  5. * -------------------------
  6. *
  7. * An object in an OPC UA information model may contain methods similar to
  8. * objects in a programming language. Methods are represented by a MethodNode.
  9. * Note that several objects may reference the same MethodNode. When an object
  10. * type is instantiated, a reference to the method is added instead of copying
  11. * the MethodNode. Therefore, the identifier of the context object is always
  12. * explicitly stated when a method is called.
  13. *
  14. * The method callback takes as input a custom data pointer attached to the
  15. * method node, the identifier of the object from which the method is called,
  16. * and two arrays for the input and output arguments. The input and output
  17. * arguments are all of type :ref:`variant`. Each variant may in turn contain a
  18. * (multi-dimensional) array or scalar of any data type.
  19. *
  20. * Constraints for the method arguments are defined in terms of data type, value
  21. * rank and array dimension (similar to variable definitions). The argument
  22. * definitions are stored in child VariableNodes of the MethodNode with the
  23. * respective BrowseNames ``(0, "InputArguments")`` and ``(0,
  24. * "OutputArguments")``.
  25. *
  26. * Example: Hello World Method
  27. * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  28. * The method takes a string scalar and returns a string scalar with "Hello "
  29. * prepended. The type and length of the input arguments is checked internally
  30. * by the SDK, so that we don't have to verify the arguments in the callback. */
  31. #include <open62541/client_config_default.h>
  32. #include <open62541/plugin/log_stdout.h>
  33. #include <open62541/server.h>
  34. #include <open62541/server_config_default.h>
  35. #include <signal.h>
  36. #include <stdlib.h>
  37. #ifndef WIN32
  38. #include <pthread.h>
  39. #define THREAD_HANDLE pthread_t
  40. #define THREAD_CREATE(handle, callback) pthread_create(&handle, NULL, callback, NULL)
  41. #define THREAD_JOIN(handle) pthread_join(handle, NULL)
  42. #define THREAD_CALLBACK(name) static void * name(void *_)
  43. #else
  44. #include <windows.h>
  45. #define THREAD_HANDLE HANDLE
  46. #define THREAD_CREATE(handle, callback) { handle = CreateThread( NULL, 0, callback, NULL, 0, NULL); }
  47. #define THREAD_JOIN(handle) WaitForSingleObject(handle, INFINITE)
  48. #define THREAD_CALLBACK(name) static DWORD WINAPI name( LPVOID lpParam )
  49. #endif
  50. static UA_Server* globalServer;
  51. static volatile UA_Boolean running = true;
  52. static void stopHandler(int sign) {
  53. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
  54. running = false;
  55. }
  56. static UA_StatusCode
  57. helloWorldMethodCallback1(UA_Server *server,
  58. const UA_NodeId *sessionId, void *sessionHandle,
  59. const UA_NodeId *methodId, void *methodContext,
  60. const UA_NodeId *objectId, void *objectContext,
  61. size_t inputSize, const UA_Variant *input,
  62. size_t outputSize, UA_Variant *output) {
  63. UA_String *inputStr = (UA_String*)input->data;
  64. UA_String tmp = UA_STRING_ALLOC("Hello ");
  65. if(inputStr->length > 0) {
  66. tmp.data = (UA_Byte *)UA_realloc(tmp.data, tmp.length + inputStr->length);
  67. memcpy(&tmp.data[tmp.length], inputStr->data, inputStr->length);
  68. tmp.length += inputStr->length;
  69. }
  70. UA_Variant_setScalarCopy(output, &tmp, &UA_TYPES[UA_TYPES_STRING]);
  71. char* test = (char*)calloc(1,tmp.length+1);
  72. memcpy(test, tmp.data, tmp.length);
  73. UA_String_clear(&tmp);
  74. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "'Hello World 1 (async)' was called and will take 3 seconds");
  75. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, " Data 1: %s", test);
  76. free(test);
  77. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "'Hello World 1 (async)' has ended");
  78. return UA_STATUSCODE_GOOD;
  79. }
  80. static void
  81. addHellWorldMethod1(UA_Server *server) {
  82. UA_Argument inputArgument;
  83. UA_Argument_init(&inputArgument);
  84. inputArgument.description = UA_LOCALIZEDTEXT("en-US", "A String");
  85. inputArgument.name = UA_STRING("MyInput");
  86. inputArgument.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
  87. inputArgument.valueRank = UA_VALUERANK_SCALAR;
  88. UA_Argument outputArgument;
  89. UA_Argument_init(&outputArgument);
  90. outputArgument.description = UA_LOCALIZEDTEXT("en-US", "A String");
  91. outputArgument.name = UA_STRING("MyOutput");
  92. outputArgument.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
  93. outputArgument.valueRank = UA_VALUERANK_SCALAR;
  94. UA_MethodAttributes helloAttr = UA_MethodAttributes_default;
  95. helloAttr.description = UA_LOCALIZEDTEXT("en-US","Say `Hello World` async");
  96. helloAttr.displayName = UA_LOCALIZEDTEXT("en-US","Hello World async");
  97. helloAttr.executable = true;
  98. helloAttr.userExecutable = true;
  99. UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1,62541),
  100. UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
  101. UA_NODEID_NUMERIC(0, UA_NS0ID_HASORDEREDCOMPONENT),
  102. UA_QUALIFIEDNAME(1, "hello world"),
  103. helloAttr, &helloWorldMethodCallback1,
  104. 1, &inputArgument, 1, &outputArgument, NULL, NULL);
  105. /* Get the method node */
  106. UA_NodeId id = UA_NODEID_NUMERIC(1, 62541);
  107. UA_Server_setMethodNodeAsync(server, id, UA_TRUE);
  108. }
  109. static UA_StatusCode
  110. helloWorldMethodCallback2(UA_Server *server,
  111. const UA_NodeId *sessionId, void *sessionHandle,
  112. const UA_NodeId *methodId, void *methodContext,
  113. const UA_NodeId *objectId, void *objectContext,
  114. size_t inputSize, const UA_Variant *input,
  115. size_t outputSize, UA_Variant *output) {
  116. UA_String *inputStr = (UA_String*)input->data;
  117. UA_String tmp = UA_STRING_ALLOC("Hello ");
  118. if (inputStr->length > 0) {
  119. tmp.data = (UA_Byte *)UA_realloc(tmp.data, tmp.length + inputStr->length);
  120. memcpy(&tmp.data[tmp.length], inputStr->data, inputStr->length);
  121. tmp.length += inputStr->length;
  122. }
  123. UA_Variant_setScalarCopy(output, &tmp, &UA_TYPES[UA_TYPES_STRING]);
  124. char* test = (char*)calloc(1, tmp.length + 1);
  125. memcpy(test, tmp.data, tmp.length);
  126. UA_String_clear(&tmp);
  127. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "'Hello World 2 (async)' was called and will take 1 seconds");
  128. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, " Data 2: %s", test);
  129. free(test);
  130. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "'Hello World 2 (async)' has ended");
  131. return UA_STATUSCODE_GOOD;
  132. }
  133. static void
  134. addHellWorldMethod2(UA_Server *server) {
  135. UA_Argument inputArgument;
  136. UA_Argument_init(&inputArgument);
  137. inputArgument.description = UA_LOCALIZEDTEXT("en-US", "A String");
  138. inputArgument.name = UA_STRING("MyInput");
  139. inputArgument.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
  140. inputArgument.valueRank = UA_VALUERANK_SCALAR;
  141. UA_Argument outputArgument;
  142. UA_Argument_init(&outputArgument);
  143. outputArgument.description = UA_LOCALIZEDTEXT("en-US", "A String");
  144. outputArgument.name = UA_STRING("MyOutput");
  145. outputArgument.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
  146. outputArgument.valueRank = UA_VALUERANK_SCALAR;
  147. UA_MethodAttributes helloAttr = UA_MethodAttributes_default;
  148. helloAttr.description = UA_LOCALIZEDTEXT("en-US", "Say `Hello World` sync");
  149. helloAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Hello World sync");
  150. helloAttr.executable = true;
  151. helloAttr.userExecutable = true;
  152. UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1, 62542),
  153. UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
  154. UA_NODEID_NUMERIC(0, UA_NS0ID_HASORDEREDCOMPONENT),
  155. UA_QUALIFIEDNAME(1, "hello world 2"),
  156. helloAttr, &helloWorldMethodCallback2,
  157. 1, &inputArgument, 1, &outputArgument, NULL, NULL);
  158. /* Get the method node */
  159. UA_NodeId id = UA_NODEID_NUMERIC(1, 62542);
  160. UA_Server_setMethodNodeAsync(server, id, UA_TRUE);
  161. }
  162. THREAD_CALLBACK(ThreadWorker) {
  163. while(running) {
  164. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
  165. "Try to dequeue an async operation");
  166. const UA_AsyncOperationRequest* request = NULL;
  167. void *context = NULL;
  168. UA_AsyncOperationType type;
  169. if(UA_Server_getAsyncOperationNonBlocking(globalServer, &type, &request, &context, NULL) == true) {
  170. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "AsyncMethod_Testing: Got entry: OKAY");
  171. UA_CallMethodResult response = UA_Server_call(globalServer, &request->callMethodRequest);
  172. UA_Server_setAsyncOperationResult(globalServer, (UA_AsyncOperationResponse*)&response,
  173. context);
  174. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "AsyncMethod_Testing: Call done: OKAY");
  175. UA_CallMethodResult_clear(&response);
  176. } else {
  177. /* not a good style, but done for simplicity :-) */
  178. Sleep(5000);
  179. }
  180. }
  181. return 0;
  182. }
  183. /* This callback will be called when a new entry is added to the Callrequest queue */
  184. static void
  185. TestCallback(UA_Server *server) {
  186. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
  187. "Dispatched an async method");
  188. }
  189. int main(void) {
  190. signal(SIGINT, stopHandler);
  191. signal(SIGTERM, stopHandler);
  192. globalServer = UA_Server_new();
  193. UA_ServerConfig *config = UA_Server_getConfig(globalServer);
  194. UA_ServerConfig_setDefault(config);
  195. /* Set the NotifyCallback */
  196. config->asyncOperationNotifyCallback = TestCallback;
  197. /* Start the Worker-Thread */
  198. THREAD_HANDLE hThread;
  199. THREAD_CREATE(hThread, ThreadWorker);
  200. /* Add methods */
  201. addHellWorldMethod1(globalServer);
  202. addHellWorldMethod2(globalServer);
  203. UA_StatusCode retval = UA_Server_run(globalServer, &running);
  204. /* Shutdown the thread */
  205. THREAD_JOIN(hThread);
  206. UA_Server_delete(globalServer);
  207. return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
  208. }