forked from open62541/open62541
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tutorial_server_events.c
162 lines (137 loc) · 7.36 KB
/
tutorial_server_events.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
/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
* See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
#include <open62541/plugin/log_stdout.h>
#include <open62541/server.h>
#include <open62541/server_config_default.h>
#include <signal.h>
#include <stdlib.h>
/**
* Generating events
* -----------------
* To make sense of the many things going on in a server, monitoring items can be useful. Though in many cases, data
* change does not convey enough information to be the optimal solution. Events can be generated at any time,
* hold a lot of information and can be filtered so the client only receives the specific attributes of interest.
*
* Emitting events by calling methods
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* The following example will be based on the server method tutorial. We will be
* creating a method node which generates an event from the server node.
*
* The event we want to generate should be very simple. Since the `BaseEventType` is abstract,
* we will have to create our own event type. `EventTypes` are saved internally as `ObjectTypes`,
* so add the type as you would a new `ObjectType`. */
static UA_NodeId eventType;
static UA_StatusCode
addNewEventType(UA_Server *server) {
UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default;
attr.displayName = UA_LOCALIZEDTEXT("en-US", "SimpleEventType");
attr.description = UA_LOCALIZEDTEXT("en-US", "The simple event type we created");
return UA_Server_addObjectTypeNode(server, UA_NODEID_NULL,
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE),
UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
UA_QUALIFIEDNAME(0, "SimpleEventType"),
attr, NULL, &eventType);
}
/**
* Setting up an event
* ^^^^^^^^^^^^^^^^^^^
* In order to set up the event, we can first use ``UA_Server_createEvent`` to give us a node representation of the event.
* All we need for this is our `EventType`. Once we have our event node, which is saved internally as an `ObjectNode`,
* we can define the attributes the event has the same way we would define the attributes of an object node. It is not
* necessary to define the attributes `EventId`, `ReceiveTime`, `SourceNode` or `EventType` since these are set
* automatically by the server. In this example, we will be setting the fields 'Message' and 'Severity' in addition
* to `Time` which is needed to make the example UaExpert compliant.
*/
static UA_StatusCode
setUpEvent(UA_Server *server, UA_NodeId *outId) {
UA_StatusCode retval = UA_Server_createEvent(server, eventType, outId);
if (retval != UA_STATUSCODE_GOOD) {
UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
"createEvent failed. StatusCode %s", UA_StatusCode_name(retval));
return retval;
}
/* Set the Event Attributes */
/* Setting the Time is required or else the event will not show up in UAExpert! */
UA_DateTime eventTime = UA_DateTime_now();
UA_Server_writeObjectProperty_scalar(server, *outId, UA_QUALIFIEDNAME(0, "Time"),
&eventTime, &UA_TYPES[UA_TYPES_DATETIME]);
UA_UInt16 eventSeverity = 100;
UA_Server_writeObjectProperty_scalar(server, *outId, UA_QUALIFIEDNAME(0, "Severity"),
&eventSeverity, &UA_TYPES[UA_TYPES_UINT16]);
UA_LocalizedText eventMessage = UA_LOCALIZEDTEXT("en-US", "An event has been generated.");
UA_Server_writeObjectProperty_scalar(server, *outId, UA_QUALIFIEDNAME(0, "Message"),
&eventMessage, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
UA_String eventSourceName = UA_STRING("Server");
UA_Server_writeObjectProperty_scalar(server, *outId, UA_QUALIFIEDNAME(0, "SourceName"),
&eventSourceName, &UA_TYPES[UA_TYPES_STRING]);
return UA_STATUSCODE_GOOD;
}
/**
* Triggering an event
* ^^^^^^^^^^^^^^^^^^^
* First a node representing an event is generated using ``setUpEvent``. Once our event is good to go, we specify
* a node which emits the event - in this case the server node. We can use ``UA_Server_triggerEvent`` to trigger our
* event onto said node. Passing ``NULL`` as the second-last argument means we will not receive the `EventId`.
* The last boolean argument states whether the node should be deleted. */
static UA_StatusCode
generateEventMethodCallback(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_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Creating event");
/* set up event */
UA_NodeId eventNodeId;
UA_StatusCode retval = setUpEvent(server, &eventNodeId);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Creating event failed. StatusCode %s", UA_StatusCode_name(retval));
return retval;
}
retval = UA_Server_triggerEvent(server, eventNodeId,
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
NULL, UA_TRUE);
if(retval != UA_STATUSCODE_GOOD)
UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Triggering event failed. StatusCode %s", UA_StatusCode_name(retval));
return retval;
}
/**
* Now, all that is left to do is to create a method node which uses our callback. We do not
* require any input and as output we will be using the `EventId` we receive from ``triggerEvent``. The `EventId` is
* generated by the server internally and is a random unique ID which identifies that specific event.
*
* This method node will be added to a basic server setup.
*/
static void
addGenerateEventMethod(UA_Server *server) {
UA_MethodAttributes generateAttr = UA_MethodAttributes_default;
generateAttr.description = UA_LOCALIZEDTEXT("en-US","Generate an event.");
generateAttr.displayName = UA_LOCALIZEDTEXT("en-US","Generate Event");
generateAttr.executable = true;
generateAttr.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, "Generate Event"),
generateAttr, &generateEventMethodCallback,
0, NULL, 0, NULL, NULL, NULL);
}
/** It follows the main server code, making use of the above definitions. */
static volatile UA_Boolean running = true;
static void stopHandler(int sig) {
running = false;
}
int main (void) {
/* default server values */
signal(SIGINT, stopHandler);
signal(SIGTERM, stopHandler);
UA_Server *server = UA_Server_new();
UA_ServerConfig_setDefault(UA_Server_getConfig(server));
addNewEventType(server);
addGenerateEventMethod(server);
UA_StatusCode retval = UA_Server_run(server, &running);
UA_Server_delete(server);
return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}