/src/S2OPC/src/ClientServer/services/b2c/util_event.c
Line | Count | Source |
1 | | /* |
2 | | * Licensed to Systerel under one or more contributor license |
3 | | * agreements. See the NOTICE file distributed with this work |
4 | | * for additional information regarding copyright ownership. |
5 | | * Systerel licenses this file to you under the Apache |
6 | | * License, Version 2.0 (the "License"); you may not use this |
7 | | * file except in compliance with the License. You may obtain |
8 | | * a copy of the License at |
9 | | * |
10 | | * http://www.apache.org/licenses/LICENSE-2.0 |
11 | | * |
12 | | * Unless required by applicable law or agreed to in writing, |
13 | | * software distributed under the License is distributed on an |
14 | | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
15 | | * KIND, either express or implied. See the License for the |
16 | | * specific language governing permissions and limitations |
17 | | * under the License. |
18 | | */ |
19 | | |
20 | | #include "util_event.h" |
21 | | |
22 | | #include "constants.h" |
23 | | #include "opcua_statuscodes.h" |
24 | | #include "sopc_assert.h" |
25 | | #include "sopc_date_time.h" |
26 | | #include "sopc_macros.h" |
27 | | #include "sopc_mem_alloc.h" |
28 | | #include "sopc_toolkit_config_internal.h" |
29 | | #include "util_b2c.h" |
30 | | #include "util_variant.h" |
31 | | |
32 | | const SOPC_Server_Event_Types* initEventTypes = NULL; |
33 | | |
34 | | static const SOPC_NodeId EventQueueOverflowTypeId = SOPC_NODEID_NS0_NUMERIC(OpcUaId_EventQueueOverflowEventType); |
35 | | static const SOPC_NodeId ServerId = SOPC_NODEID_NS0_NUMERIC(OpcUaId_Server); |
36 | | static const SOPC_NodeId BaseEventTypeId = SOPC_NODEID_NS0_NUMERIC(OpcUaId_BaseEventType); |
37 | | |
38 | | static const SOPC_Event* queueOverflowEvent = NULL; |
39 | | |
40 | | bool init_event_types(SOPC_EndpointConfigIdx epIdx) |
41 | 0 | { |
42 | 0 | if (NULL == initEventTypes) |
43 | 0 | { |
44 | 0 | SOPC_Endpoint_Config* endpointConfig = SOPC_ToolkitServer_GetEndpointConfig(epIdx); |
45 | 0 | SOPC_ASSERT(NULL != endpointConfig && NULL != endpointConfig->serverConfigPtr); |
46 | 0 | SOPC_Server_Config* serverConfig = endpointConfig->serverConfigPtr; |
47 | 0 | initEventTypes = serverConfig->eventTypes; |
48 | 0 | } |
49 | | // It should not be NULL when EventNotifier attribute is supported (necessary to reach this point). |
50 | | // But since it is only guaranteed to be initialized when using server wrapper we still have to check it. |
51 | 0 | return NULL != initEventTypes; |
52 | 0 | } |
53 | | |
54 | | bool util_event__alloc_event_field_list(uint32_t clientHandle, |
55 | | int32_t nbSelectClauses, |
56 | | OpcUa_EventFieldList** ppEventFieldList) |
57 | 0 | { |
58 | 0 | SOPC_ASSERT(NULL != ppEventFieldList); |
59 | 0 | bool res = false; |
60 | 0 | OpcUa_EventFieldList* eventFieldList = NULL; |
61 | 0 | SOPC_ReturnStatus status = |
62 | 0 | SOPC_EncodeableObject_Create(&OpcUa_EventFieldList_EncodeableType, (void**) &eventFieldList); |
63 | 0 | if (SOPC_STATUS_OK == status) |
64 | 0 | { |
65 | 0 | eventFieldList->ClientHandle = clientHandle; |
66 | 0 | eventFieldList->EventFields = SOPC_Calloc((size_t) nbSelectClauses, sizeof(*eventFieldList->EventFields)); |
67 | 0 | eventFieldList->NoOfEventFields = nbSelectClauses; |
68 | 0 | if (NULL != eventFieldList->EventFields) |
69 | 0 | { |
70 | 0 | res = true; |
71 | 0 | *ppEventFieldList = eventFieldList; |
72 | 0 | } |
73 | 0 | else |
74 | 0 | { |
75 | 0 | SOPC_Free(eventFieldList); |
76 | 0 | } |
77 | 0 | } |
78 | 0 | return res; |
79 | 0 | } |
80 | | |
81 | | static void applyDataValueTsRemoval(SOPC_DataValue* dv, bool removeSrc, bool removeSrv) |
82 | 0 | { |
83 | 0 | SOPC_ASSERT(NULL != dv); |
84 | 0 | if (removeSrc) |
85 | 0 | { |
86 | 0 | dv->SourceTimestamp = 0; |
87 | 0 | dv->SourcePicoSeconds = 0; |
88 | 0 | } |
89 | 0 | if (removeSrv) |
90 | 0 | { |
91 | 0 | dv->ServerTimestamp = 0; |
92 | 0 | dv->ServerPicoSeconds = 0; |
93 | 0 | } |
94 | 0 | } |
95 | | |
96 | | void util_event__set_event_field_list_elt(char** preferredLocalesIds, |
97 | | const constants__t_TimestampsToReturn_i timestampToReturn, |
98 | | bool userAccessGranted, |
99 | | int32_t selectClauseIdx, |
100 | | const SOPC_InternalMonitoredItemFilterCtx* pFilterCtx, |
101 | | const SOPC_Event* pEvent, |
102 | | OpcUa_EventFieldList* pEventFieldList) |
103 | 0 | { |
104 | 0 | SOPC_Variant* selectVal = &pEventFieldList->EventFields[selectClauseIdx]; |
105 | | |
106 | | /* |
107 | | * From part 4 (v1.05) §7.22.3: |
108 | | * For example, the Server shall set the event field to Bad_UserAccessDenied |
109 | | * if the value is not accessible to the user associated with the Session. |
110 | | * If a Value Attribute has an uncertain or bad StatusCode associated with it then the Server |
111 | | * shall provide the StatusCode instead of the Value Attribute. |
112 | | */ |
113 | | // eventNotifier attribute is considered not accessible, return a bad status instead of value |
114 | 0 | if (!userAccessGranted) |
115 | 0 | { |
116 | 0 | SOPC_Variant_Initialize(selectVal); |
117 | 0 | selectVal->BuiltInTypeId = SOPC_StatusCode_Id; |
118 | 0 | selectVal->Value.Status = OpcUa_BadUserAccessDenied; |
119 | 0 | return; |
120 | 0 | } |
121 | | |
122 | 0 | SOPC_ASSERT(NULL != pFilterCtx && !pFilterCtx->isDataFilter); |
123 | 0 | char* qnPath = pFilterCtx->Filter.Event.qnPathStrSelectClauses[selectClauseIdx]; |
124 | 0 | SOPC_NumericRange* indexRange = pFilterCtx->Filter.Event.indexRangeSelectClauses[selectClauseIdx]; |
125 | 0 | const SOPC_Variant* eventVar = SOPC_Event_GetVariableFromStrPath(pEvent, qnPath); |
126 | | // Restricts empty browse path for NodeId attribute |
127 | | // (other browse paths are for Value attribute only which is already checked on creation) |
128 | 0 | if (NULL != eventVar && '\0' == *qnPath && |
129 | 0 | SOPC_AttributeId_NodeId != pFilterCtx->Filter.Event.eventFilter->SelectClauses[selectClauseIdx].AttributeId) |
130 | 0 | { |
131 | 0 | eventVar = NULL; |
132 | 0 | } |
133 | |
|
134 | 0 | if (NULL != eventVar) |
135 | 0 | { |
136 | 0 | SOPC_ReturnStatus status = |
137 | 0 | util_variant__copy_and_apply_locales_and_index_range(selectVal, eventVar, preferredLocalesIds, indexRange); |
138 | | |
139 | | /* Apply the TimestampToReturn in case of DataValue built it type: |
140 | | * "When monitoring Events, this applies only to Event fields that are of type DataValue. |
141 | | */ |
142 | 0 | if (SOPC_STATUS_OK == status && SOPC_DataValue_Id == selectVal->BuiltInTypeId) |
143 | 0 | { |
144 | 0 | bool removeSrc = false; |
145 | 0 | bool removeSrv = false; |
146 | 0 | switch (timestampToReturn) |
147 | 0 | { |
148 | 0 | case constants__e_ttr_source: |
149 | 0 | removeSrv = true; |
150 | 0 | break; |
151 | 0 | case constants__e_ttr_server: |
152 | 0 | removeSrc = true; |
153 | 0 | break; |
154 | 0 | case constants__e_ttr_neither: |
155 | 0 | removeSrc = true; |
156 | 0 | removeSrv = true; |
157 | 0 | break; |
158 | 0 | default: |
159 | | // Keep both in other cases |
160 | 0 | break; |
161 | 0 | } |
162 | | |
163 | 0 | if (removeSrc || removeSrv) |
164 | 0 | { |
165 | 0 | if (SOPC_VariantArrayType_SingleValue == selectVal->ArrayType) |
166 | 0 | { |
167 | 0 | applyDataValueTsRemoval(selectVal->Value.DataValue, removeSrc, removeSrv); |
168 | 0 | } |
169 | 0 | else |
170 | 0 | { |
171 | 0 | int32_t length = SOPC_Variant_GetArrayOrMatrixLength(selectVal); |
172 | 0 | for (int32_t i = 0; i < length; i++) |
173 | 0 | { |
174 | 0 | applyDataValueTsRemoval(SOPC_VARIANT_GET_ARRAY_VALUES_PTR(selectVal, DataValue), removeSrc, |
175 | 0 | removeSrv); |
176 | 0 | } |
177 | 0 | } |
178 | 0 | } |
179 | 0 | } |
180 | 0 | } // else: keep NULL variant selectVal content |
181 | 0 | } |
182 | | |
183 | | static bool is_event_type_compatible_with_select_clause(const OpcUa_SimpleAttributeOperand* selectClause, |
184 | | const SOPC_NodeId* eventTypeId) |
185 | 0 | { |
186 | 0 | bool res = SOPC_NodeId_Equal(&BaseEventTypeId, &selectClause->TypeDefinitionId); |
187 | 0 | if (!res) |
188 | 0 | { |
189 | 0 | res = SOPC_NodeId_Equal(&selectClause->TypeDefinitionId, eventTypeId); |
190 | 0 | } |
191 | 0 | return res; |
192 | 0 | } |
193 | | |
194 | | bool util_event__alloc_and_fill_event_field_list(const SOPC_InternalMonitoredItemFilterCtx* pFilterCtx, |
195 | | uint32_t clientHandle, |
196 | | char** preferredLocalesIds, |
197 | | const constants__t_TimestampsToReturn_i timestampToReturn, |
198 | | bool userAccessGranted, |
199 | | const SOPC_Event* pEvent, |
200 | | OpcUa_EventFieldList** ppEventFieldList) |
201 | 0 | { |
202 | 0 | SOPC_ASSERT(NULL != pFilterCtx); |
203 | 0 | SOPC_ASSERT(NULL != pEvent); |
204 | 0 | SOPC_ASSERT(NULL != ppEventFieldList); |
205 | 0 | SOPC_ASSERT(!pFilterCtx->isDataFilter); |
206 | 0 | int32_t nbSelectClauses = pFilterCtx->Filter.Event.eventFilter->NoOfSelectClauses; |
207 | 0 | bool res = nbSelectClauses > 0; |
208 | 0 | bool setField = false; |
209 | 0 | if (res) |
210 | 0 | { |
211 | 0 | res = util_event__alloc_event_field_list(clientHandle, nbSelectClauses, ppEventFieldList); |
212 | 0 | } |
213 | 0 | if (res) |
214 | 0 | { |
215 | 0 | const SOPC_NodeId* eventTypeId = SOPC_Event_GetEventTypeId(pEvent); |
216 | 0 | for (int32_t i = 0; i < nbSelectClauses; i++) |
217 | 0 | { |
218 | 0 | setField = is_event_type_compatible_with_select_clause( |
219 | 0 | &pFilterCtx->Filter.Event.eventFilter->SelectClauses[i], eventTypeId); |
220 | 0 | if (setField) |
221 | 0 | { |
222 | 0 | util_event__set_event_field_list_elt(preferredLocalesIds, timestampToReturn, userAccessGranted, i, |
223 | 0 | pFilterCtx, pEvent, *ppEventFieldList); |
224 | 0 | } |
225 | 0 | } |
226 | 0 | } |
227 | |
|
228 | 0 | return res; |
229 | 0 | } |
230 | | |
231 | | static SOPC_Event* create_queueOverflowEvent(void) |
232 | 0 | { |
233 | 0 | SOPC_ASSERT(NULL != initEventTypes); |
234 | 0 | if (NULL == queueOverflowEvent) |
235 | 0 | { |
236 | 0 | SOPC_Event* QOfEvent = SOPC_EventManager_CreateEventInstance(initEventTypes, &EventQueueOverflowTypeId); |
237 | 0 | if (NULL == QOfEvent) |
238 | 0 | { |
239 | | // Impossible to instantiate the event type |
240 | 0 | return false; |
241 | 0 | } |
242 | | /** |
243 | | * From part 5 (v1.05) §6.4.34: |
244 | | * The SourceNode Property for Events of this type shall be assigned to the NodeId of the Server |
245 | | * Object. The SourceName for Events of this type shall be "Internal/EventQueueOverflow". |
246 | | */ |
247 | 0 | SOPC_ReturnStatus status = SOPC_Event_SetSourceNode(QOfEvent, &ServerId); |
248 | 0 | SOPC_String sourceName; |
249 | 0 | SOPC_String_Initialize(&sourceName); |
250 | 0 | if (SOPC_STATUS_OK == status) |
251 | 0 | { |
252 | 0 | status = SOPC_String_AttachFromCstring(&sourceName, "Internal/EventQueueOverflow"); |
253 | 0 | } |
254 | 0 | if (SOPC_STATUS_OK == status) |
255 | 0 | { |
256 | 0 | status = SOPC_Event_SetSourceName(QOfEvent, &sourceName); |
257 | 0 | } |
258 | 0 | if (SOPC_STATUS_OK == status) |
259 | 0 | { |
260 | 0 | queueOverflowEvent = QOfEvent; |
261 | 0 | } |
262 | 0 | else |
263 | 0 | { |
264 | 0 | SOPC_Event_Delete(&QOfEvent); |
265 | 0 | } |
266 | 0 | } |
267 | | |
268 | 0 | SOPC_Event* event = SOPC_Event_CreateCopy(queueOverflowEvent, true); |
269 | 0 | if (NULL == event) |
270 | 0 | { |
271 | 0 | return NULL; |
272 | 0 | } |
273 | | |
274 | 0 | SOPC_ReturnStatus status = SOPC_Event_SetTime(event, SOPC_Time_GetCurrentTimeUTC()); |
275 | | // We still want to generate the created queueOverflowEvent even if it fails |
276 | 0 | SOPC_UNUSED_RESULT(status); |
277 | 0 | return event; |
278 | 0 | } |
279 | | |
280 | | bool util_event__gen_event_queue_overflow_notification(const SOPC_InternalMonitoredItemFilterCtx* pFilterCtx, |
281 | | uint32_t clientHandle, |
282 | | OpcUa_EventFieldList** ppEventFieldList) |
283 | 0 | { |
284 | 0 | SOPC_Event* queueOverflowEventInst = create_queueOverflowEvent(); |
285 | 0 | if (NULL == queueOverflowEventInst) |
286 | 0 | { |
287 | 0 | return false; |
288 | 0 | } |
289 | 0 | bool res = util_event__alloc_and_fill_event_field_list(pFilterCtx, clientHandle, NULL, constants__e_ttr_both, true, |
290 | 0 | queueOverflowEventInst, ppEventFieldList); |
291 | 0 | SOPC_Event_Delete(&queueOverflowEventInst); |
292 | 0 | return res; |
293 | 0 | } |