Coverage Report

Created: 2026-11-12 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541_15/src/client/ua_client_subscriptions.c
Line
Count
Source
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
 *
5
 *    Copyright 2015-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
6
 *    Copyright 2015 (c) Oleksiy Vasylyev
7
 *    Copyright 2016 (c) Sten Grüner
8
 *    Copyright 2017-2018 (c) Thomas Stalder, Blue Time Concept SA
9
 *    Copyright 2016-2017 (c) Florian Palm
10
 *    Copyright 2017 (c) Frank Meerkötter
11
 *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
12
 *    Copyright 2026 (c) o6 Automation GmbH (Author: Julius Pfrommer)
13
 */
14
15
#include <open62541/client_highlevel.h>
16
#include <open62541/client_highlevel_async.h>
17
18
#include "ua_client_internal.h"
19
20
struct UA_Client_MonitoredItem_ForDelete {
21
    UA_Client *client;
22
    UA_Client_Subscription *sub;
23
    UA_UInt32 *monitoredItemId;
24
};
25
26
/*****************/
27
/* Subscriptions */
28
/*****************/
29
30
/* For ZIP_TREE we use clientHandle comparison */
31
static enum ZIP_CMP
32
0
UA_ClientHandle_cmp(const void *a, const void *b) {
33
0
    const UA_Client_MonitoredItem *aa = (const UA_Client_MonitoredItem *)a;
34
0
    const UA_Client_MonitoredItem *bb = (const UA_Client_MonitoredItem *)b;
35
0
    if(aa->parameters.clientHandle < bb->parameters.clientHandle)
36
0
        return ZIP_CMP_LESS;
37
0
    if(aa->parameters.clientHandle > bb->parameters.clientHandle)
38
0
        return ZIP_CMP_MORE;
39
0
    return ZIP_CMP_EQ;
40
0
}
41
42
0
ZIP_FUNCTIONS(MonitorItemsTree, UA_Client_MonitoredItem, zipfields,
Unexecuted instantiation: ua_client_subscriptions.c:MonitorItemsTree_ZIP_INSERT
Unexecuted instantiation: ua_client_subscriptions.c:MonitorItemsTree_ZIP_REMOVE
Unexecuted instantiation: ua_client_subscriptions.c:MonitorItemsTree_ZIP_ITER
43
              UA_Client_MonitoredItem, zipfields, UA_ClientHandle_cmp)
44
45
static UA_Client_MonitoredItem *
46
0
findMonitoredItemByHandle(UA_Client_Subscription *sub, UA_UInt32 clientHandle) {
47
0
    UA_Client_MonitoredItem dummy;
48
0
    memset(&dummy, 0, sizeof(dummy));
49
0
    dummy.parameters.clientHandle = clientHandle;
50
0
    return ZIP_FIND(MonitorItemsTree, &sub->monitoredItems, &dummy);
51
0
}
52
53
static void *
54
0
MonitoredItem_findPendingByHandle(void *data, UA_Client_MonitoredItem *mon) {
55
0
    UA_UInt32 clientHandle = *(UA_UInt32*)data;
56
0
    if(mon->pendingParameters.clientHandle == clientHandle)
57
0
        return mon;
58
0
    return NULL;
59
0
}
60
61
static void
62
MonitoredItem_delete(UA_Client *client, UA_Client_Subscription *sub,
63
                     UA_Client_MonitoredItem *mon);
64
65
static void
66
Subscription_create(UA_Client *client, UA_Client_Subscription *newSub,
67
0
                    UA_CreateSubscriptionResponse *response) {
68
0
    UA_LOCK_ASSERT(&client->clientMutex);
69
70
0
    UA_EventLoop *el = client->config.eventLoop;
71
72
0
    newSub->subscriptionId = response->subscriptionId;
73
0
    newSub->sequenceNumber = 0;
74
0
    newSub->lastActivity = el->dateTime_nowMonotonic(el);
75
0
    newSub->publishingInterval = response->revisedPublishingInterval;
76
0
    newSub->maxKeepAliveCount = response->revisedMaxKeepAliveCount;
77
0
    ZIP_INIT(&newSub->monitoredItems);
78
0
    newSub->pendingRekeys = 0;
79
0
    LIST_INSERT_HEAD(&client->subscriptions, newSub, listEntry);
80
81
    /* Immediately send the first publish requests if there are none
82
     * outstanding */
83
0
    __Client_Subscriptions_backgroundPublish(client);
84
0
}
85
86
static void
87
Subscriptions_create_handler(UA_Client *client, void *data,
88
0
                             UA_UInt32 requestId, void *r) {
89
0
    UA_LOCK_ASSERT(&client->clientMutex);
90
91
0
    UA_CreateSubscriptionResponse *response = (UA_CreateSubscriptionResponse *)r;
92
0
    CustomCallback *cc = (CustomCallback *)data;
93
0
    UA_Client_Subscription *newSub = (UA_Client_Subscription *)cc->clientData;
94
0
    if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
95
0
        UA_free(newSub);
96
0
        goto cleanup;
97
0
    }
98
99
    /* Prepare the internal representation */
100
0
    Subscription_create(client, newSub, response);
101
102
0
cleanup:
103
0
    if(cc->userCallback)
104
0
        cc->userCallback(client, cc->userData, requestId, response);
105
0
    UA_free(cc);
106
0
}
107
108
UA_CreateSubscriptionResponse
109
UA_Client_Subscriptions_create(UA_Client *client,
110
                               const UA_CreateSubscriptionRequest request,
111
                               void *subscriptionContext,
112
                               UA_Client_StatusChangeNotificationCallback statusChangeCallback,
113
0
                               UA_Client_DeleteSubscriptionCallback deleteCallback) {
114
0
    lockClient(client);
115
116
0
    UA_CreateSubscriptionResponse response;
117
0
    UA_Client_Subscription *sub = (UA_Client_Subscription *)
118
0
        UA_malloc(sizeof(UA_Client_Subscription));
119
0
    if(!sub) {
120
0
        UA_CreateSubscriptionResponse_init(&response);
121
0
        response.responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
122
0
        unlockClient(client);
123
0
        return response;
124
0
    }
125
0
    sub->context = subscriptionContext;
126
0
    sub->statusChangeCallback = statusChangeCallback;
127
0
    sub->deleteCallback = deleteCallback;
128
129
    /* Send the request as a synchronous service call */
130
0
    __Client_Service(client, &request, &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONREQUEST],
131
0
                     &response, &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONRESPONSE]);
132
0
    if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
133
0
        UA_free(sub);
134
0
        unlockClient(client);
135
0
        return response;
136
0
    }
137
138
0
    Subscription_create(client, sub, &response);
139
140
0
    unlockClient(client);
141
0
    return response;
142
0
}
143
144
UA_StatusCode
145
UA_Client_Subscriptions_create_async(UA_Client *client, const UA_CreateSubscriptionRequest request,
146
                                     void *subscriptionContext,
147
                                     UA_Client_StatusChangeNotificationCallback statusChangeCallback,
148
                                     UA_Client_DeleteSubscriptionCallback deleteCallback,
149
                                     UA_ClientAsyncCreateSubscriptionCallback createCallback,
150
0
                                     void *userdata, UA_UInt32 *requestId) {
151
0
    CustomCallback *cc = (CustomCallback *)UA_calloc(1, sizeof(CustomCallback));
152
0
    if(!cc)
153
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
154
155
0
    UA_Client_Subscription *sub = (UA_Client_Subscription *)
156
0
        UA_malloc(sizeof(UA_Client_Subscription));
157
0
    if(!sub) {
158
0
        UA_free(cc);
159
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
160
0
    }
161
0
    sub->context = subscriptionContext;
162
0
    sub->statusChangeCallback = statusChangeCallback;
163
0
    sub->deleteCallback = deleteCallback;
164
165
0
    cc->userCallback = (UA_ClientAsyncServiceCallback)createCallback;
166
0
    cc->userData = userdata;
167
0
    cc->clientData = sub;
168
169
    /* Send the request as asynchronous service call */
170
0
    UA_StatusCode res =
171
0
        __UA_Client_AsyncService(client, &request,
172
0
                                 &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONREQUEST],
173
0
                                 Subscriptions_create_handler,
174
0
                                 &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONRESPONSE],
175
0
                                 cc, requestId);
176
0
    if(res != UA_STATUSCODE_GOOD) {
177
0
        UA_free(cc);
178
0
        UA_free(sub);
179
0
    }
180
0
    return res;
181
0
}
182
183
static UA_Client_Subscription *
184
0
findSubscriptionById(const UA_Client *client, UA_UInt32 subscriptionId) {
185
0
    UA_Client_Subscription *sub = NULL;
186
0
    LIST_FOREACH(sub, &client->subscriptions, listEntry) {
187
0
        if(sub->subscriptionId == subscriptionId)
188
0
            break;
189
0
    }
190
0
    return sub;
191
0
}
192
193
static void
194
Subscription_modify(UA_Client *client, UA_Client_Subscription *sub,
195
0
                    const UA_ModifySubscriptionResponse *response) {
196
0
    sub->publishingInterval = response->revisedPublishingInterval;
197
0
    sub->maxKeepAliveCount = response->revisedMaxKeepAliveCount;
198
0
}
199
200
static void
201
Subscription_modify_handler(UA_Client *client, void *data,
202
0
                            UA_UInt32 requestId, void *r) {
203
0
    UA_LOCK_ASSERT(&client->clientMutex);
204
205
0
    UA_ModifySubscriptionResponse *response = (UA_ModifySubscriptionResponse *)r;
206
0
    CustomCallback *cc = (CustomCallback *)data;
207
0
    UA_Client_Subscription *sub =
208
0
        findSubscriptionById(client, (UA_UInt32)(uintptr_t)cc->clientData);
209
0
    if(sub) {
210
0
        Subscription_modify(client, sub, response);
211
0
    } else {
212
0
        UA_LOG_INFO(client->config.logging, UA_LOGCATEGORY_CLIENT,
213
0
                    "No internal representation of subscription %" PRIu32,
214
0
                    (UA_UInt32)(uintptr_t)cc->clientData);
215
0
    }
216
217
0
    if(cc->userCallback)
218
0
        cc->userCallback(client, cc->userData, requestId, response);
219
0
    UA_free(cc);
220
0
}
221
222
UA_StatusCode
223
UA_Client_Subscriptions_getContext(UA_Client *client, UA_UInt32 subscriptionId,
224
0
                                   void **subContext) {
225
0
    if(!client || !subContext)
226
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
227
228
0
    lockClient(client);
229
0
    UA_Client_Subscription *sub = findSubscriptionById(client, subscriptionId);
230
0
    if(!sub) {
231
0
        unlockClient(client);
232
0
        return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
233
0
    }
234
235
0
    *subContext = sub->context;
236
0
    unlockClient(client);
237
0
    return UA_STATUSCODE_GOOD;
238
0
}
239
240
UA_StatusCode
241
UA_Client_Subscriptions_setContext(UA_Client *client, UA_UInt32 subscriptionId,
242
0
                                   void *subContext) {
243
0
    if(!client)
244
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
245
246
0
    lockClient(client);
247
0
    UA_Client_Subscription *sub = findSubscriptionById(client, subscriptionId);
248
0
    if(!sub) {
249
0
        unlockClient(client);
250
0
        return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
251
0
    }
252
253
0
    sub->context = subContext;
254
0
    unlockClient(client);
255
0
    return UA_STATUSCODE_GOOD;
256
0
}
257
258
UA_ModifySubscriptionResponse
259
UA_Client_Subscriptions_modify(UA_Client *client,
260
0
                               const UA_ModifySubscriptionRequest request) {
261
0
    UA_ModifySubscriptionResponse response;
262
0
    UA_ModifySubscriptionResponse_init(&response);
263
264
    /* Find the internal representation */
265
0
    lockClient(client);
266
0
    UA_Client_Subscription *sub = findSubscriptionById(client, request.subscriptionId);
267
0
    if(!sub) {
268
0
        unlockClient(client);
269
0
        response.responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
270
0
        return response;
271
0
    }
272
273
    /* Call the service */
274
0
    __Client_Service(client,
275
0
                     &request, &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONREQUEST],
276
0
                     &response, &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE]);
