forked from open62541/open62541
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tutorial_server_method.c
187 lines (164 loc) · 8.06 KB
/
tutorial_server_method.c
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/* 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 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/plugin/log_stdout.h>
#include <open62541/server.h>
#include <open62541/server_config_default.h>
#include <signal.h>
#include <stdlib.h>
static UA_StatusCode
helloWorldMethodCallback(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_String *inputStr = (UA_String*)input->data;
UA_String tmp = UA_STRING_ALLOC("Hello ");
if(inputStr->length > 0) {
tmp.data = (UA_Byte *)UA_realloc(tmp.data, tmp.length + inputStr->length);
memcpy(&tmp.data[tmp.length], inputStr->data, inputStr->length);
tmp.length += inputStr->length;
}
UA_Variant_setScalarCopy(output, &tmp, &UA_TYPES[UA_TYPES_STRING]);
UA_String_clear(&tmp);
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Hello World was called");
return UA_STATUSCODE_GOOD;
}
static void
addHellWorldMethod(UA_Server *server) {
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`");
helloAttr.displayName = UA_LOCALIZEDTEXT("en-US","Hello World");
helloAttr.executable = true;
helloAttr.userExecutable = true;
UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1,62541),
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
UA_QUALIFIEDNAME(1, "hello world"),
helloAttr, &helloWorldMethodCallback,
1, &inputArgument, 1, &outputArgument, NULL, NULL);
}
/**
* Increase Array Values Method
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* The method takes an array of 5 integers and a scalar as input. It returns a
* copy of the array with every entry increased by the scalar. */
static UA_StatusCode
IncInt32ArrayMethodCallback(UA_Server *server,
const UA_NodeId *sessionId, void *sessionContext,
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_Int32 *inputArray = (UA_Int32*)input[0].data;
UA_Int32 delta = *(UA_Int32*)input[1].data;
/* Copy the input array */
UA_StatusCode retval = UA_Variant_setArrayCopy(output, inputArray, 5,
&UA_TYPES[UA_TYPES_INT32]);
if(retval != UA_STATUSCODE_GOOD)
return retval;
/* Increate the elements */
UA_Int32 *outputArray = (UA_Int32*)output->data;
for(size_t i = 0; i < input->arrayLength; i++)
outputArray[i] = inputArray[i] + delta;
return UA_STATUSCODE_GOOD;
}
static void
addIncInt32ArrayMethod(UA_Server *server) {
/* Two input arguments */
UA_Argument inputArguments[2];
UA_Argument_init(&inputArguments[0]);
inputArguments[0].description = UA_LOCALIZEDTEXT("en-US", "int32[5] array");
inputArguments[0].name = UA_STRING("int32 array");
inputArguments[0].dataType = UA_TYPES[UA_TYPES_INT32].typeId;
inputArguments[0].valueRank = UA_VALUERANK_ONE_DIMENSION;
UA_UInt32 pInputDimension = 5;
inputArguments[0].arrayDimensionsSize = 1;
inputArguments[0].arrayDimensions = &pInputDimension;
UA_Argument_init(&inputArguments[1]);
inputArguments[1].description = UA_LOCALIZEDTEXT("en-US", "int32 delta");
inputArguments[1].name = UA_STRING("int32 delta");
inputArguments[1].dataType = UA_TYPES[UA_TYPES_INT32].typeId;
inputArguments[1].valueRank = UA_VALUERANK_SCALAR;
/* One output argument */
UA_Argument outputArgument;
UA_Argument_init(&outputArgument);
outputArgument.description = UA_LOCALIZEDTEXT("en-US", "int32[5] array");
outputArgument.name = UA_STRING("each entry is incremented by the delta");
outputArgument.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
outputArgument.valueRank = UA_VALUERANK_ONE_DIMENSION;
UA_UInt32 pOutputDimension = 5;
outputArgument.arrayDimensionsSize = 1;
outputArgument.arrayDimensions = &pOutputDimension;
/* Add the method node */
UA_MethodAttributes incAttr = UA_MethodAttributes_default;
incAttr.description = UA_LOCALIZEDTEXT("en-US", "IncInt32ArrayValues");
incAttr.displayName = UA_LOCALIZEDTEXT("en-US", "IncInt32ArrayValues");
incAttr.executable = true;
incAttr.userExecutable = true;
UA_Server_addMethodNode(server, UA_NODEID_STRING(1, "IncInt32ArrayValues"),
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
UA_QUALIFIEDNAME(1, "IncInt32ArrayValues"),
incAttr, &IncInt32ArrayMethodCallback,
2, inputArguments, 1, &outputArgument,
NULL, NULL);
}
/** It follows the main server code, making use of the above definitions. */
static volatile UA_Boolean running = true;
static void stopHandler(int sign) {
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
running = false;
}
int main(void) {
signal(SIGINT, stopHandler);
signal(SIGTERM, stopHandler);
UA_Server *server = UA_Server_new();
UA_ServerConfig_setDefault(UA_Server_getConfig(server));
addHellWorldMethod(server);
addIncInt32ArrayMethod(server);
UA_StatusCode retval = UA_Server_run(server, &running);
UA_Server_delete(server);
return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}