forked from open62541/open62541
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtutorial_server_method_async.c
More file actions
153 lines (128 loc) · 6.07 KB
/
tutorial_server_method_async.c
File metadata and controls
153 lines (128 loc) · 6.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
* See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
/**
* Adding Async Methods to Objects
* -------------------------
*
* An object in an OPC UA information model may contain methods similar to
* objects in a programming language. Methods are represented by a MethodNode.
* Note that several objects may reference the same MethodNode. When an object
* type is instantiated, a reference to the method is added instead of copying
* the MethodNode. Therefore, the identifier of the context object is always
* explicitly stated when a method is called.
*
* The method callback takes as input a custom data pointer attached to the
* method node, the identifier of the object from which the method is called,
* and two arrays for the input and output arguments. The input and output
* arguments are all of type :ref:`variant`. Each variant may in turn contain a
* (multi-dimensional) array or scalar of any data type.
*
* Constraints for the method arguments are defined in terms of data type, value
* rank and array dimension (similar to variable definitions). The argument
* definitions are stored in child VariableNodes of the MethodNode with the
* respective BrowseNames ``(0, "InputArguments")`` and ``(0,
* "OutputArguments")``.
*
* Example: Hello World Method
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^
* The method takes a string scalar and returns a string scalar with "Hello "
* prepended. The type and length of the input arguments is checked internally
* by the SDK, so that we don't have to verify the arguments in the callback. */
#include <open62541/client_config_default.h>
#include <open62541/server.h>
#include <open62541/plugin/log.h>
/* The example mocks up a work queue for worker threads with a single slot. The
* asyncLock is introduced to show how locking can be used for independent
* worker threads. Notably the method result pointer must not be used after the
* asyncOperationCancelCallback signals the cancellation of the operation. */
#if UA_MULTITHREADING >= 100
UA_Lock asyncLock;
#endif
UA_Variant * workQueue[1]; /* The currently active async callback */
static void
asyncOperationCancelCallback(UA_Server *server, const void *out) {
/* This blocks if a worker thread currently processes the async callback */
UA_LOCK(&asyncLock);
if((void*)workQueue[0] == out)
workQueue[0] = NULL; /* Disable active async callback */
UA_UNLOCK(&asyncLock);
}
static void
asyncCall(UA_Server *server, void *data) {
UA_LOG_INFO(UA_Server_getConfig(server)->logging, UA_LOGCATEGORY_APPLICATION, "call");
/* Process async result if still active */
UA_LOCK(&asyncLock);
if((void*)workQueue[0] == data)
UA_Server_setAsyncCallMethodResult(server, workQueue[0], UA_STATUSCODE_GOOD);
workQueue[0] = NULL;
UA_UNLOCK(&asyncLock);
}
static UA_StatusCode
helloWorldMethodCallback1(UA_Server *server,
const UA_NodeId *sessionId, void *sessionHandle,
const UA_NodeId *methodId, void *methodContext,
const UA_NodeId *objectId, void *objectContext,
size_t inputSize, const UA_Variant *input,
size_t outputSize, UA_Variant *output) {
UA_LOG_INFO(UA_Server_getConfig(server)->logging, UA_LOGCATEGORY_APPLICATION, "async");
UA_LOCK(&asyncLock);
/* The work queue is full */
if(workQueue[0]) {
UA_LOG_WARNING(UA_Server_getConfig(server)->logging, UA_LOGCATEGORY_APPLICATION,
"async queue full");
UA_UNLOCK(&asyncLock);
return UA_STATUSCODE_BADTOOMANYOPERATIONS;
}
/* Prepare the output */
UA_String *inputStr = (UA_String*)input->data;
UA_String out = UA_STRING_NULL;
UA_String_format(&out, "Hello %S", *inputStr);
UA_Variant_setScalarCopy(output, &out, &UA_TYPES[UA_TYPES_STRING]);
UA_String_clear(&out);
/* Return the output with a five second delay */
UA_DateTime callTime = UA_DateTime_nowMonotonic() + (2 * UA_DATETIME_SEC);
UA_Server_addTimedCallback(server, asyncCall, output, callTime, NULL);
/* Store the pointer to the active async operation.
* So it can be cancelled. */
workQueue[0] = output;
UA_UNLOCK(&asyncLock);
/* Signal async processing to the server. Will be completed later. */
return UA_STATUSCODE_GOODCOMPLETESASYNCHRONOUSLY;
}
int main(void) {
/* Create the server */
UA_LOCK_INIT(&asyncLock);
UA_Server *server = UA_Server_new();
/* Set the cancel callback */
UA_ServerConfig *sc = UA_Server_getConfig(server);
sc->asyncOperationCancelCallback = asyncOperationCancelCallback;
/* Add method */
UA_Argument inputArgument;
UA_Argument_init(&inputArgument);
inputArgument.description = UA_LOCALIZEDTEXT("en-US", "A String");
inputArgument.name = UA_STRING("MyInput");
inputArgument.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
inputArgument.valueRank = UA_VALUERANK_SCALAR;
UA_Argument outputArgument;
UA_Argument_init(&outputArgument);
outputArgument.description = UA_LOCALIZEDTEXT("en-US", "A String");
outputArgument.name = UA_STRING("MyOutput");
outputArgument.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
outputArgument.valueRank = UA_VALUERANK_SCALAR;
UA_MethodAttributes helloAttr = UA_MethodAttributes_default;
helloAttr.description = UA_LOCALIZEDTEXT("en-US","Say `Hello World` async");
helloAttr.displayName = UA_LOCALIZEDTEXT("en-US","Hello World async");
helloAttr.executable = true;
helloAttr.userExecutable = true;
UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1,62541),
UA_NS0ID(OBJECTSFOLDER), UA_NS0ID(HASCOMPONENT),
UA_QUALIFIEDNAME(1, "hello world"),
helloAttr, &helloWorldMethodCallback1,
1, &inputArgument, 1, &outputArgument, NULL, NULL);
/* Run the server */
UA_Server_runUntilInterrupt(server);
/* Clean up */
UA_Server_delete(server);
UA_LOCK_DESTROY(&asyncLock);
return 0;
}