277
278
    /* Adjust the internal representation. Lookup again for thread-safety. */
279
0
    sub = findSubscriptionById(client, request.subscriptionId);
280
0
    if(!sub) {
281
0
        response.responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
282
0
        unlockClient(client);
283
0
        return response;
284
0
    }
285
0
    Subscription_modify(client, sub, &response);
286
0
    unlockClient(client);
287
0
    return response;
288
0
}
289
290
UA_StatusCode
291
UA_Client_Subscriptions_modify_async(UA_Client *client,
292
                                     const UA_ModifySubscriptionRequest request,
293
                                     UA_ClientAsyncModifySubscriptionCallback callback,
294
0
                                     void *userdata, UA_UInt32 *requestId) {
295
0
    lockClient(client);
296
297
0
    UA_Client_Subscription *sub = findSubscriptionById(client, request.subscriptionId);
298
0
    if(!sub) {
299
0
        unlockClient(client);
300
0
        return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
301
0
    }
302
303
0
    CustomCallback *cc = (CustomCallback *)UA_calloc(1, sizeof(CustomCallback));
304
0
    if(!cc) {
305
0
        unlockClient(client);
306
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
307
0
    }
308
309
0
    cc->clientData = (void *)(uintptr_t)request.subscriptionId;
310
0
    cc->userData = userdata;
311
0
    cc->userCallback = (UA_ClientAsyncServiceCallback)callback;
312
313
0
    UA_StatusCode res =
314
0
        __Client_AsyncService(client, &request,
315
0
                              &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONREQUEST],
316
0
                              Subscription_modify_handler,
317
0
                              &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE],
318
0
                              cc, requestId);
319
320
0
    unlockClient(client);
321
0
    return res;
322
0
}
323
324
static void *
325
0
MonitoredItem_delete_wrapper(void *data, UA_Client_MonitoredItem *mon) {
326
0
    struct UA_Client_MonitoredItem_ForDelete *deleteMonitoredItem =
327
0
        (struct UA_Client_MonitoredItem_ForDelete *)data;
328
0
    if(deleteMonitoredItem != NULL) {
329
0
        if(deleteMonitoredItem->monitoredItemId != NULL &&
330
0
           (mon->monitoredItemId != *deleteMonitoredItem->monitoredItemId)) {
331
0
            return NULL;
332
0
        }
333
0
        MonitoredItem_delete(deleteMonitoredItem->client, deleteMonitoredItem->sub, mon);
334
        /* A targeted deletion can remove the entire Subscription from the
335
         * callback. Stop the tree walk before it visits saved child pointers. */
336
0
        if(deleteMonitoredItem->monitoredItemId)
337
0
            return deleteMonitoredItem;
338
0
    }
339
0
    return NULL;
340
0
}
341
342
static void
343
__Client_Subscription_deleteInternal(UA_Client *client,
344
0
                                     UA_Client_Subscription *sub) {
345
    /* Mark the Subscription as removed before user callbacks can re-enter. */
346
0
    LIST_REMOVE(sub, listEntry);
347
348
    /* Remove the MonitoredItems */
349
0
    struct UA_Client_MonitoredItem_ForDelete deleteMonitoredItem;
350
0
    memset(&deleteMonitoredItem, 0, sizeof(struct UA_Client_MonitoredItem_ForDelete));
351
0
    deleteMonitoredItem.client = client;
352
0
    deleteMonitoredItem.sub = sub;
353
0
    ZIP_ITER(MonitorItemsTree, &sub->monitoredItems,
354
0
             MonitoredItem_delete_wrapper, &deleteMonitoredItem);
355
356
    /* Call the delete callback */
357
0
    if(sub->deleteCallback) {
358
0
        void *subC = sub->context;
359
0
        UA_UInt32 subId = sub->subscriptionId;
360
0
        sub->deleteCallback(client, subId, subC);
361
0
    }
362
363
0
    UA_free(sub);
364
0
}
365
366
static void
367
__Client_Subscription_processDelete(UA_Client *client,
368
                                    const UA_DeleteSubscriptionsRequest *request,
369
0
                                    const UA_DeleteSubscriptionsResponse *response)  {
370
0
    if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD)
371
0
        return;
372
373
    /* Check that the request and response size -- use the same index for both */
374
0
    if(request->subscriptionIdsSize != response->resultsSize)
375
0
        return;
376
377
0
    for(size_t i = 0; i < request->subscriptionIdsSize; i++) {
378
0
        if(response->results[i] != UA_STATUSCODE_GOOD &&
379
0
           response->results[i] != UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID)
380
0
            continue;
381
382
        /* Get the Subscription */
383
0
        UA_Client_Subscription *sub =
384
0
            findSubscriptionById(client, request->subscriptionIds[i]);
385
0
        if(!sub) {
386
0
            UA_LOG_INFO(client->config.logging, UA_LOGCATEGORY_CLIENT,
387
0
                        "No internal representation of subscription %" PRIu32,
388
0
                        request->subscriptionIds[i]);
389
0
            continue;
390
0
        }
391
392
        /* Delete the Subscription */
393
0
        __Client_Subscription_deleteInternal(client, sub);
394
0
    }
395
0
}
396
397
typedef struct {
398
    UA_DeleteSubscriptionsRequest request;
399
    UA_ClientAsyncServiceCallback userCallback;
400
    void *userData;
401
} DeleteSubscriptionCallback;
402
403
static void
404
Subscriptions_delete_handler(UA_Client *client, void *data,
405
0
                             UA_UInt32 requestId, void *r) {
406
0
    UA_DeleteSubscriptionsResponse *response =
407
0
        (UA_DeleteSubscriptionsResponse *)r;
408
0
    DeleteSubscriptionCallback *dsc =
409
0
        (DeleteSubscriptionCallback*)data;
410
411
0
    lockClient(client);
412
413
    /* Delete */
414
0
    __Client_Subscription_processDelete(client, &dsc->request, response);
415
416
    /* Userland Callback */
417
0
    if(dsc->userCallback)
418
0
        dsc->userCallback(client, dsc->userData, requestId, response);
419
420
    /* Cleanup */
421
0
    UA_DeleteSubscriptionsRequest_clear(&dsc->request);
422
0
    UA_free(dsc);
423
424
0
    unlockClient(client);
425
0
}
426
427
UA_StatusCode
428
UA_Client_Subscriptions_delete_async(UA_Client *client,
429
                                     const UA_DeleteSubscriptionsRequest request,
430
                                     UA_ClientAsyncDeleteSubscriptionsCallback callback,
431
0
                                     void *userdata, UA_UInt32 *requestId) {
432
    /* Make a copy of the request that persists into the async callback */
433
0
    DeleteSubscriptionCallback *dsc = (DeleteSubscriptionCallback*)
434
0
        UA_malloc(sizeof(DeleteSubscriptionCallback));
435
0
    if(!dsc)
436
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
437
0
    dsc->userCallback = (UA_ClientAsyncServiceCallback)callback;
438
0
    dsc->userData = userdata;
439
0
    UA_StatusCode res = UA_DeleteSubscriptionsRequest_copy(&request, &dsc->request);
440
0
    if(res != UA_STATUSCODE_GOOD) {
441
0
        UA_free(dsc);
442
0
        return res;
443
0
    }
444
445
    /* Make the async call */
446
0
    res = __UA_Client_AsyncService(client, &request,
447
0
                                   &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST],
448
0
                                   Subscriptions_delete_handler,
449
0
                                   &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSRESPONSE],
450
0
                                   dsc, requestId);
451
0
    if(res != UA_STATUSCODE_GOOD) {
452
0
        UA_DeleteSubscriptionsRequest_clear(&dsc->request);
453
0
        UA_free(dsc);
454
0
    }
455
0
    return res;
456
0
}
457
458
UA_DeleteSubscriptionsResponse
459
UA_Client_Subscriptions_delete(UA_Client *client,
460
0
                               const UA_DeleteSubscriptionsRequest request) {
461
0
    lockClient(client);
462
463
    /* Send the request */
464
0
    UA_DeleteSubscriptionsResponse response;
465
0
    __Client_Service(client, &request,
466
0
                     &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST],
467
0
                     &response, &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSRESPONSE]);
468
469
    /* Process */
470
0
    __Client_Subscription_processDelete(client, &request, &response);
471
472
0
    unlockClient(client);
473
0
    return response;
