/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 | | static const SOPC_NodeId EventQueueOverflowTypeId = SOPC_NODEID_NS0_NUMERIC(OpcUaId_EventQueueOverflowEventType); |
33 | | static const SOPC_NodeId ServerId = SOPC_NODEID_NS0_NUMERIC(OpcUaId_Server); |
34 | | static const SOPC_NodeId BaseEventTypeId = SOPC_NODEID_NS0_NUMERIC(OpcUaId_BaseEventType); |
35 | | |
36 | | static const SOPC_Event* queueOverflowEvent = NULL; |
37 | | |
38 | | const SOPC_Server_Event_Types* util_event__get_event_types(void) |
39 | 0 | { |
40 | | // Use first EP as all are pointing to the same server configuration |
41 | 0 | SOPC_Endpoint_Config* endpointConfig = SOPC_ToolkitServer_GetEndpointConfig(1); |
42 | 0 | if (NULL != endpointConfig && NULL != endpointConfig->serverConfigPtr) |
43 | 0 | { |
44 | 0 | SOPC_Server_Config* serverConfig = endpointConfig->serverConfigPtr; |
45 | 0 | return serverConfig->eventTypes; |
46 | 0 | } |
47 | 0 | else |
48 | 0 | { |
49 | 0 | return NULL; |
50 | 0 | } |
51 | 0 | } |
52 | | |
53 | | bool util_event__alloc_event_field_list(uint32_t clientHandle, |
54 | | int32_t nbSelectClauses, |
55 | | OpcUa_EventFieldList** ppEventFieldList) |
56 | 0 | { |
57 | 0 | SOPC_ASSERT(NULL != ppEventFieldList); |
58 | 0 | bool res = false; |
59 | 0 | OpcUa_EventFieldList* eventFieldList = NULL; |
60 | 0 | SOPC_ReturnStatus status = |
61 | 0 | SOPC_EncodeableObject_Create(&OpcUa_EventFieldList_EncodeableType, (void**) &eventFieldList); |
62 | 0 | if (SOPC_STATUS_OK == status) |
63 | 0 | { |
64 | 0 | eventFieldList->ClientHandle = clientHandle; |
65 | 0 | eventFieldList->EventFields = SOPC_Calloc((size_t) nbSelectClauses, sizeof(*eventFieldList->EventFields)); |
66 | 0 | eventFieldList->NoOfEventFields = nbSelectClauses; |
67 | 0 | if (NULL != eventFieldList->EventFields) |
68 | 0 | { |
69 | 0 | res = true; |
70 | 0 | *ppEventFieldList = eventFieldList; |
71 | 0 | } |
72 | 0 | else |
73 | 0 | { |
74 | 0 | SOPC_Free(eventFieldList); |
75 | 0 | } |
76 | 0 | } |
77 | 0 | return res; |
78 | 0 | } |
79 | | |
80 | | static void applyDataValueTsRemoval(SOPC_DataValue* dv, bool removeSrc, bool removeSrv) |
81 | 0 | { |
82 | 0 | SOPC_ASSERT(NULL != dv); |
83 | 0 | if (removeSrc) |
84 | 0 | { |
85 | 0 | dv->SourceTimestamp = 0; |
86 | 0 | dv->SourcePicoSeconds = 0; |
87 | 0 | } |
88 | 0 | if (removeSrv) |
89 | 0 | { |
90 | 0 | dv->ServerTimestamp = 0; |
91 | 0 | dv->ServerPicoSeconds = 0; |
92 | 0 | } |
93 | 0 | } |
94 | | |
95 | | void util_event__set_event_field_list_elt(char** preferredLocalesIds, |
96 | | const constants__t_TimestampsToReturn_i timestampToReturn, |
97 | | bool userAccessGranted, |
98 | | int32_t selectClauseIdx, |
99 | | const SOPC_InternalMonitoredItemFilterCtx* pFilterCtx, |
100 | | const SOPC_Event* pEvent, |
101 | | OpcUa_EventFieldList* pEventFieldList) |
102 | 0 | { |
103 | 0 | SOPC_Variant* selectVal = &pEventFieldList->EventFields[selectClauseIdx]; |
104 | | |
105 | | /* |
106 | | * From part 4 (v1.05) §7.22.3: |
107 | | * For example, the Server shall set the event field to Bad_UserAccessDenied |
108 | | * if the value is not accessible to the user associated with the Session. |
109 | | * If a Value Attribute has an uncertain or bad StatusCode associated with it then the Server |
110 | | * shall provide the StatusCode instead of the Value Attribute. |
111 | | */ |
112 | | // eventNotifier attribute is considered not accessible, return a bad status instead of value |
113 | 0 | if (!userAccessGranted) |
114 | 0 | { |
115 | 0 | SOPC_Variant_Initialize(selectVal); |
116 | 0 | selectVal->BuiltInTypeId = SOPC_StatusCode_Id; |
117 | 0 | selectVal->Value.Status = OpcUa_BadUserAccessDenied; |
118 | 0 | return; |
119 | 0 | } |
120 | | |
121 | 0 | SOPC_ASSERT(NULL != pFilterCtx && !pFilterCtx->isDataFilter); |
122 | 0 | char* qnPath = pFilterCtx->Filter.Event.qnPathStrSelectClauses[selectClauseIdx]; |
123 | 0 | SOPC_NumericRange* indexRange = pFilterCtx->Filter.Event.indexRangeSelectClauses[selectClauseIdx]; |
124 | 0 | const SOPC_Variant* eventVar = SOPC_Event_GetVariableFromStrPath(pEvent, qnPath); |
125 | | // Restricts empty browse path for NodeId attribute |
126 | | // (other browse paths are for Value attribute only which is already checked on creation) |
127 | 0 | if (NULL != eventVar && '\0' == *qnPath && |
128 | 0 | SOPC_AttributeId_NodeId != pFilterCtx->Filter.Event.eventFilter->SelectClauses[selectClauseIdx].AttributeId) |
129 | 0 | { |
130 | 0 | eventVar = NULL; |
131 | 0 | } |
132 | |
|
133 | 0 | if (NULL != eventVar) |
134 | 0 | { |
135 | 0 | SOPC_ReturnStatus status = |
136 | 0 | util_variant__copy_and_apply_locales_and_index_range(selectVal, eventVar, preferredLocalesIds, indexRange); |
137 | | |
138 | | /* Apply the TimestampToReturn in case of DataValue built it type: |
139 | | * "When monitoring Events, this applies only to Event fields that are of type DataValue. |
140 | | */ |
141 | 0 | if (SOPC_STATUS_OK == status && SOPC_DataValue_Id == selectVal->BuiltInTypeId) |
142 | 0 | { |
143 | 0 | bool removeSrc = false; |
144 | 0 | bool removeSrv = false; |
145 | 0 | switch (timestampToReturn) |
146 | 0 | { |
147 | 0 | case constants__e_ttr_source: |
148 | 0 | removeSrv = true; |
149 | 0 | break; |
150 | 0 | case constants__e_ttr_server: |
151 | 0 | removeSrc = true; |
152 | 0 | break; |
153 | 0 | case constants__e_ttr_neither: |
154 | 0 | removeSrc = true; |
155 | 0 | removeSrv = true; |
156 | 0 | break; |
157 | 0 | default: |
158 | | // Keep both in other cases |
159 | 0 | break; |
160 | 0 | } |
161 | | |
162 | 0 | if (removeSrc || removeSrv) |
163 | 0 | { |
164 | 0 | if (SOPC_VariantArrayType_SingleValue == selectVal->ArrayType) |
165 | 0 | { |
166 | 0 | applyDataValueTsRemoval(selectVal->Value.DataValue, removeSrc, removeSrv); |
167 | 0 | } |
168 | 0 | else |
169 | 0 | { |
170 | 0 | int32_t length = SOPC_Variant_GetArrayOrMatrixLength(selectVal); |
171 | 0 | for (int32_t i = 0; i < length; i++) |
172 | 0 | { |
173 | 0 | applyDataValueTsRemoval(SOPC_VARIANT_GET_ARRAY_VALUES_PTR(selectVal, DataValue), removeSrc, |
174 | 0 | removeSrv); |
175 | 0 | } |
176 | 0 | } |
177 | 0 | } |
178 | 0 | } |
179 | 0 | } // else: keep NULL variant selectVal content |
180 | 0 | } |
181 | | |
182 | | static bool is_event_type_compatible_with_select_clause(const OpcUa_SimpleAttributeOperand* selectClause, |
183 | | const SOPC_NodeId* eventTypeId) |
184 | 0 | { |
185 | 0 | bool res = SOPC_NodeId_Equal(&BaseEventTypeId, &selectClause->TypeDefinitionId); |
186 | 0 | if (!res) |
187 | 0 | { |
188 | 0 | res = SOPC_NodeId_Equal(&selectClause->TypeDefinitionId, eventTypeId); |
189 | 0 | } |
190 | 0 | return res; |
191 | 0 | } |
192 | | |
193 | | bool util_event__alloc_and_fill_event_field_list(const SOPC_InternalMonitoredItemFilterCtx* pFilterCtx, |
194 | | uint32_t clientHandle, |
195 | | char** preferredLocalesIds, |
196 | | const constants__t_TimestampsToReturn_i timestampToReturn, |
197 | | bool userAccessGranted, |
198 | | const SOPC_Event* pEvent, |
199 | | OpcUa_EventFieldList** ppEventFieldList) |
200 | 0 | { |
201 | 0 | SOPC_ASSERT(NULL != pFilterCtx); |
202 | 0 | SOPC_ASSERT(NULL != pEvent); |
203 | 0 | SOPC_ASSERT(NULL != ppEventFieldList); |
204 | 0 | SOPC_ASSERT(!pFilterCtx->isDataFilter); |
205 | 0 | int32_t nbSelectClauses = pFilterCtx->Filter.Event.eventFilter->NoOfSelectClauses; |
206 | 0 | bool res = nbSelectClauses > 0; |
207 | 0 | bool setField = false; |
208 | 0 | if (res) |
209 | 0 | { |
210 | 0 | res = util_event__alloc_event_field_list(clientHandle, nbSelectClauses, ppEventFieldList); |
211 | 0 | } |
212 | 0 | if (res) |
213 | 0 | { |
214 | 0 | const SOPC_NodeId* eventTypeId = SOPC_Event_GetEventTypeId(pEvent); |
215 | 0 | for (int32_t i = 0; i < nbSelectClauses; i++) |
216 | 0 | { |
217 | 0 | setField = is_event_type_compatible_with_select_clause( |
218 | 0 | &pFilterCtx->Filter.Event.eventFilter->SelectClauses[i], eventTypeId); |
219 | 0 | if (setField) |
220 | 0 | { |
221 | 0 | util_event__set_event_field_list_elt(preferredLocalesIds, timestampToReturn, userAccessGranted, i, |
222 | 0 | pFilterCtx, pEvent, *ppEventFieldList); |
223 | 0 | } |
224 | 0 | } |
225 | 0 | } |
226 | |
|
227 | 0 | return res; |
228 | 0 | } |
229 | | |
230 | | static SOPC_Event* create_queueOverflowEvent(void) |
231 | 0 | { |
232 | 0 | if (NULL == queueOverflowEvent) |
233 | 0 | { |
234 | 0 | SOPC_Event* QOfEvent = |
235 | 0 | SOPC_EventManager_CreateEventInstance(util_event__get_event_types(), &EventQueueOverflowTypeId); |
236 | 0 | if (NULL == QOfEvent) |
237 | 0 | { |
238 | | // Impossible to instantiate the event type |
239 | 0 | return NULL; |
240 | 0 | } |
241 | | /** |
242 | | * From part 5 (v1.05) §6.4.34: |
243 | | * The SourceNode Property for Events of this type shall be assigned to the NodeId of the Server |
244 | | * Object. The SourceName for Events of this type shall be "Internal/EventQueueOverflow". |
245 | | */ |
246 | 0 | SOPC_ReturnStatus status = SOPC_Event_SetSourceNode(QOfEvent, &ServerId); |
247 | 0 | SOPC_String sourceName; |
248 | 0 | SOPC_String_Initialize(&sourceName); |
249 | 0 | if (SOPC_STATUS_OK == status) |
250 | 0 | { |
251 | 0 | status = SOPC_String_AttachFromCstring(&sourceName, "Internal/EventQueueOverflow"); |
252 | 0 | } |
253 | 0 | if (SOPC_STATUS_OK == status) |
254 | 0 | { |
255 | 0 | status = SOPC_Event_SetSourceName(QOfEvent, &sourceName); |
256 | 0 | } |
257 | 0 | if (SOPC_STATUS_OK == status) |
258 | 0 | { |
259 | 0 | queueOverflowEvent = QOfEvent; |
260 | 0 | } |
261 | 0 | else |
262 | 0 | { |
263 | 0 | SOPC_Event_Delete(&QOfEvent); |
264 | 0 | } |
265 | 0 | } |
266 | | |
267 | 0 | SOPC_Event* event = SOPC_Event_CreateCopy(queueOverflowEvent, true); |
268 | 0 | if (NULL == event) |
269 | 0 | { |
270 | 0 | return NULL; |
271 | 0 | } |
272 | | |
273 | 0 | SOPC_ReturnStatus status = SOPC_Event_SetTime(event, SOPC_Time_GetCurrentTimeUTC()); |
274 | | // We still want to generate the created queueOverflowEvent even if it fails |
275 | 0 | SOPC_UNUSED_RESULT(status); |
276 | 0 | return event; |
277 | 0 | } |
278 | | |
279 | | bool util_event__gen_event_queue_overflow_notification(const SOPC_InternalMonitoredItemFilterCtx* pFilterCtx, |
280 | | uint32_t clientHandle, |
281 | | OpcUa_EventFieldList** ppEventFieldList) |
282 | 0 | { |
283 | 0 | SOPC_Event* queueOverflowEventInst = create_queueOverflowEvent(); |
284 | 0 | if (NULL == queueOverflowEventInst) |
285 | 0 | { |
286 | 0 | return false; |
287 | 0 | } |
288 | 0 | bool res = util_event__alloc_and_fill_event_field_list(pFilterCtx, clientHandle, NULL, constants__e_ttr_both, true, |
289 | 0 | queueOverflowEventInst, ppEventFieldList); |
290 | 0 | SOPC_Event_Delete(&queueOverflowEventInst); |
291 | 0 | return res; |
292 | 0 | } |