474
0
}
475
476
UA_StatusCode
477
0
UA_Client_Subscriptions_deleteSingle(UA_Client *client, UA_UInt32 subscriptionId) {
478
0
    UA_DeleteSubscriptionsRequest request;
479
0
    UA_DeleteSubscriptionsRequest_init(&request);
480
0
    request.subscriptionIds = &subscriptionId;
481
0
    request.subscriptionIdsSize = 1;
482
483
0
    UA_DeleteSubscriptionsResponse response =
484
0
        UA_Client_Subscriptions_delete(client, request);
485
486
0
    UA_StatusCode retval = response.responseHeader.serviceResult;
487
0
    if(retval != UA_STATUSCODE_GOOD) {
488
0
        UA_DeleteSubscriptionsResponse_clear(&response);
489
0
        return retval;
490
0
    }
491
492
0
    if(response.resultsSize != 1) {
493
0
        UA_DeleteSubscriptionsResponse_clear(&response);
494
0
        return UA_STATUSCODE_BADINTERNALERROR;
495
0
    }
496
497
0
    retval = response.results[0];
498
0
    UA_DeleteSubscriptionsResponse_clear(&response);
499
0
    return retval;
500
0
}
501
502
/******************/
503
/* MonitoredItems */
504
/******************/
505
506
static void
507
0
EventFields_clear(UA_KeyValueMap *eventFields) {
508
    /* The values are borrowed from the PublishResponse. Detach them before
509
     * clearing the map-owned field-name keys. */
510
0
    for(size_t i = 0; i < eventFields->mapSize; i++)
511
0
        UA_Variant_init(&eventFields->map[i].value);
512
0
    UA_KeyValueMap_clear(eventFields);
513
0
}
514
515
static void
516
MonitoredItem_delete(UA_Client *client, UA_Client_Subscription *sub,
517
0
                     UA_Client_MonitoredItem *mon) {
518
0
    UA_LOCK_ASSERT(&client->clientMutex);
519
520
0
    ZIP_REMOVE(MonitorItemsTree, &sub->monitoredItems, mon);
521
0
    UA_UInt32 subId = sub->subscriptionId;
522
0
    void *subContext = sub->context;
523
0
    if(mon->pendingParameters.clientHandle != 0)
524
0
        sub->pendingRekeys--;
525
0
    if(mon->deleteCallback)
526
0
        mon->deleteCallback(client, subId, subContext,
527
0
                            mon->monitoredItemId, mon->context);
528
0
    EventFields_clear(&mon->eventFields);
529
0
    UA_MonitoringParameters_clear(&mon->parameters);
530
0
    UA_MonitoringParameters_clear(&mon->pendingParameters);
531
0
    UA_free(mon);
532
0
}
533
534
static UA_StatusCode
535
prepareEventFieldsMap(UA_KeyValueMap *eventFields,
536
0
                      UA_MonitoringParameters *params) {
537
    /* Get the EventFilter */
538
0
    UA_ExtensionObject *eo = &params->filter;
539
0
    if(eo->content.decoded.type != &UA_TYPES[UA_TYPES_EVENTFILTER])
540
0
        return UA_STATUSCODE_GOOD;
541
0
    UA_EventFilter *ef = (UA_EventFilter*)eo->content.decoded.data;
542
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
543
544
    /* Check whether there are fields */
545
0
    if(ef->selectClausesSize == 0)
546
0
        return UA_STATUSCODE_GOOD;
547
548
    /* Allocate the map */
549
0
    eventFields->map = (UA_KeyValuePair*)
550
0
        UA_calloc(ef->selectClausesSize, sizeof(UA_KeyValuePair));
551
0
    if(!eventFields->map)
552
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
553
0
    eventFields->mapSize = ef->selectClausesSize;
554
555
    /* Create the key-strings for the fields */
556
0
    for(size_t i = 0; i < eventFields->mapSize; i++) {
557
0
        res |= UA_SimpleAttributeOperand_print(&ef->selectClauses[i],
558
0
                                               &eventFields->map[i].key.name);
559
0
    }
560
561
0
    return res;
562
0
}
563
564
static UA_StatusCode
565
MonitoredItem_createBegin(UA_Client *client, UA_Client_Subscription *sub,
566
                          UA_MonitoredItemCreateRequest *item,
567
                          UA_Client_DeleteMonitoredItemCallback deleteCallback,
568
                          void *context, void *handlingCallback,
569
0
                          UA_Client_MonitoredItem **outMon) {
570
    /* Allocate MonitoredItem */
571
0
    UA_Client_MonitoredItem *mon = (UA_Client_MonitoredItem *)
572
0
        UA_calloc(1, sizeof(UA_Client_MonitoredItem));
573
0
    if(!mon)
574
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
575
576
    /* Set a unique ClientHandle and retain the active parameters. */
577
0
    item->requestedParameters.clientHandle = ++client->monitoredItemHandles;
578
0
    UA_StatusCode res =
579
0
        UA_MonitoringParameters_copy(&item->requestedParameters,
580
0
                                     &mon->parameters);
581
0
    if(res != UA_STATUSCODE_GOOD) {
582
0
        UA_free(mon);
583
0
        return res;
584
0
    }
585
586
0
    mon->isEventMonitoredItem =
587
0
        (item->itemToMonitor.attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER);
588
589
    /* Fill in members and add to the client  */
590
0
    mon->context = context;
591
0
    mon->deleteCallback = deleteCallback;
592
0
    mon->handler.dataChangeCallback =
593
0
        (UA_Client_DataChangeNotificationCallback)(uintptr_t)handlingCallback;
594
0
    ZIP_INSERT(MonitorItemsTree, &sub->monitoredItems, mon);
595
596
0
    UA_LOG_DEBUG(client->config.logging, UA_LOGCATEGORY_CLIENT,
597
0
                 "Subscription %" PRIu32 " | Added a MonitoredItem with handle %" PRIu32,
598
0
                 sub->subscriptionId, mon->parameters.clientHandle);
599
600
0
    *outMon = mon;
601
0
    return UA_STATUSCODE_GOOD;
602
0
}
603
604
static void
605
MonitoredItem_createFinish(UA_Client *client, UA_Client_Subscription *sub,
606
                           UA_Client_MonitoredItem *mon,
607
0
                           UA_MonitoredItemCreateResult *result) {
608
0
    UA_assert(result->statusCode == UA_STATUSCODE_GOOD);
609
610
0
    mon->monitoredItemId = result->monitoredItemId;
611
    /* revisedSamplingInterval; */
612
    /* revisedQueueSize; */
613
    /* filterResult; */
614
0
}
615
616
/************************************/
617
/* CreateMonitoredItems Synchronous */
618
/************************************/
619
620
static void
621
Client_MonitoredItems_create(UA_Client *client,
622
                             const UA_CreateMonitoredItemsRequest *constRequest,
623
                             void **contexts, void **handlingCallbacks,
624
                             UA_Client_DeleteMonitoredItemCallback *deleteCallbacks,
625
0
                             UA_CreateMonitoredItemsResponse *response) {
626
0
    UA_LOCK_ASSERT(&client->clientMutex);
627
0
    UA_CreateMonitoredItemsResponse_init(response);
628
629
    /* Any items? */
630
0
    if(constRequest->itemsToCreateSize == 0) {
631
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
632
0
        return;
633
0
    }
634
635
    /* Get the subscription */
636
0
    UA_Client_Subscription *sub =
637
0
        findSubscriptionById(client, constRequest->subscriptionId);
638
0
    if(!sub) {
639
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
640
0
        return;
641
0
    }
642
643
    /* Make a mutable copy. We modify the request to set the internal
644
     * clientHandle. */
645
0
    UA_CreateMonitoredItemsRequest request;
646
0
    UA_StatusCode res =
647
0
        UA_CreateMonitoredItemsRequest_copy(constRequest, &request);
648
0
    if(res != UA_STATUSCODE_GOOD) {
649
0
        response->responseHeader.serviceResult = res;
650
0
        return;
651
0
    }
652
653
    /* Create the MonitoredItems */
654
0
    UA_STACKARRAY(UA_Client_MonitoredItem*, mons, request.itemsToCreateSize);
655
0
    memset(mons, 0, sizeof(UA_Client_MonitoredItem*) * request.itemsToCreateSize);
656
0
    for(size_t i = 0; i < request.itemsToCreateSize; i++) {
657
0
        void *context = (contexts) ? contexts[i] : NULL;
658
0
        void *handlingCallback = (handlingCallbacks) ? handlingCallbacks[i] : NULL;
659
0
        UA_Client_DeleteMonitoredItemCallback deleteCallback =
660
0
            (deleteCallbacks) ? deleteCallbacks[i] : NULL;
661
0
        res |= MonitoredItem_createBegin(client, sub, &request.itemsToCreate[i],
662
0
                                         deleteCallback, context,
663
0
                                         handlingCallback, &mons[i]);
664
0
    }
665
666
    /* Failure -> Delete created MonitoredItems. Directly call deleteCallback if
667
     * creation failed. The MonitoredItemId is not yet known, use zero. */
668
0
    if(res != UA_STATUSCODE_GOOD) {
669
0
        void *subContext = sub->context;
670
0
        for(size_t i = 0; i < request.itemsToCreateSize; i++) {
671
0
            if(mons[i]) {
672
0
                if(findSubscriptionById(client, request.subscriptionId) != sub)
673
0
                    continue;
674
0
                UA_Client_MonitoredItem *mon = findMonitoredItemByHandle(sub,
675
0
                    request.itemsToCreate[i].requestedParameters.clientHandle);
676
0
                if(mon)
677
0
                    MonitoredItem_delete(client, sub, mon);
678
0
            } else if(deleteCallbacks && deleteCallbacks[i])
679
0
                deleteCallbacks[i](client, request.subscriptionId,
680
0
                                   subContext, 0, contexts ? contexts[i] : NULL);
681
0
        }
682
0
        UA_CreateMonitoredItemsRequest_clear(&request);
683
0
        response->responseHeader.serviceResult = res;
684
0
        return;
685
0
    }
686
687
    /* Call the service */
688
0
    __Client_Service(client, &request,
689
0
                     &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSREQUEST],
690
0
                     response, &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSRESPONSE]);
691
692
    /* Check that the response size is good */
693
0
    if(response->responseHeader.serviceResult == UA_STATUSCODE_GOOD &&
694
0
       response->resultsSize != request.itemsToCreateSize)
695
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR;
696
697
    /* The Subscription and its MonitoredItems are removed if the Session was
698
     * closed while waiting for the response. Look them up again. */
699
0
    sub = findSubscriptionById(client, request.subscriptionId);
700
0
    if(!sub && response->responseHeader.serviceResult == UA_STATUSCODE_GOOD)
701
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
702
703
    /* Update the MonitoredItems */
704
0
    for(size_t i = 0; sub && i < request.itemsToCreateSize; i++) {
705
0
        if(findSubscriptionById(client, request.subscriptionId) != sub)
706
0
            break;
707
0
        UA_Client_MonitoredItem *mon = findMonitoredItemByHandle(sub,
708
0
            request.itemsToCreate[i].requestedParameters.clientHandle);
709
0
        if(!mon)
710
0
            continue;
711
0
        if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
712
0
            MonitoredItem_delete(client, sub, mon);
713
0
            continue;
714
0
        }
715
0
        UA_MonitoredItemCreateResult *item = &response->results[i];
716
0
        if(item->statusCode != UA_STATUSCODE_GOOD) {
717
0
            MonitoredItem_delete(client, sub, mon);
718
0
            continue;
719
0
        }
720
0
        MonitoredItem_createFinish(client, sub, mon, item);
721
0
    }
722
723
0
    UA_CreateMonitoredItemsRequest_clear(&request);
724
0
}
725
726
UA_CreateMonitoredItemsResponse
727
UA_Client_MonitoredItems_createDataChanges(UA_Client *client,
728
                                           const UA_CreateMonitoredItemsRequest request,
729
                                           void **contexts,
730
                                           UA_Client_DataChangeNotificationCallback *callbacks,
731
0
                                           UA_Client_DeleteMonitoredItemCallback *deleteCallbacks) {
732
0
    UA_CreateMonitoredItemsResponse response;
733
0
    lockClient(client);
734
0
    Client_MonitoredItems_create(client, &request, contexts, (void **)callbacks,
735
0
                                 deleteCallbacks, &response);
736
0
    unlockClient(client);
737
0
    return response;
738
0
}
739
740
UA_MonitoredItemCreateResult
741
UA_Client_MonitoredItems_createDataChange(UA_Client *client, UA_UInt32 subscriptionId,
742
                                          UA_TimestampsToReturn timestampsToReturn,
743
                                          const UA_MonitoredItemCreateRequest item,
744
                                          void *context,
745
                                          UA_Client_DataChangeNotificationCallback callback,
746
0
                                          UA_Client_DeleteMonitoredItemCallback deleteCallback) {
747
0
    UA_CreateMonitoredItemsRequest request;
748
0
    UA_CreateMonitoredItemsRequest_init(&request);
749
0
    request.subscriptionId = subscriptionId;
750
0
    request.timestampsToReturn = timestampsToReturn;
751
0
    request.itemsToCreate = (UA_MonitoredItemCreateRequest*)(uintptr_t)&item;
752
0
    request.itemsToCreateSize = 1;
753
0
    UA_CreateMonitoredItemsResponse response =
754
0
       UA_Client_MonitoredItems_createDataChanges(client, request, &context,
755
0
                                                   &callback, &deleteCallback);
756
0
    UA_MonitoredItemCreateResult result;
757
0
    UA_MonitoredItemCreateResult_init(&result);
758
0
    if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
759
0
        result.statusCode = response.responseHeader.serviceResult;
760
0
    if(result.statusCode == UA_STATUSCODE_GOOD &&
761
0
       response.resultsSize != 1)
762
0
        result.statusCode = UA_STATUSCODE_BADINTERNALERROR;
763
0
    if(result.statusCode == UA_STATUSCODE_GOOD) {
764
0
        result = response.results[0];
765
0
        UA_MonitoredItemCreateResult_init(&response.results[0]);
766
0
    }
767
0
    UA_CreateMonitoredItemsResponse_clear(&response);
768
0
    return result;
769
0
}
770
771
UA_CreateMonitoredItemsResponse
772
UA_Client_MonitoredItems_createEvents(UA_Client *client,
773
                                      const UA_CreateMonitoredItemsRequest request,
774
                                      void **contexts,
775
                                      UA_Client_EventNotificationCallback *callback,
776
0
                                      UA_Client_DeleteMonitoredItemCallback *deleteCallback) {
777
0
    UA_CreateMonitoredItemsResponse response;
778
0
    lockClient(client);
779
0
    Client_MonitoredItems_create(client, &request, contexts, (void **)callback,
780
0
                                 deleteCallback, &response);
781
0
    unlockClient(client);
782
0
    return response;
783
0
}
784
785
UA_MonitoredItemCreateResult
786
UA_Client_MonitoredItems_createEvent(UA_Client *client, UA_UInt32 subscriptionId,
787
                                     UA_TimestampsToReturn timestampsToReturn,
788
                                     const UA_MonitoredItemCreateRequest item, void *context,
789
                                     UA_Client_EventNotificationCallback callback,
790
0
                                     UA_Client_DeleteMonitoredItemCallback deleteCallback) {
791
0
    UA_CreateMonitoredItemsRequest request;
792
0
    UA_CreateMonitoredItemsRequest_init(&request);
793
0
    request.subscriptionId = subscriptionId;
794
0
    request.timestampsToReturn = timestampsToReturn;
795
0
    request.itemsToCreate = (UA_MonitoredItemCreateRequest*)(uintptr_t)&item;
796
0
    request.itemsToCreateSize = 1;
797
0
    UA_CreateMonitoredItemsResponse response =
798
0
       UA_Client_MonitoredItems_createEvents(client, request, &context,
799
0
                                             &callback, &deleteCallback);
800
0
    UA_MonitoredItemCreateResult result;
801
0
    UA_MonitoredItemCreateResult_init(&result);
802
0
    if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
803
0
        result.statusCode = response.responseHeader.serviceResult;
804
0
    if(result.statusCode == UA_STATUSCODE_GOOD &&
805
0
       response.resultsSize != 1)
806
0
        result.statusCode = UA_STATUSCODE_BADINTERNALERROR;
807
0
    if(result.statusCode == UA_STATUSCODE_GOOD) {
808
0
        result = response.results[0];
809
0
        UA_MonitoredItemCreateResult_init(&response.results[0]);
810
0
    }
811
0
    UA_CreateMonitoredItemsResponse_clear(&response);
812
0
    return result;
813
0
}
814
815
/*************************************/
816
/* CreateMonitoredItems Asynchronous */
817
/*************************************/
818
819
/* The handles are an array of [subId, monSize, monHandleId1, monHandle2, ...] */
820
static void
821
MonitoredItems_create_async_handler(UA_Client *client, void *data,
822
0
                                    UA_UInt32 requestId, void *resp) {
823
0
    CustomCallback *cc = (CustomCallback*)data;
824
0
    UA_UInt32 *handles = (UA_UInt32*)cc->clientData;
825
0
    UA_CreateMonitoredItemsResponse *response =
826
0
        (UA_CreateMonitoredItemsResponse *)resp;
827
828
0
    lockClient(client);
829
830
    /* Extract the first elements from the handles */
831
0
    UA_UInt32 subId = handles[0];
832
0
    UA_UInt32 monSize = handles[1];
833
0
    UA_UInt32 *monHandles = handles + 2;
834
835
    /* Check that the response size is good */
836
0
    if(response->responseHeader.serviceResult == UA_STATUSCODE_GOOD &&
837
0
       response->resultsSize != monSize)
838
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR;
839
840
    /* Get the Subscription from the SubscriptionId */
841
0
    UA_Client_Subscription *sub = findSubscriptionById(client, subId);
842
0
    if(!sub)
843
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
844
845
    /* Update the MonitoredItems */
846
0
    for(size_t i = 0; sub && i < monSize; i++) {
847
0
        if(findSubscriptionById(client, subId) != sub)
848
0
            break;
849
        /* Get the MonitoredItem from the ClientHandle */
850
0
        UA_Client_MonitoredItem *mon =
851
0
            findMonitoredItemByHandle(sub, monHandles[i]);
852
0
        if(!mon)
853
0
            continue;
854
855
        /* Delete MonitoredItem if the creation failed */
856
0
        if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
857
0
            MonitoredItem_delete(client, sub, mon);
858
0
            continue;
859
0
        }
860
0
        UA_MonitoredItemCreateResult *item = &response->results[i];
861
0
        if(item->statusCode != UA_STATUSCODE_GOOD) {
862
0
            MonitoredItem_delete(client, sub, mon);
863
0
            continue;
864
0
        }
865
866
        /* Update the MonitoredItem with the server's response  */
867
0
        MonitoredItem_createFinish(client, sub, mon, item);
868
0
    }
869
870
    /* Notify the application */
871
0
    if(cc->userCallback) {
872
0
        UA_ClientAsyncCreateMonitoredItemsCallback cb =
873
0
            (UA_ClientAsyncCreateMonitoredItemsCallback)cc->userCallback;
874
0
        cb(client, cc->userData, requestId, response);
875
0
    }
876
877
    /* Clean up */
878
0
    UA_free(handles);
879
0
    UA_free(cc);
880
881
0
    unlockClient(client);
882
0
}
883
884
static UA_StatusCode
885
Client_MonitoredItems_createAsync(UA_Client *client,
886
                                  const UA_CreateMonitoredItemsRequest *constRequest,
887
                                  void **contexts, void **handlingCallbacks,
888
                                  UA_Client_DeleteMonitoredItemCallback *deleteCallbacks,
889
                                  UA_ClientAsyncServiceCallback createCallback,
890
0
                                  void *userdata, UA_UInt32 *requestId) {
891
0
    UA_LOCK_ASSERT(&client->clientMutex);
892
893
    /* Get the Subscription */
894
0
    UA_Client_Subscription *sub =
895
0
        findSubscriptionById(client, constRequest->subscriptionId);
896
0
    if(!sub)
897
0
        return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
898
899
    /* Any items? */
900
0
    if(constRequest->itemsToCreateSize == 0)
901
0
        return UA_STATUSCODE_BADNOTHINGTODO;
902
903
    /* Make a mutable copy. We modify the request to set the internal
904
     * clientHandle. */
905
0
    UA_CreateMonitoredItemsRequest request;
906
0
    UA_StatusCode res =
907
0
        UA_CreateMonitoredItemsRequest_copy(constRequest, &request);
908
0
    if(res != UA_STATUSCODE_GOOD)
909
0
        return res;
910
911
    /* Allocate context for the async handling */
912
0
    CustomCallback *cc = (CustomCallback*)UA_calloc(1, sizeof(CustomCallback));
913
0
    if(!cc) {
914
0
        UA_CreateMonitoredItemsRequest_clear(&request);
915
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
916
0
    }
917
0
    UA_UInt32 *handles = (UA_UInt32*)
918
0
        UA_malloc(sizeof(UA_UInt32) * (request.itemsToCreateSize + 2));
919
0
    if(!handles) {
920
0
        UA_free(cc);
921
0
        UA_CreateMonitoredItemsRequest_clear(&request);
922
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
923
0
    }
924
925
    /* Create the MonitoredItems locally */
926
0
    UA_STACKARRAY(UA_Client_MonitoredItem*, mons, request.itemsToCreateSize);
927
0
    memset(mons, 0, sizeof(UA_Client_MonitoredItem*) * request.itemsToCreateSize);
928
0
    for(size_t i = 0; i < request.itemsToCreateSize; i++) {
929
0
        void *context = (contexts) ? contexts[i] : NULL;
930
0
        void *handlingCallback = (handlingCallbacks) ? handlingCallbacks[i] : NULL;
931
0
        UA_Client_DeleteMonitoredItemCallback deleteCallback =
932
0
            (deleteCallbacks) ? deleteCallbacks[i] : NULL;
933
0
        res |= MonitoredItem_createBegin(client, sub, &request.itemsToCreate[i],
934
0
                                         deleteCallback, context,
935
0
                                         handlingCallback, &mons[i]);
936
0
    }
937
938
    /* Failure -> Delete created MonitoredItems. Directly call deleteCallback if
939
     * creation failed. The MonitoredItemId is not yet known, use zero. */
940
0
    if(res != UA_STATUSCODE_GOOD) {
941
0
        void *subContext = sub->context;
942
0
        for(size_t i = 0; i < request.itemsToCreateSize; i++) {
943
0
            if(mons[i]) {
944
0
                if(findSubscriptionById(client, request.subscriptionId) != sub)
945
0
                    continue;
946
0
                UA_Client_MonitoredItem *mon = findMonitoredItemByHandle(sub,
947
0
                    request.itemsToCreate[i].requestedParameters.clientHandle);
948
0
                if(mon)
949
0
                    MonitoredItem_delete(client, sub, mon);
950
0
            } else if(deleteCallbacks && deleteCallbacks[i])
951
0
                deleteCallbacks[i](client, request.subscriptionId,
952
0
                                   subContext, 0, contexts ? contexts[i] : NULL);
953
0
        }
954
0
        UA_free(handles);
955
0
        UA_free(cc);
956
0
        UA_CreateMonitoredItemsRequest_clear(&request);
957
0
        return res;
958
0
    }
959
960
    /* Set the async handler context */
961
0
    handles[0] = sub->subscriptionId;
962
0
    handles[1] = (UA_UInt32)request.itemsToCreateSize;
963
0
    for(size_t i = 0; i < request.itemsToCreateSize; i++) {
964
0
        handles[i+2] = mons[i]->parameters.clientHandle;
965
0
    }
966
0
    cc->clientData = handles;
967
0
    cc->userCallback = createCallback;
968
0
    cc->userData = userdata;
969
970
    /* Call the service asynchronously */
971
0
    res = __Client_AsyncService(client, &request,
972
0
                                &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSREQUEST],
973
0
                                MonitoredItems_create_async_handler,
974
0
                                &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSRESPONSE],
975
0
                                cc, requestId);
976
977
    /* Manually clean up the context in the failure case */
978
0
    if(res != UA_STATUSCODE_GOOD) {
979
0
        for(size_t i = 0; i < request.itemsToCreateSize; i++) {
980
0
            if(findSubscriptionById(client, request.subscriptionId) != sub)
981
0
                break;
982
0
            UA_Client_MonitoredItem *mon = findMonitoredItemByHandle(sub,
983
0
                request.itemsToCreate[i].requestedParameters.clientHandle);
984
0
            if(mon)
985
0
                MonitoredItem_delete(client, sub, mon);
986
0
        }
987
0
        UA_free(handles);
988
0
        UA_free(cc);
989
0
    }
990
991
    /* Clean up */
992
0
    UA_CreateMonitoredItemsRequest_clear(&request);
993
0
    return res;
994
0
}
995
996
UA_StatusCode
997
UA_Client_MonitoredItems_createDataChanges_async(UA_Client *client,
998
                                                 const UA_CreateMonitoredItemsRequest request,
999
                                                 void **contexts,
1000
                                                 UA_Client_DataChangeNotificationCallback *callbacks,
1001
                                                 UA_Client_DeleteMonitoredItemCallback *deleteCallbacks,
1002
                                                 UA_ClientAsyncCreateMonitoredItemsCallback createCallback,
1003
0
                                                 void *userdata, UA_UInt32 *requestId) {
1004
0
    lockClient(client);
1005
0
    UA_StatusCode res =
1006
0
        Client_MonitoredItems_createAsync(client, &request, contexts,
1007
0
                                          (void **)callbacks, deleteCallbacks,
1008
0
                                          (UA_ClientAsyncServiceCallback)createCallback,
1009
0
                                          userdata, requestId);
1010
0
    unlockClient(client);
1011
0
    return res;
1012
0
}
1013
1014
UA_StatusCode
1015
UA_Client_MonitoredItems_createEvents_async(UA_Client *client,
1016
                                            const UA_CreateMonitoredItemsRequest request,
1017
                                            void **contexts,
1018
                                            UA_Client_EventNotificationCallback *callbacks,
1019
                                            UA_Client_DeleteMonitoredItemCallback *deleteCallbacks,
1020
                                            UA_ClientAsyncCreateMonitoredItemsCallback createCallback,
1021
0
                                            void *userdata, UA_UInt32 *requestId) {
1022
0
    lockClient(client);
1023
0
    UA_StatusCode res =
1024
0
        Client_MonitoredItems_createAsync(client, &request, contexts,
1025
0
                                          (void **)callbacks, deleteCallbacks,
1026
0
                                          (UA_ClientAsyncServiceCallback)createCallback,
1027
0
                                          userdata, requestId);
1028
0
    unlockClient(client);
1029
0
    return res;
1030
0
}
1031
1032
static void
1033
MonitoredItems_delete(UA_Client *client, UA_Client_Subscription *sub,
1034
                      const UA_DeleteMonitoredItemsRequest *request,
1035
0
                      const UA_DeleteMonitoredItemsResponse *response) {
1036
#ifdef __clang_analyzer__
1037
    return;
1038
#endif
1039
1040
    /* Check that the request and response size match, use the same index for both */
1041
0
    if(request->monitoredItemIdsSize != response->resultsSize)
1042
0
        return;
1043
1044
    /* Loop over deleted MonitoredItems */
1045
0
    struct UA_Client_MonitoredItem_ForDelete deleteMonitoredItem;
1046
0
    memset(&deleteMonitoredItem, 0, sizeof(struct UA_Client_MonitoredItem_ForDelete));
1047
0
    deleteMonitoredItem.client = client;
1048
0
    deleteMonitoredItem.sub = sub;
1049
0
    UA_UInt32 subId = sub->subscriptionId;
1050
1051
0
    for(size_t i = 0; i < request->monitoredItemIdsSize; i++) {
1052
0
        if(response->results[i] != UA_STATUSCODE_GOOD &&
1053
0
           response->results[i] != UA_STATUSCODE_BADMONITOREDITEMIDINVALID) {
1054
0
            continue;
1055
0
        }
1056
0
        deleteMonitoredItem.monitoredItemId = &request->monitoredItemIds[i];
1057
        /* Delete the internal representation */
1058
0
        ZIP_ITER(MonitorItemsTree,&sub->monitoredItems,
1059
0
                 MonitoredItem_delete_wrapper, &deleteMonitoredItem);
1060
0
        if(findSubscriptionById(client, subId) != sub)
1061
0
            return;
1062
0
    }
1063
0
}
1064
1065
static void
1066
0
MonitoredItems_delete_handler(UA_Client *client, void *d, UA_UInt32 requestId, void *r) {
1067
0
    UA_Client_Subscription *sub = NULL;
1068
0
    CustomCallback *cc = (CustomCallback *)d;
1069
0
    UA_DeleteMonitoredItemsResponse *response = (UA_DeleteMonitoredItemsResponse *)r;
1070
0
    UA_DeleteMonitoredItemsRequest *request =
1071
0
        (UA_DeleteMonitoredItemsRequest *)cc->clientData;
1072
1073
0
    lockClient(client);
1074
1075
0
    if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD)
1076
0
        goto cleanup;
1077
1078
0
    sub = findSubscriptionById(client, request->subscriptionId);
1079
0
    if(!sub) {
1080
0
        UA_LOG_INFO(client->config.logging, UA_LOGCATEGORY_CLIENT,
1081
0
                    "No internal representation of subscription %" PRIu32,
1082
0
                    request->subscriptionId);
1083
0
        goto cleanup;
1084
0
    }
1085
1086
    /* Delete MonitoredItems from the internal representation */
1087
0
    MonitoredItems_delete(client, sub, request, response);
1088
1089
0
cleanup:
1090
0
    if(cc->userCallback)
1091
0
        cc->userCallback(client, cc->userData, requestId, response);
1092
0
    UA_DeleteMonitoredItemsRequest_delete(request);
1093
0
    UA_free(cc);
1094
1095
0
    unlockClient(client);
1096
0
}
1097
1098
UA_DeleteMonitoredItemsResponse
1099
UA_Client_MonitoredItems_delete(UA_Client *client,
1100
0
                                const UA_DeleteMonitoredItemsRequest request) {
1101
    /* Send the request */
1102
0
    UA_DeleteMonitoredItemsResponse response;
1103
0
    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSREQUEST],
1104
0
                        &response, &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSRESPONSE]);
1105
1106
    /* A problem occured remote? */
1107
0
    if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
1108
0
        return response;
1109
1110
0
    lockClient(client);
1111
1112
    /* Find the internal subscription representation */
1113
0
    UA_Client_Subscription *sub = findSubscriptionById(client, request.subscriptionId);
1114
0
    if(!sub) {
1115
0
        UA_LOG_INFO(client->config.logging, UA_LOGCATEGORY_CLIENT,
1116
0
                    "No internal representation of subscription %" PRIu32,
1117
0
                    request.subscriptionId);
1118
0
        unlockClient(client);
1119
0
        return response;
1120
0
    }
1121
1122
    /* Remove MonitoredItems in the internal representation */
1123
0
    MonitoredItems_delete(client, sub, &request, &response);
1124
1125
0
    unlockClient(client);
1126
1127
0
    return response;
1128
0
}
1129
1130
UA_StatusCode
1131
UA_Client_MonitoredItems_delete_async(UA_Client *client,
1132
                                      const UA_DeleteMonitoredItemsRequest request,
1133
                                      UA_ClientAsyncDeleteMonitoredItemsCallback callback,
1134
0
                                      void *userdata, UA_UInt32 *requestId) {
1135
    /* Send the request */
1136
0
    CustomCallback *cc = (CustomCallback *)UA_calloc(1, sizeof(CustomCallback));
1137
0
    if(!cc)
1138
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
1139
1140
0
    UA_DeleteMonitoredItemsRequest *req_copy = UA_DeleteMonitoredItemsRequest_new();
1141
0
    if(!req_copy) {
1142
0
        UA_free(cc);
1143
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
1144
0
    }
1145
1146
0
    UA_DeleteMonitoredItemsRequest_copy(&request, req_copy);
1147
0
    cc->clientData = req_copy;
1148
0
    cc->userCallback = (UA_ClientAsyncServiceCallback)callback;
1149
0
    cc->userData = userdata;
1150
1151
0
    UA_StatusCode res =
1152
0
        __UA_Client_AsyncService(client, &request,
1153
0
                                 &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSREQUEST],
1154
0
                                 MonitoredItems_delete_handler,
1155
0
                                 &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSRESPONSE],
1156
0
                                 cc, requestId);
1157
0
    if(res != UA_STATUSCODE_GOOD) {
1158
0
        UA_DeleteMonitoredItemsRequest_delete(req_copy);
1159
0
        UA_free(cc);
1160
0
    }
1161
0
    return res;
1162
0
}
1163
1164
UA_StatusCode
1165
UA_Client_MonitoredItems_deleteSingle(UA_Client *client, UA_UInt32 subscriptionId,
1166
0
                                      UA_UInt32 monitoredItemId) {
1167
0
    UA_DeleteMonitoredItemsRequest request;
1168
0
    UA_DeleteMonitoredItemsRequest_init(&request);
1169
0
    request.subscriptionId = subscriptionId;
1170
0
    request.monitoredItemIds = &monitoredItemId;
1171
0
    request.monitoredItemIdsSize = 1;
1172
1173
0
    UA_DeleteMonitoredItemsResponse response =
1174
0
        UA_Client_MonitoredItems_delete(client, request);
1175
1176
0
    UA_StatusCode retval = response.responseHeader.serviceResult;
1177
0
    if(retval != UA_STATUSCODE_GOOD) {
1178
0
        UA_DeleteMonitoredItemsResponse_clear(&response);
1179
0
        return retval;
1180
0
    }
1181
1182
0
    if(response.resultsSize != 1) {
1183
0
        UA_DeleteMonitoredItemsResponse_clear(&response);
1184
0
        return UA_STATUSCODE_BADINTERNALERROR;
1185
0
    }
1186
1187
0
    retval = response.results[0];
1188
0
    UA_DeleteMonitoredItemsResponse_clear(&response);
1189
0
    return retval;
1190
0
}
1191
1192
static void *
1193
0
MonitoredItem_findByID(void *data, UA_Client_MonitoredItem *mon) {
1194
0
    UA_UInt32 monitorId = *(UA_UInt32*)data;
1195
0
    if(monitorId && (mon->monitoredItemId == monitorId))
1196
0
        return mon;
1197
0
    return NULL;
1198
0
}
1199
1200
static UA_Client_MonitoredItem *
1201
0
findMonitoredItemById(UA_Client_Subscription *sub, UA_UInt32 monitoredItemId) {
1202
0
    return (UA_Client_MonitoredItem *)
1203
0
        ZIP_ITER(MonitorItemsTree, &sub->monitoredItems,
1204
0
                 MonitoredItem_findByID, &monitoredItemId);
1205
0
}
1206
1207
static UA_StatusCode
1208
MonitoredItems_prepareModify(UA_Client *client, UA_Client_Subscription *sub,
1209
0
                             UA_ModifyMonitoredItemsRequest *request) {
1210
0
    UA_STACKARRAY(UA_Client_MonitoredItem*, mons, request->itemsToModifySize);
1211
0
    UA_STACKARRAY(UA_MonitoringParameters, preparedParameters,
1212
0
                  request->itemsToModifySize);
1213
0
    memset(mons, 0, sizeof(UA_Client_MonitoredItem*) *
1214
0
           request->itemsToModifySize);
1215
0
    memset(preparedParameters, 0, sizeof(UA_MonitoringParameters) *
1216
0
           request->itemsToModifySize);
1217
1218
    /* Prepare every settings slot before changing the MonitoredItems. So an
1219
     * allocation failure leaves all active and pending settings untouched. */
1220
0
    for(size_t i = 0; i < request->itemsToModifySize; ++i) {
1221
0
        UA_MonitoredItemModifyRequest *mimr = &request->itemsToModify[i];
1222
0
        mimr->requestedParameters.clientHandle = 0;
1223
0
        mons[i] = findMonitoredItemById(sub, mimr->monitoredItemId);
1224
0
        if(!mons[i])
1225
0
            continue;
1226
1227
0
        mimr->requestedParameters.clientHandle = ++client->monitoredItemHandles;
1228
0
        UA_StatusCode res =
1229
0
            UA_MonitoringParameters_copy(&mimr->requestedParameters,
1230
0
                                         &preparedParameters[i]);
1231
0
        if(res != UA_STATUSCODE_GOOD) {
1232
0
            for(size_t j = 0; j <= i; j++)
1233
0
                UA_MonitoringParameters_clear(&preparedParameters[j]);
1234
0
            return res;
1235
0
        }
1236
0
    }
1237
1238
    /* Commit the prepared settings. A newer modification supersedes a pending
1239
     * generation that has not produced a notification yet. */
1240
0
    for(size_t i = 0; i < request->itemsToModifySize; ++i) {
1241
0
        UA_Client_MonitoredItem *mon = mons[i];
1242
0
        if(!mon)
1243
0
            continue;
1244
        /* Newly entering the pending re-key state (a fresh handle is
1245
         * always non-zero); a supersede keeps it pending, no double count. */
1246
0
        if(mon->pendingParameters.clientHandle == 0)
1247
0
            sub->pendingRekeys++;
1248
0
        UA_MonitoringParameters_clear(&mon->pendingParameters);
1249
0
        mon->pendingParameters = preparedParameters[i];
1250
0
        memset(&preparedParameters[i], 0, sizeof(UA_MonitoringParameters));
1251
0
    }
1252
0
    return UA_STATUSCODE_GOOD;
1253
0
}
1254
1255
static void
1256
MonitoredItems_reconcileModify(UA_Client_Subscription *sub,
1257
                               const UA_ModifyMonitoredItemsRequest *request,
1258
0
                               UA_ModifyMonitoredItemsResponse *response) {
1259
0
    UA_Boolean validResponse = response &&
1260
0
        response->responseHeader.serviceResult == UA_STATUSCODE_GOOD &&
1261
0
        response->resultsSize == request->itemsToModifySize;
1262
0
    if(response && response->responseHeader.serviceResult == UA_STATUSCODE_GOOD &&
1263
0
       !validResponse)
1264
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR;
1265
1266
0
    for(size_t i = 0; i < request->itemsToModifySize; ++i) {
1267
0
        if(validResponse && response->results[i].statusCode == UA_STATUSCODE_GOOD)
1268
0
            continue;
1269
0
        const UA_MonitoredItemModifyRequest *mimr = &request->itemsToModify[i];
1270
0
        UA_Client_MonitoredItem *mon =
1271
0
            findMonitoredItemById(sub, mimr->monitoredItemId);
1272
0
        if(mon && mon->pendingParameters.clientHandle ==
1273
0
                  mimr->requestedParameters.clientHandle) {
1274
0
            UA_MonitoringParameters_clear(&mon->pendingParameters);
1275
0
            sub->pendingRekeys--;
1276
0
        }
1277
0
    }
1278
0
}
1279
1280
UA_ModifyMonitoredItemsResponse
1281
UA_Client_MonitoredItems_modify(UA_Client *client,
1282
0
                                const UA_ModifyMonitoredItemsRequest request) {
1283
0
    UA_ModifyMonitoredItemsResponse response;
1284
0
    UA_ModifyMonitoredItemsResponse_init(&response);
1285
1286
    /* Make a modifiable copy of the request */
1287
0
    UA_ModifyMonitoredItemsRequest modifiedRequest;
1288
0
    UA_StatusCode res = UA_ModifyMonitoredItemsRequest_copy(&request, &modifiedRequest);
1289
0
    if(res != UA_STATUSCODE_GOOD) {
1290
0
        response.responseHeader.serviceResult = res;
1291
0
        return response;
1292
0
    }
1293
1294
0
    lockClient(client);
1295
1296
    /* Get the subscription */
1297
0
    UA_Client_Subscription *sub = findSubscriptionById(client, request.subscriptionId);
1298
0
    if(!sub) {
1299
0
        unlockClient(client);
1300
0
        UA_ModifyMonitoredItemsRequest_clear(&modifiedRequest);
1301
0
        response.responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
1302
0
        return response;
1303
0
    }
1304
1305
0
    res = MonitoredItems_prepareModify(client, sub, &modifiedRequest);
1306
0
    if(res != UA_STATUSCODE_GOOD) {
1307
0
        response.responseHeader.serviceResult = res;
1308
0
        unlockClient(client);
1309
0
        UA_ModifyMonitoredItemsRequest_clear(&modifiedRequest);
1310
0
        return response;
1311
0
    }
1312
1313
    /* Call the service */
1314
0
    __Client_Service(client, &modifiedRequest,
1315
0
                     &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSREQUEST], &response,
1316
0
                     &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSRESPONSE]);
1317
1318
    /* The Subscription is removed if the Session was closed while waiting for
1319
     * the response */
1320
0
    sub = findSubscriptionById(client, modifiedRequest.subscriptionId);
1321
0
    if(sub)
1322
0
        MonitoredItems_reconcileModify(sub, &modifiedRequest, &response);
1323
1324
0
    unlockClient(client);
1325
1326
    /* Clean up */
1327
0
    UA_ModifyMonitoredItemsRequest_clear(&modifiedRequest);
1328
0
    return response;
1329
0
}
1330
1331
static void
1332
MonitoredItems_modify_async_handler(UA_Client *client, void *data,
1333
0
                                    UA_UInt32 requestId, void *resp) {
1334
0
    CustomCallback *cc = (CustomCallback*)data;
1335
0
    UA_ModifyMonitoredItemsRequest *request =
1336
0
        (UA_ModifyMonitoredItemsRequest*)cc->clientData;
1337
0
    UA_ModifyMonitoredItemsResponse *response =
1338
0
        (UA_ModifyMonitoredItemsResponse*)resp;
1339
1340
0
    lockClient(client);
1341
0
    UA_Client_Subscription *sub =
1342
0
        findSubscriptionById(client, request->subscriptionId);
1343
0
    if(sub)
1344
0
        MonitoredItems_reconcileModify(sub, request, response);
1345
1346
0
    if(cc->userCallback) {
1347
0
        UA_ClientAsyncModifyMonitoredItemsCallback cb =
1348
0
            (UA_ClientAsyncModifyMonitoredItemsCallback)cc->userCallback;
1349
0
        cb(client, cc->userData, requestId, response);
1350
0
    }
1351
1352
0
    UA_ModifyMonitoredItemsRequest_delete(request);
1353
0
    UA_free(cc);
1354
0
    unlockClient(client);
1355
0
}
1356
1357
UA_StatusCode
1358
UA_Client_MonitoredItems_modify_async(UA_Client *client,
1359
                                      const UA_ModifyMonitoredItemsRequest request,
1360
                                      UA_ClientAsyncModifyMonitoredItemsCallback callback,
1361
0
                                      void *userdata, UA_UInt32 *requestId) {
1362
0
    UA_ModifyMonitoredItemsRequest *requestCopy =
1363
0
        UA_ModifyMonitoredItemsRequest_new();
1364
0
    if(!requestCopy)
1365
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
1366
0
    UA_StatusCode res =
1367
0
        UA_ModifyMonitoredItemsRequest_copy(&request, requestCopy);
1368
0
    if(res != UA_STATUSCODE_GOOD) {
1369
0
        UA_ModifyMonitoredItemsRequest_delete(requestCopy);
1370
0
        return res;
1371
0
    }
1372
1373
0
    lockClient(client);
1374
1375
    /* Get the subscription */
1376
0
    UA_Client_Subscription *sub = findSubscriptionById(client, request.subscriptionId);
1377
0
    if(!sub) {
1378
0
        unlockClient(client);
1379
0
        UA_ModifyMonitoredItemsRequest_delete(requestCopy);
1380
0
        return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
1381
0
    }
1382
1383
0
    res = MonitoredItems_prepareModify(client, sub, requestCopy);
1384
0
    if(res != UA_STATUSCODE_GOOD) {
1385
0
        unlockClient(client);
1386
0
        UA_ModifyMonitoredItemsRequest_delete(requestCopy);
1387
0
        return res;
1388
0
    }
1389
1390
0
    CustomCallback *cc = (CustomCallback*)UA_calloc(1, sizeof(CustomCallback));
1391
0
    if(!cc) {
1392
0
        MonitoredItems_reconcileModify(sub, requestCopy, NULL);
1393
0
        unlockClient(client);
1394
0
        UA_ModifyMonitoredItemsRequest_delete(requestCopy);
1395
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
1396
0
    }
1397
0
    cc->clientData = requestCopy;
1398
0
    cc->userCallback = (UA_ClientAsyncServiceCallback)callback;
1399
0
    cc->userData = userdata;
1400
1401
    /* Call the service */
1402
0
    UA_StatusCode statusCode = __Client_AsyncService(
1403
0
        client, requestCopy, &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSREQUEST],
1404
0
        MonitoredItems_modify_async_handler,
1405
0
        &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSRESPONSE], cc, requestId);
1406
1407
0
    if(statusCode != UA_STATUSCODE_GOOD) {
1408
0
        MonitoredItems_reconcileModify(sub, requestCopy, NULL);
1409
0
        UA_ModifyMonitoredItemsRequest_delete(requestCopy);
1410
0
        UA_free(cc);
1411
0
    }
1412
1413
0
    unlockClient(client);
1414
0
    return statusCode;
1415
0
}
1416
1417
UA_StatusCode
1418
UA_Client_MonitoredItem_getContext(UA_Client *client, UA_UInt32 subscriptionId,
1419
0
                                   UA_UInt32 monitoredItemId, void **monContext) {
1420
0
    if(!client || !monContext)
1421
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
1422
1423
0
    *monContext = NULL;
1424
1425
0
    lockClient(client);
1426
0
    UA_Client_Subscription *sub = findSubscriptionById(client, subscriptionId);
1427
0
    if(!sub) {
1428
0
        unlockClient(client);
1429
0
        return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
1430
0
    }
1431
1432
0
    UA_StatusCode itemstatus = UA_STATUSCODE_BADMONITOREDITEMIDINVALID;
1433
0
    UA_Client_MonitoredItem *monItem = findMonitoredItemById(sub, monitoredItemId);
1434
0
    if(monItem) {
1435
0
        *monContext = monItem->context;
1436
0
        itemstatus = UA_STATUSCODE_GOOD;
1437
0
    }
1438
0
    unlockClient(client);
1439
0
    return itemstatus;
1440
0
}
1441
1442
UA_StatusCode
1443
UA_Client_MonitoredItem_setContext(UA_Client *client, UA_UInt32 subscriptionId,
1444
0
                                   UA_UInt32 monitoredItemId, void *monContext) {
1445
0
    if(!client)
1446
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
1447
1448
0
    lockClient(client);
1449
0
    UA_Client_Subscription *sub = findSubscriptionById(client, subscriptionId);
1450
0
    if(!sub) {
1451
0
        unlockClient(client);
1452
0
        return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
1453
0
    }
1454
1455
0
    UA_StatusCode itemstatus = UA_STATUSCODE_BADMONITOREDITEMIDINVALID;
1456
0
    UA_Client_MonitoredItem *monItem = findMonitoredItemById(sub, monitoredItemId);
1457
0
    if(monItem) {
1458
0
        monItem->context = monContext;
1459
0
        itemstatus = UA_STATUSCODE_GOOD;
1460
0
    }
1461
0
    unlockClient(client);
1462
0
    return itemstatus;
1463
0
}
1464
1465
/*************************************/
1466
/* Async Processing of Notifications */
1467
/*************************************/
1468
1469
/* Assume the request is already initialized */
1470
UA_StatusCode
1471
0
__Client_preparePublishRequest(UA_Client *client, UA_PublishRequest *request) {
1472
0
    UA_LOCK_ASSERT(&client->clientMutex);
1473
1474
    /* Count acks */
1475
0
    UA_Client_NotificationsAckNumber *ack;
1476
0
    LIST_FOREACH(ack, &client->pendingNotificationsAcks, listEntry)
1477
0
        ++request->subscriptionAcknowledgementsSize;
1478
1479
    /* Create the array. Returns a sentinel pointer if the length is zero. */
1480
0
    request->subscriptionAcknowledgements = (UA_SubscriptionAcknowledgement*)
1481
0
        UA_Array_new(request->subscriptionAcknowledgementsSize,
1482
0
                     &UA_TYPES[UA_TYPES_SUBSCRIPTIONACKNOWLEDGEMENT]);
1483
0
    if(!request->subscriptionAcknowledgements) {
1484
0
        request->subscriptionAcknowledgementsSize = 0;
1485
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
1486
0
    }
1487
1488
0
    size_t i = 0;
1489
0
    UA_Client_NotificationsAckNumber *ack_tmp;
1490
0
    LIST_FOREACH_SAFE(ack, &client->pendingNotificationsAcks, listEntry, ack_tmp) {
1491
0
        LIST_REMOVE(ack, listEntry);
1492
0
        UA_SubscriptionAcknowledgement *reqAck = &request->subscriptionAcknowledgements[i];
1493
0
        reqAck->sequenceNumber = ack->subAck.sequenceNumber;
1494
0
        reqAck->subscriptionId = ack->subAck.subscriptionId;
1495
0
        UA_free(ack);
1496
0
        i++;
1497
0
    }
1498
0
    return UA_STATUSCODE_GOOD;
1499
0
}
1500
1501
/* According to specification, Part 4 5.13.1, the value 0 is never used for the
1502
 * sequence number */
1503
static UA_UInt32
1504
0
__nextSequenceNumber(UA_UInt32 sequenceNumber) {
1505
0
    UA_UInt32 nextSequenceNumber = sequenceNumber + 1;
1506
0
    if(nextSequenceNumber == 0)
1507
0
        nextSequenceNumber = 1;
1508
0
    return nextSequenceNumber;
1509
0
}
1510
1511
static UA_Client_MonitoredItem *
1512
findMonitoredItemForNotification(UA_Client_Subscription *sub,
1513
0
                                 UA_UInt32 clientHandle) {
1514
0
    UA_Client_MonitoredItem *mon =
1515
0
        findMonitoredItemByHandle(sub, clientHandle);
1516
0
    if(mon)
1517
0
        return mon;
1518
1519
    /* A notification is the authoritative signal that the server has started
1520
     * to use the modified settings. Promote the embedded pending slot and
1521
     * re-key the existing tree node. */
1522
    /* Skip the O(n) scan entirely when no re-key is pending: stops a
1523
     * malicious server from burning CPU on the single client thread by
1524
     * flooding notifications with unknown clientHandles (O(N*n)). */
1525
0
    if(sub->pendingRekeys == 0)
1526
0
        return NULL;
1527
0
    mon = (UA_Client_MonitoredItem*)
1528
0
        ZIP_ITER(MonitorItemsTree, &sub->monitoredItems,
1529
0
                 MonitoredItem_findPendingByHandle, &clientHandle);
1530
0
    if(!mon)
1531
0
        return NULL;
1532
1533
0
    ZIP_REMOVE(MonitorItemsTree, &sub->monitoredItems, mon);
1534
0
    UA_MonitoringParameters_clear(&mon->parameters);
1535
0
    mon->parameters = mon->pendingParameters;
1536
0
    UA_MonitoringParameters_init(&mon->pendingParameters);
1537
0
    sub->pendingRekeys--;
1538
0
    EventFields_clear(&mon->eventFields);
1539
0
    ZIP_INSERT(MonitorItemsTree, &sub->monitoredItems, mon);
1540
0
    return mon;
1541
0
}
1542
1543
static UA_Boolean
1544
processDataChangeNotification(UA_Client *client, UA_Client_Subscription *sub,
1545
0
                              UA_DataChangeNotification *dataChangeNotification) {
1546
0
    UA_LOCK_ASSERT(&client->clientMutex);
1547
0
    UA_UInt32 subId = sub->subscriptionId;
1548
1549
0
    for(size_t j = 0; j < dataChangeNotification->monitoredItemsSize; ++j) {
1550
0
        UA_MonitoredItemNotification *min = &dataChangeNotification->monitoredItems[j];
1551
1552
        /* Find the MonitoredItem */
1553
0
        UA_Client_MonitoredItem *mon =
1554
0
            findMonitoredItemForNotification(sub, min->clientHandle);
1555
1556
0
        if(!mon) {
1557
0
            UA_LOG_WARNING(client->config.logging, UA_LOGCATEGORY_CLIENT,
1558
0
                           "Could not process a notification with clienthandle %" PRIu32
1559
0
                           " on subscription %" PRIu32, min->clientHandle, sub->subscriptionId);
1560
0
            continue;
1561
0
        }
1562
1563
0
        if(mon->isEventMonitoredItem) {
1564
0
            UA_LOG_WARNING(client->config.logging, UA_LOGCATEGORY_CLIENT,
1565
0
                           "MonitoredItem is configured for Events. But received a "
1566
0
                           "DataChangeNotification.");
1567
0
            continue;
1568
0
        }
1569
1570
0
        if(mon->handler.dataChangeCallback) {
1571
0
            void *subC = sub->context;
1572
0
            void *monC = mon->context;
1573
0
            UA_UInt32 monId = mon->monitoredItemId;
1574
0
            mon->handler.dataChangeCallback(client, subId, subC, monId, monC, &min->value);
1575
0
            if(findSubscriptionById(client, subId) != sub)
1576
0
                return false;
1577
0
        }
1578
0
    }
1579
0
    return true;
1580
0
}
1581
1582
static UA_Boolean
1583
processEventNotification(UA_Client *client, UA_Client_Subscription *sub,
1584
0
                         UA_EventNotificationList *eventNotificationList) {
1585
0
    UA_LOCK_ASSERT(&client->clientMutex);
1586
0
    UA_UInt32 subId = sub->subscriptionId;
1587
1588
0
    for(size_t j = 0; j < eventNotificationList->eventsSize; ++j) {
1589
0
        UA_EventFieldList *efl = &eventNotificationList->events[j];
1590
1591
        /* Find the MonitoredItem */
1592
0
        UA_Client_MonitoredItem *mon =
1593
0
            findMonitoredItemForNotification(sub, efl->clientHandle);
1594
1595
0
        if(!mon) {
1596
0
            UA_LOG_DEBUG(client->config.logging, UA_LOGCATEGORY_CLIENT,
1597
0
                         "Could not process a notification with clienthandle %" PRIu32
1598
0
                         " on subscription %" PRIu32, efl->clientHandle,
1599
0
                         sub->subscriptionId);
1600
0
            continue;
1601
0
        }
1602
1603
0
        if(!mon->isEventMonitoredItem) {
1604
0
            UA_LOG_DEBUG(client->config.logging, UA_LOGCATEGORY_CLIENT,
1605
0
                         "MonitoredItem is configured for DataChanges. But received a "
1606
0
                         "EventNotification");
1607
0
            continue;
1608
0
        }
1609
1610
0
        if(!mon->handler.eventCallback)
1611
0
            continue;
1612
1613
        /* Build the callback metadata from the active filter only when it is
1614
         * first needed. After a promotion the cache is empty and rebuilt from
1615
         * the newly active parameters. */
1616
0
        if(mon->eventFields.mapSize == 0) {
1617
0
            UA_StatusCode res =
1618
0
                prepareEventFieldsMap(&mon->eventFields, &mon->parameters);
1619
0
            if(res != UA_STATUSCODE_GOOD) {
1620
0
                EventFields_clear(&mon->eventFields);
1621
0
                UA_LOG_WARNING(client->config.logging, UA_LOGCATEGORY_CLIENT,
1622
0
                               "Could not prepare event fields: %s",
1623
0
                               UA_StatusCode_name(res));
1624
0
                continue;
1625
0
            }
1626
0
        }
1627
1628
0
        if(mon->eventFields.mapSize != efl->eventFieldsSize) {
1629
0
            UA_LOG_DEBUG(client->config.logging, UA_LOGCATEGORY_CLIENT,
1630
0
                         "MonitoredItem received a EventNotification with the "
1631
0
                          "wrong number of event fields");
1632
0
            continue;
1633
0
        }
1634
1635
        /* Add the borrowed notification values to the cached field names. */
1636
0
        for(size_t i = 0; i < mon->eventFields.mapSize; i++)
1637
0
            mon->eventFields.map[i].value = efl->eventFields[i];
1638
0
        mon->handler.eventCallback(client, sub->subscriptionId, sub->context,
1639
0
                                   mon->monitoredItemId, mon->context,
1640
0
                                   mon->eventFields);
1641
0
        if(findSubscriptionById(client, subId) != sub)
1642
0
            return false;
1643
0
    }
1644
0
    return true;
1645
0
}
1646
1647
static UA_Boolean
1648
processNotificationMessage(UA_Client *client, UA_Client_Subscription *sub,
1649
0
                           UA_ExtensionObject *msg) {
1650
0
    UA_LOCK_ASSERT(&client->clientMutex);
1651
1652
0
    if(msg->encoding != UA_EXTENSIONOBJECT_DECODED)
1653
0
        return true;
1654
1655
    /* Handle DataChangeNotification */
1656
0
    if(msg->content.decoded.type == &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION]) {
1657
0
        UA_DataChangeNotification *dataChangeNotification =
1658
0
            (UA_DataChangeNotification *)msg->content.decoded.data;
1659
0
        return processDataChangeNotification(client, sub, dataChangeNotification);
1660
0
    }
1661
1662
    /* Handle EventNotification */
1663
0
    if(msg->content.decoded.type == &UA_TYPES[UA_TYPES_EVENTNOTIFICATIONLIST]) {
1664
0
        UA_EventNotificationList *eventNotificationList =
1665
0
            (UA_EventNotificationList *)msg->content.decoded.data;
1666
0
        return processEventNotification(client, sub, eventNotificationList);
1667
0
    }
1668
1669
    /* Handle StatusChangeNotification */
1670
0
    if(msg->content.decoded.type == &UA_TYPES[UA_TYPES_STATUSCHANGENOTIFICATION]) {
1671
0
        UA_UInt32 subId = sub->subscriptionId;
1672
0
        if(sub->statusChangeCallback) {
1673
0
            void *subC = sub->context;
1674
0
            sub->statusChangeCallback(client, subId, subC,
1675
0
                                      (UA_StatusChangeNotification*)msg->content.decoded.data);
1676
0
        } else {
1677
0
            UA_LOG_WARNING(client->config.logging, UA_LOGCATEGORY_CLIENT,
1678
0
                           "Dropped a StatusChangeNotification since no "
1679
0
                           "callback is registered");
1680
0
        }
1681
0
        return findSubscriptionById(client, subId) == sub;
1682
0
    }
1683
1684
0
    UA_LOG_WARNING(client->config.logging, UA_LOGCATEGORY_CLIENT,
1685
0
                   "Unknown notification message type");
1686
0
    return true;
1687
0
}
1688
1689
void
1690
__Client_Subscriptions_processPublishResponse(UA_Client *client, UA_PublishRequest *request,
1691
0
                                              UA_PublishResponse *response) {
1692
0
    UA_LOCK_ASSERT(&client->clientMutex);
1693
1694
    /* Reduce the number of "in-flight" PublishRequests */
1695
0
    client->currentlyOutStandingPublishRequests--;
1696
1697
    /* Process ServiceResult for bad StatusCodes without referring to a
1698
     * SubscriptionId */
1699
0
    switch(response->responseHeader.serviceResult) {
1700
0
    case UA_STATUSCODE_BADTOOMANYPUBLISHREQUESTS:
1701
        /* Correct the assumed number of max outstanding requests */
1702
0
        if(client->config.outStandingPublishRequests > 1) {
1703
0
            client->config.outStandingPublishRequests--;
1704
0
            UA_LOG_WARNING(client->config.logging, UA_LOGCATEGORY_CLIENT,
1705
0
                           "PublishResponse: Too many PublishRequest, reduce "
1706
0
                           "outStandingPublishRequests to %" PRId16,
1707
0
                           client->config.outStandingPublishRequests);
1708
0
        } else {
1709
0
            UA_LOG_ERROR(client->config.logging, UA_LOGCATEGORY_CLIENT,
1710
0
                         "PublishResponse: Too many PublishRequests when "
1711
0
                         "outStandingPublishRequests = 1");
1712
0
            UA_Client_Subscriptions_deleteSingle(client, response->subscriptionId);
1713
0
        }
1714
0
        return;
1715
1716
0
    case UA_STATUSCODE_BADSHUTDOWN:
1717
        /* If the remote server shuts down, DEBUG-log to avoid a warning-storm
1718
         * for normal operations */
1719
0
        UA_LOG_DEBUG(client->config.logging, UA_LOGCATEGORY_CLIENT,
1720
0
                     "PublishResponse: Received BadShutdown status");
1721
0
        return;
1722
1723
0
    case UA_STATUSCODE_BADNOSUBSCRIPTION:
1724
        /* There is no Subscription configured, the server expects no
1725
         * PublishRequests. We demote this to debug-logging, as it can occur
1726
         * during regular shutdown when the Subscriptions are removed. */
1727
0
        UA_LOG_DEBUG(client->config.logging, UA_LOGCATEGORY_CLIENT,
1728
0
                     "PublishResponse: Received BadNoSubscription status");
1729
0
        return;
1730
1731
0
    default:
1732
0
        break;
1733
0
    }
1734
1735
    /* Get the Subscription */
1736
0
    UA_Client_Subscription *sub = findSubscriptionById(client, response->subscriptionId);
1737
0
    if(!sub) {
1738
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOSUBSCRIPTION;
1739
0
        UA_LOG_WARNING(client->config.logging, UA_LOGCATEGORY_CLIENT,
1740
0
                       "PublishResponse: Received response for an unknown Subscription");
1741
0
        return;
1742
0
    }
1743
1744
    /* Process ServiceResult */
1745
0
    switch(response->responseHeader.serviceResult) {
1746
0
    case UA_STATUSCODE_BADSESSIONCLOSED:
1747
        /* The Session no longer exists on the server - remove the Subscription */
1748
0
        __Client_Subscription_deleteInternal(client, sub);
1749
0
        return;
1750
1751
0
    case UA_STATUSCODE_BADTIMEOUT:
1752
0
        UA_LOG_WARNING(client->config.logging, UA_LOGCATEGORY_CLIENT,
1753
0
                       "PublishResponse: Aborted with BadTimeout status");
1754
0
        if(client->config.subscriptionInactivityCallback) {
1755
0
            void *subC = sub->context;
1756
0
            UA_UInt32 subId = sub->subscriptionId;
1757
0
            client->config.subscriptionInactivityCallback(client, subId, subC);
1758
0
        }
1759
0
        return;
1760
1761
0
    case UA_STATUSCODE_GOOD:
1762
0
        break; /* Continue below */
1763
1764
0
    default:
1765
        /* Catch-all for other bad StatusCodes */
1766
0
        UA_LOG_WARNING(client->config.logging, UA_LOGCATEGORY_CLIENT,
1767
0
                       "PublishResponse: Received %s status",
1768
0
                       UA_StatusCode_name(response->responseHeader.serviceResult));
1769
0
        return;
1770
0
    }
1771
1772
    /* Update the LastActivity for the Subscription */
1773
0
    UA_EventLoop *el = client->config.eventLoop;
1774
0
    sub->lastActivity = el->dateTime_nowMonotonic(el);
1775
1776
    /* Detect missing message - OPC Unified Architecture, Part 4 5.13.1.1 e) */
1777
0
    UA_NotificationMessage *msg = &response->notificationMessage;
1778
0
    if(__nextSequenceNumber(sub->sequenceNumber) != msg->sequenceNumber) {
1779
0
        UA_LOG_WARNING(client->config.logging, UA_LOGCATEGORY_CLIENT,
1780
0
                       "PublishResponse: Invalid subscription sequence number: "
1781
0
                       "Expected %" PRIu32 " but got %" PRIu32,
1782
0
                       __nextSequenceNumber(sub->sequenceNumber), msg->sequenceNumber);
1783
        /* This is an error. But we do not abort the connection. Some server
1784
         * SDKs misbehave from time to time and send out-of-order sequence
1785
         * numbers. (Probably some multi-threading synchronization issue.) */
1786
        /* UA_Client_disconnect(client);
1787
           return; */
1788
0
    }
1789
1790
    /* According to f), a keep-alive message contains no notifications and has
1791
     * the sequence number of the next NotificationMessage that is to be sent =>
1792
     * More than one consecutive keep-alive message or a NotificationMessage
1793
     * following a keep-alive message will share the same sequence number. */
1794
0
    if(msg->notificationDataSize)
1795
0
        sub->sequenceNumber = msg->sequenceNumber;
1796
1797
    /* Process the notification messages */
1798
0
    for(size_t k = 0; k < msg->notificationDataSize; ++k) {
1799
0
        if(!processNotificationMessage(client, sub, &msg->notificationData[k]))
1800
0
            return;
1801
0
    }
1802
1803
    /* Add the current NotificationMessage (SequenceNumber) to the list of
1804
     * pending acks to be acknowledged. But only if it is in the list of
1805
     * sequence numbers the server has available. */
1806
0
    for(size_t i = 0; i < response->availableSequenceNumbersSize; i++) {
1807
0
        if(response->availableSequenceNumbers[i] != msg->sequenceNumber)
1808
0
            continue;
1809
0
        UA_Client_NotificationsAckNumber *tmpAck = (UA_Client_NotificationsAckNumber*)
1810
0
            UA_malloc(sizeof(UA_Client_NotificationsAckNumber));
1811
0
        if(!tmpAck) {
1812
0
            UA_LOG_WARNING(client->config.logging, UA_LOGCATEGORY_CLIENT,
1813
0
                           "PublishResponse: Not enough memory to store the pending "
1814
0
                           "acknowledgement for Subscription %" PRIu32, sub->subscriptionId);
1815
0
            break;
1816
0
        }
1817
0
        tmpAck->subAck.sequenceNumber = msg->sequenceNumber;
1818
0
        tmpAck->subAck.subscriptionId = sub->subscriptionId;
1819
0
        LIST_INSERT_HEAD(&client->pendingNotificationsAcks, tmpAck, listEntry);
1820
0
        break;
1821
0
    }
1822
0
}
1823
1824
static void
1825
processPublishResponseAsync(UA_Client *client, void *userdata,
1826
0
                            UA_UInt32 requestId, void *response) {
1827
0
    UA_PublishRequest *req = (UA_PublishRequest*)userdata;
1828
0
    UA_PublishResponse *res = (UA_PublishResponse*)response;
1829
1830
0
    lockClient(client);
1831
1832
    /* Process the response */
1833
0
    __Client_Subscriptions_processPublishResponse(client, req, res);
1834
1835
    /* Delete the cached request */
1836
0
    UA_PublishRequest_delete(req);
1837
1838
    /* Fill up the outstanding publish requests */
1839
0
    __Client_Subscriptions_backgroundPublish(client);
1840
1841
0
    unlockClient(client);
1842
0
}
1843
1844
void
1845
60
__Client_Subscriptions_clear(UA_Client *client) {
1846
60
    UA_Client_NotificationsAckNumber *n;
1847
60
    UA_Client_NotificationsAckNumber *tmp;
1848
60
    LIST_FOREACH_SAFE(n, &client->pendingNotificationsAcks, listEntry, tmp) {
1849
0
        LIST_REMOVE(n, listEntry);
1850
0
        UA_free(n);
1851
0
    }
1852
1853
60
    UA_Client_Subscription *sub;
1854
    /* Deletion callbacks may remove other subscriptions as well. */
1855
60
    while((sub = LIST_FIRST(&client->subscriptions)))
1856
0
        __Client_Subscription_deleteInternal(client, sub); /* force local removal */
1857
1858
60
    client->monitoredItemHandles = 0;
1859
60
}
1860
1861
void
1862
0
__Client_Subscriptions_backgroundPublishInactivityCheck(UA_Client *client) {
1863
0
    UA_LOCK_ASSERT(&client->clientMutex);
1864
1865
0
    UA_EventLoop *el = client->config.eventLoop;
1866
0
    UA_DateTime nowm = el->dateTime_nowMonotonic(el);
1867
1868
0
    UA_Client_Subscription *sub;
1869
0
    LIST_FOREACH(sub, &client->subscriptions, listEntry) {
1870
0
        UA_DateTime maxSilence = (UA_DateTime)
1871
0
            ((sub->publishingInterval * sub->maxKeepAliveCount) +
1872
0
             client->config.timeout) * UA_DATETIME_MSEC;
1873
0
        if(maxSilence + sub->lastActivity < nowm) {
1874
            /* Reset activity */
1875
0
            sub->lastActivity = nowm;
1876
1877
0
            if(client->config.subscriptionInactivityCallback) {
1878
0
                void *subC = sub->context;
1879
0
                UA_UInt32 subId = sub->subscriptionId;
1880
0
                client->config.subscriptionInactivityCallback(client, subId, subC);
1881
0
                if(findSubscriptionById(client, subId) != sub)
1882
0
                    return;
1883
0
            }
1884
0
            UA_LOG_WARNING(client->config.logging, UA_LOGCATEGORY_CLIENT,
1885
0
                           "Inactivity for Subscription %" PRIu32 ".",
1886
0
                           sub->subscriptionId);
1887
0
        }
1888
0
    }
1889
0
}
1890
1891
void
1892
0
__Client_Subscriptions_backgroundPublish(UA_Client *client) {
1893
0
    UA_LOCK_ASSERT(&client->clientMutex);
1894
1895
0
    if(client->sessionState != UA_SESSIONSTATE_ACTIVATED)
1896
0
        return;
1897
1898
    /* The session must have at least one subscription */
1899
0
    if(!LIST_FIRST(&client->subscriptions))
1900
0
        return;
1901
1902
0
    while(client->currentlyOutStandingPublishRequests < client->config.outStandingPublishRequests) {
1903
0
        UA_PublishRequest *request = UA_PublishRequest_new();
1904
0
        if(!request)
1905
0
            return;
1906
1907
        /* Publish requests are valid for 10 minutes */
1908
0
        request->requestHeader.timeoutHint = 10 * 60 * 1000;
1909
1910
0
        UA_StatusCode retval = __Client_preparePublishRequest(client, request);
1911
0
        if(retval != UA_STATUSCODE_GOOD) {
1912
0
            UA_PublishRequest_delete(request);
1913
0
            return;
1914
0
        }
1915
1916
0
        retval = __Client_AsyncService(client, request,
1917
0
                                         &UA_TYPES[UA_TYPES_PUBLISHREQUEST],
1918
0
                                         processPublishResponseAsync,
1919
0
                                         &UA_TYPES[UA_TYPES_PUBLISHRESPONSE],
1920
0
                                         (void*)request, NULL);
1921
0
        if(retval != UA_STATUSCODE_GOOD) {
1922
0
            UA_PublishRequest_delete(request);
1923
0
            return;
1924
0
        }
1925
1926
0
        client->currentlyOutStandingPublishRequests++;
1927
0
    }
1928
0
}
1929
1930
UA_SetPublishingModeResponse
1931
UA_Client_Subscriptions_setPublishingMode(UA_Client *client,
1932
0
                                          const UA_SetPublishingModeRequest request) {
1933
0
    UA_SetPublishingModeResponse response;
1934
0
    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_SETPUBLISHINGMODEREQUEST],
1935
0
                        &response, &UA_TYPES[UA_TYPES_SETPUBLISHINGMODERESPONSE]);
1936
0
    return response;
1937
0
}
1938
1939
UA_SetMonitoringModeResponse
1940
UA_Client_MonitoredItems_setMonitoringMode(UA_Client *client,
1941
0
                                           const UA_SetMonitoringModeRequest request) {
1942
0
    UA_SetMonitoringModeResponse response;
1943
0
    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_SETMONITORINGMODEREQUEST],
1944
0
                        &response, &UA_TYPES[UA_TYPES_SETMONITORINGMODERESPONSE]);
1945
0
    return response;
1946
0
}
1947
1948
UA_StatusCode
1949
UA_Client_MonitoredItems_setMonitoringMode_async(UA_Client *client,
1950
                                                 const UA_SetMonitoringModeRequest request,
1951
                                                 UA_ClientAsyncSetMonitoringModeCallback callback,
1952
0
                                                 void *userdata, UA_UInt32 *requestId) {
1953
0
    return __UA_Client_AsyncService(client, &request,
1954
0
                                    &UA_TYPES[UA_TYPES_SETMONITORINGMODEREQUEST],
1955
0
                                    (UA_ClientAsyncServiceCallback)callback,
1956
0
                                    &UA_TYPES[UA_TYPES_SETMONITORINGMODERESPONSE],
1957
0
                                    userdata, requestId);
1958
0
}
1959
1960
UA_SetTriggeringResponse
1961
UA_Client_MonitoredItems_setTriggering(UA_Client *client,
1962
0
                                       const UA_SetTriggeringRequest request) {
1963
0
    UA_SetTriggeringResponse response;
1964
0
    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_SETTRIGGERINGREQUEST],
1965
0
                        &response, &UA_TYPES[UA_TYPES_SETTRIGGERINGRESPONSE]);
1966
0
    return response;
1967
0
}
1968
1969
UA_StatusCode
1970
UA_Client_MonitoredItems_setTriggering_async(UA_Client *client,
1971
                                             const UA_SetTriggeringRequest request,
1972
                                             UA_ClientAsyncSetTriggeringCallback callback,
1973
0
                                             void *userdata, UA_UInt32 *requestId) {
1974
0
    return __UA_Client_AsyncService(client, &request,
1975
0
                                    &UA_TYPES[UA_TYPES_SETTRIGGERINGREQUEST],
1976
0
                                    (UA_ClientAsyncServiceCallback)callback,
1977
0
                                    &UA_TYPES[UA_TYPES_SETTRIGGERINGRESPONSE],
1978
0
                                    userdata, requestId);
1979
0
}