Coverage Report

Created: 2026-05-16 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541_15/src/pubsub/ua_pubsub_reader.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 (c) 2017-2025 Fraunhofer IOSB (Author: Andreas Ebner)
6
 * Copyright (c) 2019 Fraunhofer IOSB (Author: Julius Pfrommer)
7
 * Copyright (c) 2019 Kalycito Infotech Private Limited
8
 * Copyright (c) 2021 Fraunhofer IOSB (Author: Jan Hermes)
9
 * Copyright (c) 2022 Siemens AG (Author: Thomas Fischer)
10
 * Copyright (c) 2022 Fraunhofer IOSB (Author: Noel Graf)
11
 */
12
13
#include "ua_pubsub_internal.h"
14
15
#ifdef UA_ENABLE_PUBSUB /* conditional compilation */
16
17
#include "ua_pubsub_networkmessage.h"
18
19
static UA_Boolean
20
0
publisherIdIsMatching(UA_NetworkMessage *msg, UA_PublisherId *idB) {
21
0
    if(!msg->publisherIdEnabled)
22
0
        return true;
23
0
    UA_PublisherId *idA = &msg->publisherId;
24
0
    if(idA->idType != idB->idType)
25
0
        return false;
26
0
    switch(idA->idType) {
27
0
        case UA_PUBLISHERIDTYPE_BYTE:   return idA->id.byte == idB->id.byte;
28
0
        case UA_PUBLISHERIDTYPE_UINT16: return idA->id.uint16 == idB->id.uint16;
29
0
        case UA_PUBLISHERIDTYPE_UINT32: return idA->id.uint32 == idB->id.uint32;
30
0
        case UA_PUBLISHERIDTYPE_UINT64: return idA->id.uint64 == idB->id.uint64;
31
0
        case UA_PUBLISHERIDTYPE_STRING: return UA_String_equal(&idA->id.string, &idB->id.string);
32
0
        default: break;
33
0
    }
34
0
    return false;
35
0
}
36
37
#if UA_LOGLEVEL <= 200
38
static void
39
printPublisherId(char *out, size_t size, UA_PublisherId *id) {
40
    switch(id->idType) {
41
    case UA_PUBLISHERIDTYPE_BYTE:   mp_snprintf(out, size, "(b)%u", (unsigned)id->id.byte); break;
42
    case UA_PUBLISHERIDTYPE_UINT16: mp_snprintf(out, size, "(u16)%u", (unsigned)id->id.uint16); break;
43
    case UA_PUBLISHERIDTYPE_UINT32: mp_snprintf(out, size, "(u32)%u", (unsigned)id->id.uint32); break;
44
    case UA_PUBLISHERIDTYPE_UINT64: mp_snprintf(out, size, "(u64)%lu", id->id.uint64); break;
45
    case UA_PUBLISHERIDTYPE_STRING: mp_snprintf(out, size, "\"%S\"", id->id.string); break;
46
    default: out[0] = 0; break;
47
    }
48
}
49
#endif
50
51
UA_StatusCode
52
UA_DataSetReader_checkIdentifier(UA_PubSubManager *psm, UA_DataSetReader *dsr,
53
0
                                 UA_NetworkMessage *msg) {
54
0
    if(!publisherIdIsMatching(msg, &dsr->config.publisherId)) {
55
#if UA_LOGLEVEL <= 200
56
        char idAStr[512];
57
        char idBStr[512];
58
        printPublisherId(idAStr, 512, &dsr->config.publisherId);
59
        printPublisherId(idBStr, 512, &msg->publisherId);
60
        UA_LOG_DEBUG_PUBSUB(psm->logging, dsr, "PublisherId does not match. "
61
                            "Expected %s, received %s", idAStr, idBStr);
62
#endif
63
0
        return UA_STATUSCODE_BADNOTFOUND;
64
0
    }
65
66
0
    UA_ReaderGroup *rg = dsr->linkedReaderGroup;
67
0
    if(rg->config.encodingMimeType == UA_PUBSUB_ENCODING_JSON) {
68
        // TODO
69
        /* if(dsr->config.dataSetWriterId == */
70
        /*    *msg->payloadHeader.dataSetPayloadHeader.dataSetWriterIds) { */
71
        /*     return UA_STATUSCODE_GOOD; */
72
        /* } */
73
74
        /* UA_LOG_DEBUG_PUBSUB(psm->logging, dsr, "DataSetWriterId does not match. " */
75
        /*                     "Expected %u, received %u", dsr->config.dataSetWriterId, */
76
        /*                     *msg->payloadHeader.dataSetPayloadHeader.dataSetWriterIds); */
77
0
        return UA_STATUSCODE_BADNOTFOUND;
78
0
    }
79
80
0
    if(msg->groupHeaderEnabled && msg->groupHeader.writerGroupIdEnabled) {
81
0
        if(dsr->config.writerGroupId != msg->groupHeader.writerGroupId) {
82
0
            UA_LOG_DEBUG_PUBSUB(psm->logging, dsr, "WriterGroupId does not match. "
83
0
                                "Expected %u, received %u", dsr->config.writerGroupId,
84
0
                                msg->groupHeader.writerGroupId);
85
0
            return UA_STATUSCODE_BADNOTFOUND;
86
0
        }
87
0
    }
88
89
0
    if(msg->payloadHeaderEnabled) {
90
0
        for(size_t i = 0; i < msg->messageCount; i++) {
91
0
            if(dsr->config.dataSetWriterId == msg->dataSetWriterIds[i])
92
0
                return UA_STATUSCODE_GOOD;
93
0
        }
94
0
        UA_LOG_DEBUG_PUBSUB(psm->logging, dsr,
95
0
                            "DataSetWriterIds in the payload do not match");
96
0
        return UA_STATUSCODE_BADNOTFOUND;
97
0
    }
98
99
0
    return UA_STATUSCODE_GOOD;
100
0
}
101
102
UA_DataSetReader *
103
0
UA_DataSetReader_find(UA_PubSubManager *psm, const UA_NodeId id) {
104
0
    if(!psm)
105
0
        return NULL;
106
0
    UA_PubSubConnection *psc;
107
0
    TAILQ_FOREACH(psc, &psm->connections, listEntry) {
108
0
        UA_ReaderGroup *rg;
109
0
        LIST_FOREACH(rg, &psc->readerGroups, listEntry) {
110
0
            UA_DataSetReader *dsr;
111
0
            LIST_FOREACH(dsr, &rg->readers, listEntry) {
112
0
                if(UA_NodeId_equal(&id, &dsr->head.identifier))
113
0
                    return dsr;
114
0
            }
115
0
        }
116
0
    }
117
0
    return NULL;
118
0
}
119
120
static UA_StatusCode
121
0
validateDSRConfig(UA_PubSubManager *psm, UA_DataSetReader *dsr) {
122
    /* Check if used dataSet metaData is valid in context of the rest of the config */
123
0
    if(dsr->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_RAWDATA) {
124
0
        for(size_t i = 0; i < dsr->config.dataSetMetaData.fieldsSize; i++) {
125
0
            const UA_FieldMetaData *field = &dsr->config.dataSetMetaData.fields[i];
126
0
            if((field->builtInType == UA_NS0ID_STRING || field->builtInType == UA_NS0ID_BYTESTRING) &&
127
0
               field->maxStringLength == 0) {
128
                /* Fields of type String or ByteString need to have defined
129
                 * MaxStringLength*/
130
0
                UA_LOG_ERROR_PUBSUB(psm->logging, dsr,
131
0
                                    "Add DataSetReader failed. MaxStringLength must be "
132
0
                                    "set in MetaData when using RawData field encoding.");
133
0
                return UA_STATUSCODE_BADCONFIGURATIONERROR;
134
0
            }
135
136
            /* Warn if the field is a structure type that contains
137
             * String/ByteString members. MaxStringLength padding is not
138
             * yet applied for strings nested inside structures. */
139
0
            const UA_DataType *type =
140
0
                UA_findDataTypeWithCustom(&field->dataType,
141
0
                                          psm->sc.server->config.customDataTypes);
142
0
            if(type &&
143
0
               (type->typeKind == UA_DATATYPEKIND_STRUCTURE ||
144
0
                type->typeKind == UA_DATATYPEKIND_UNION ||
145
0
                type->typeKind == UA_DATATYPEKIND_OPTSTRUCT) &&
146
0
               typeContainsString(type, 0)) {
147
0
                UA_LOG_WARNING_PUBSUB(psm->logging, dsr,
148
0
                                      "Field %.*s uses a structure type that "
149
0
                                      "contains String/ByteString members. "
150
0
                                      "MaxStringLength padding is not applied for "
151
0
                                      "strings inside structures with RawData encoding.",
152
0
                                      (int)field->name.length, field->name.data);
153
0
            }
154
0
        }
155
0
    }
156
0
    return UA_STATUSCODE_GOOD;
157
0
}
158
159
static void
160
0
disconnectDSR2Standalone(UA_PubSubManager *psm, UA_DataSetReader *dsr) {
161
    /* Check if a sds name is defined */
162
0
    const UA_String sdsName = dsr->config.linkedStandaloneSubscribedDataSetName;
163
0
    if(UA_String_isEmpty(&sdsName))
164
0
        return;
165
166
0
    UA_SubscribedDataSet *sds = UA_SubscribedDataSet_findByName(psm, sdsName);
167
0
    if(!sds)
168
0
        return;
169
170
    /* Remove the backpointer from the sds */
171
0
    sds->connectedReader = NULL;
172
173
    /* Remove the references in the information model */
174
0
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
175
0
    disconnectDataSetReaderToDataSet(psm->sc.server, dsr->head.identifier);
176
0
#endif
177
0
}
178
179
/* Connect to StandaloneSubscribedDataSet if a name is defined */
180
static UA_StatusCode
181
0
connectDSR2Standalone(UA_PubSubManager *psm, UA_DataSetReader *dsr) {
182
    /* Check if a sds name is defined */
183
0
    const UA_String sdsName = dsr->config.linkedStandaloneSubscribedDataSetName;
184
0
    if(UA_String_isEmpty(&sdsName))
185
0
        return UA_STATUSCODE_GOOD;
186
187
0
    UA_SubscribedDataSet *sds = UA_SubscribedDataSet_findByName(psm, sdsName);
188
0
    if(!sds)
189
0
        return UA_STATUSCODE_BADNOTFOUND;
190
191
    /* Already connected? */
192
0
    if(sds->connectedReader) {
193
0
        if(sds->connectedReader != dsr)
194
0
            UA_LOG_ERROR_PUBSUB(psm->logging, dsr,
195
0
                                "Configured StandaloneSubscribedDataSet already "
196
0
                                "connected to a different DataSetReader");
197
0
        return UA_STATUSCODE_BADINTERNALERROR;
198
0
    }
199
200
    /* Check supported type */
201
0
    if(sds->config.subscribedDataSetType != UA_PUBSUB_SDS_TARGET) {
202
0
        UA_LOG_ERROR_PUBSUB(psm->logging, dsr,
203
0
                            "Not implemented! Currently only SubscribedDataSet as "
204
0
                            "TargetVariables is implemented");
205
0
        return UA_STATUSCODE_BADNOTIMPLEMENTED;
206
0
    }
207
208
0
    UA_LOG_DEBUG_PUBSUB(psm->logging, dsr, "Connecting SubscribedDataSet");
209
210
    /* Copy the metadata from the sds */
211
0
    UA_DataSetMetaDataType metaData;
212
0
    UA_StatusCode res = UA_DataSetMetaDataType_copy(&sds->config.dataSetMetaData, &metaData);
213
0
    if(res != UA_STATUSCODE_GOOD)
214
0
        return res;
215
216
    /* Prepare the input for _createTargetVariables and call it */
217
0
    UA_TargetVariablesDataType *tvs = &sds->config.subscribedDataSet.target;
218
0
    res = DataSetReader_createTargetVariables(psm, dsr, tvs->targetVariablesSize,
219
0
                                              tvs->targetVariables);
220
0
    if(res != UA_STATUSCODE_GOOD) {
221
0
        UA_DataSetMetaDataType_clear(&metaData);
222
0
        return res;
223
0
    }
224
225
    /* Use the metadata from the sds */
226
0
    UA_DataSetMetaDataType_clear(&dsr->config.dataSetMetaData);
227
0
    dsr->config.dataSetMetaData = metaData;
228
229
    /* Set the backpointer from the sds */
230
0
    sds->connectedReader = dsr;
231
232
    /* Make the connection visible in the information model */
233
0
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
234
0
    return connectDataSetReaderToDataSet(psm->sc.server, dsr->head.identifier,
235
0
                                         sds->head.identifier);
236
#else
237
    return UA_STATUSCODE_GOOD;
238
#endif
239
0
}
240
241
UA_StatusCode
242
UA_DataSetReader_create(UA_PubSubManager *psm, UA_NodeId readerGroupIdentifier,
243
                        const UA_DataSetReaderConfig *dataSetReaderConfig,
244
0
                        UA_NodeId *readerIdentifier) {
245
0
    if(!psm || !dataSetReaderConfig)
246
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
247
248
0
    UA_LOCK_ASSERT(&psm->sc.server->serviceMutex);
249
250
    /* Search the reader group by the given readerGroupIdentifier */
251
0
    UA_ReaderGroup *rg = UA_ReaderGroup_find(psm, readerGroupIdentifier);
252
0
    if(!rg)
253
0
        return UA_STATUSCODE_BADNOTFOUND;
254
255
0
    if(UA_PubSubState_isEnabled(rg->head.state)) {
256
0
        UA_LOG_WARNING_PUBSUB(psm->logging, rg,
257
0
                              "Cannot add a DataSetReader while the "
258
0
                              "ReaderGroup is enabled");
259
0
        return UA_STATUSCODE_BADCONFIGURATIONERROR;
260
0
    }
261
262
    /* Allocate memory for new DataSetReader */
263
0
    UA_DataSetReader *dsr = (UA_DataSetReader *)
264
0
        UA_calloc(1, sizeof(UA_DataSetReader));
265
0
    if(!dsr)
266
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
267
268
0
    dsr->head.componentType = UA_PUBSUBCOMPONENT_DATASETREADER;
269
0
    dsr->linkedReaderGroup = rg;
270
271
    /* Add the new reader to the group. Add to the end of the linked list to
272
     * ensure the order for the realtime offsets is as expected. The received
273
     * DataSetMessages are matched via UA_DataSetReader_checkIdentifier for the
274
     * non-RT path. */
275
0
    UA_DataSetReader *after = LIST_FIRST(&rg->readers);
276
0
    if(!after) {
277
0
        LIST_INSERT_HEAD(&rg->readers, dsr, listEntry);
278
0
    } else {
279
0
        while(LIST_NEXT(after, listEntry))
280
0
            after = LIST_NEXT(after, listEntry);
281
0
        LIST_INSERT_AFTER(after, dsr, listEntry);
282
0
    }
283
0
    rg->readersCount++;
284
285
    /* Copy the config into the new dataSetReader */
286
0
    UA_StatusCode retVal =
287
0
        UA_DataSetReaderConfig_copy(dataSetReaderConfig, &dsr->config);
288
0
    if(retVal != UA_STATUSCODE_GOOD) {
289
0
        UA_DataSetReader_remove(psm, dsr);
290
0
        return retVal;
291
0
    }
292
293
0
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
294
0
    retVal = addDataSetReaderRepresentation(psm->sc.server, dsr);
295
0
    if(retVal != UA_STATUSCODE_GOOD) {
296
0
        UA_LOG_ERROR_PUBSUB(psm->logging, rg,
297
0
                            "Adding the DataSetReader to the information model failed");
298
0
        UA_DataSetReader_remove(psm, dsr);
299
0
        return retVal;
300
0
    }
301
#else
302
    UA_PubSubManager_generateUniqueNodeId(psm, &dsr->head.identifier);
303
#endif
304
305
    /* Cache the log string */
306
0
    char tmpLogIdStr[128];
307
0
    mp_snprintf(tmpLogIdStr, 128, "%SDataSetReader %N\t| ",
308
0
                dsr->linkedReaderGroup->head.logIdString, dsr->head.identifier);
309
0
    dsr->head.logIdString = UA_STRING_ALLOC(tmpLogIdStr);
310
311
    /* Connect to StandaloneSubscribedDataSet if a name is defined. Needs to be
312
     * added in the information model first, as this adds references to the
313
     * StandaloneSubscribedDataSet. */
314
0
    retVal = connectDSR2Standalone(psm, dsr);
315
0
    if(retVal != UA_STATUSCODE_GOOD) {
316
0
        UA_DataSetReader_remove(psm, dsr);
317
0
        return retVal;
318
0
    }
319
320
    /* Validate the config */
321
0
    retVal = validateDSRConfig(psm, dsr);
322
0
    if(retVal != UA_STATUSCODE_GOOD) {
323
0
        UA_DataSetReader_remove(psm, dsr);
324
0
        return retVal;
325
0
    }
326
327
    /* Notify the application that a new Reader was created.
328
     * This may internally adjust the config */
329
0
    UA_Server *server = psm->sc.server;
330
0
    if(server->config.pubSubConfig.componentLifecycleCallback) {
331
0
        UA_StatusCode res = server->config.pubSubConfig.
332
0
            componentLifecycleCallback(server, dsr->head.identifier,
333
0
                                       UA_PUBSUBCOMPONENT_DATASETREADER, false);
334
0
        if(res != UA_STATUSCODE_GOOD) {
335
0
            UA_DataSetReader_remove(psm, dsr);
336
0
            return res;
337
0
        }
338
0
    }
339
340
0
    UA_LOG_INFO_PUBSUB(psm->logging, dsr, "DataSetReader created (State: %s)",
341
0
                       UA_PubSubState_name(dsr->head.state));
342
343
0
    if(readerIdentifier)
344
0
        UA_NodeId_copy(&dsr->head.identifier, readerIdentifier);
345
346
    /* Enable the DataSetReader immediately if the enabled flag is set */
347
0
    if(dataSetReaderConfig->enabled)
348
0
        UA_DataSetReader_setPubSubState(psm, dsr, UA_PUBSUBSTATE_OPERATIONAL,
349
0
                                        UA_STATUSCODE_GOOD);
350
351
0
    return UA_STATUSCODE_GOOD;
352
0
}
353
354
UA_StatusCode
355
0
UA_DataSetReader_remove(UA_PubSubManager *psm, UA_DataSetReader *dsr) {
356
0
    UA_LOCK_ASSERT(&psm->sc.server->serviceMutex);
357
358
0
    UA_ReaderGroup *rg = dsr->linkedReaderGroup;
359
0
    UA_assert(rg);
360
361
    /* Check if the ReaderGroup is enabled */
362
0
    if(UA_PubSubState_isEnabled(rg->head.state)) {
363
0
        UA_LOG_WARNING_PUBSUB(psm->logging, dsr,
364
0
                              "Removal of the DataSetReader not possible while "
365
0
                              "the ReaderGroup is enabled");
366
0
        return UA_STATUSCODE_BADINTERNALERROR;
367
0
    }
368
369
    /* Check with the application if we can remove */
370
0
    UA_Server *server = psm->sc.server;
371
0
    if(server->config.pubSubConfig.componentLifecycleCallback) {
372
0
        UA_StatusCode res = server->config.pubSubConfig.
373
0
            componentLifecycleCallback(server, dsr->head.identifier,
374
0
                                       UA_PUBSUBCOMPONENT_DATASETREADER, true);
375
0
        if(res != UA_STATUSCODE_GOOD)
376
0
            return res;
377
0
    }
378
379
    /* Disable and signal to the application */
380
0
    UA_DataSetReader_setPubSubState(psm, dsr, UA_PUBSUBSTATE_DISABLED,
381
0
                                    UA_STATUSCODE_BADSHUTDOWN);
382
383
    /* Remove from information model */
384
0
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
385
0
    deleteNode(psm->sc.server, dsr->head.identifier, true);
386
0
#endif
387
388
    /* Check if a Standalone-SubscribedDataSet is associated with this reader
389
     * and disconnect it*/
390
0
    const UA_String sdsName = dsr->config.linkedStandaloneSubscribedDataSetName;
391
0
    UA_SubscribedDataSet *sds = (UA_String_isEmpty(&sdsName)) ?
392
0
        NULL : UA_SubscribedDataSet_findByName(psm, sdsName);
393
0
    if(sds && sds->connectedReader == dsr)
394
0
        sds->connectedReader = NULL;
395
396
    /* Remove DataSetReader from group */
397
0
    LIST_REMOVE(dsr, listEntry);
398
0
    rg->readersCount--;
399
400
0
    UA_LOG_INFO_PUBSUB(psm->logging, dsr, "DataSetReader deleted");
401
402
0
    UA_DataSetReaderConfig_clear(&dsr->config);
403
0
    UA_PubSubComponentHead_clear(&dsr->head);
404
0
    UA_free(dsr);
405
406
0
    return UA_STATUSCODE_GOOD;
407
0
}
408
409
UA_StatusCode
410
UA_DataSetReaderConfig_copy(const UA_DataSetReaderConfig *src,
411
0
                            UA_DataSetReaderConfig *dst) {
412
0
    memcpy(dst, src, sizeof(UA_DataSetReaderConfig));
413
0
    dst->writerGroupId = src->writerGroupId;
414
0
    dst->dataSetWriterId = src->dataSetWriterId;
415
0
    dst->dataSetFieldContentMask = src->dataSetFieldContentMask;
416
0
    dst->messageReceiveTimeout = src->messageReceiveTimeout;
417
418
0
    UA_StatusCode ret = UA_String_copy(&src->name, &dst->name);
419
0
    ret |= UA_PublisherId_copy(&src->publisherId, &dst->publisherId);
420
0
    ret |= UA_DataSetMetaDataType_copy(&src->dataSetMetaData, &dst->dataSetMetaData);
421
0
    ret |= UA_ExtensionObject_copy(&src->messageSettings, &dst->messageSettings);
422
0
    ret |= UA_ExtensionObject_copy(&src->transportSettings, &dst->transportSettings);
423
0
    ret |= UA_String_copy(&src->linkedStandaloneSubscribedDataSetName,
424
0
                             &dst->linkedStandaloneSubscribedDataSetName);
425
426
0
    if(src->subscribedDataSetType == UA_PUBSUB_SDS_TARGET) {
427
0
        ret |= UA_TargetVariablesDataType_copy(&src->subscribedDataSet.target,
428
0
                                               &dst->subscribedDataSet.target);
429
0
    }
430
431
0
    if(ret != UA_STATUSCODE_GOOD)
432
0
        UA_DataSetReaderConfig_clear(dst);
433
434
0
    return ret;
435
0
}
436
437
void
438
0
UA_DataSetReaderConfig_clear(UA_DataSetReaderConfig *cfg) {
439
0
    UA_String_clear(&cfg->name);
440
0
    UA_String_clear(&cfg->linkedStandaloneSubscribedDataSetName);
441
0
    UA_PublisherId_clear(&cfg->publisherId);
442
0
    UA_DataSetMetaDataType_clear(&cfg->dataSetMetaData);
443
0
    UA_ExtensionObject_clear(&cfg->messageSettings);
444
0
    UA_ExtensionObject_clear(&cfg->transportSettings);
445
0
    if(cfg->subscribedDataSetType == UA_PUBSUB_SDS_TARGET) {
446
0
        UA_TargetVariablesDataType_clear(&cfg->subscribedDataSet.target);
447
0
    }
448
0
}
449
450
UA_StatusCode
451
UA_DataSetReader_setPubSubState(UA_PubSubManager *psm, UA_DataSetReader *dsr,
452
0
                                UA_PubSubState targetState, UA_StatusCode errorReason) {
453
0
    UA_ReaderGroup *rg = dsr->linkedReaderGroup;
454
0
    UA_assert(rg);
455
456
    /* Callback to modify the WriterGroup config and change the targetState
457
     * before the state machine executes */
458
0
    UA_Server *server = psm->sc.server;
459
0
    if(server->config.pubSubConfig.beforeStateChangeCallback) {
460
0
        server->config.pubSubConfig.
461
0
            beforeStateChangeCallback(server, dsr->head.identifier, &targetState);
462
0
    }
463
464
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
465
0
    UA_PubSubState oldState = dsr->head.state;
466
467
    /* Custom state machine */
468
0
    if(dsr->config.customStateMachine) {
469
0
        res = dsr->config.customStateMachine(server, dsr->head.identifier,
470
0
                                             dsr->config.context,
471
0
                                             &dsr->head.state, targetState);
472
0
        if(res != UA_STATUSCODE_GOOD)
473
0
            errorReason = res;
474
0
        goto finalize_state_machine;
475
0
    }
476
477
    /* Internal state machine */
478
0
    switch(targetState) {
479
        /* Disabled */
480
0
    case UA_PUBSUBSTATE_DISABLED:
481
0
    case UA_PUBSUBSTATE_ERROR:
482
0
        dsr->head.state = targetState;
483
0
        break;
484
485
        /* Enabled */
486
0
    case UA_PUBSUBSTATE_PAUSED:
487
0
    case UA_PUBSUBSTATE_PREOPERATIONAL:
488
0
    case UA_PUBSUBSTATE_OPERATIONAL:
489
0
        if(rg->head.state == UA_PUBSUBSTATE_DISABLED ||
490
0
           rg->head.state == UA_PUBSUBSTATE_ERROR ||
491
0
           rg->head.state == UA_PUBSUBSTATE_PAUSED) {
492
0
            dsr->head.state = UA_PUBSUBSTATE_PAUSED; /* RG is disabled -> paused */
493
0
        } else {
494
0
            dsr->head.state = rg->head.state; /* RG is enabled -> same state */
495
0
        }
496
0
        break;
497
498
0
    default:
499
0
        dsr->head.state = UA_PUBSUBSTATE_ERROR;
500
0
        res = UA_STATUSCODE_BADINTERNALERROR;
501
0
        errorReason = res;
502
0
        break;
503
0
    }
504
505
    /* Only keep the timeout callback if the reader is operational */
506
0
    if(dsr->head.state != UA_PUBSUBSTATE_OPERATIONAL &&
507
0
       dsr->msgRcvTimeoutTimerId != 0) {
508
0
        UA_EventLoop *el = psm->sc.server->config.eventLoop;
509
0
        el->removeTimer(el, dsr->msgRcvTimeoutTimerId);
510
0
        dsr->msgRcvTimeoutTimerId = 0;
511
0
    }
512
513
0
 finalize_state_machine:
514
515
    /* No state change has happened */
516
0
    if(dsr->head.state == oldState)
517
0
        return res;
518
519
0
    UA_LOG_INFO_PUBSUB(psm->logging, dsr, "%s -> %s",
520
0
                       UA_PubSubState_name(oldState),
521
0
                       UA_PubSubState_name(dsr->head.state));
522
523
    /* Inform application about state change */
524
0
    if(server->config.pubSubConfig.stateChangeCallback)
525
0
        server->config.pubSubConfig.
526
0
            stateChangeCallback(server, dsr->head.identifier,
527
0
                                dsr->head.state, errorReason);
528
                                
529
0
    return res;
530
0
}
531
532
/* This Method is used to initially set the SubscribedDataSet to
533
 * TargetVariablesType and to create the list of target Variables of a
534
 * SubscribedDataSetType. */
535
UA_StatusCode
536
DataSetReader_createTargetVariables(UA_PubSubManager *psm, UA_DataSetReader *dsr,
537
0
                                    size_t tvsSize, const UA_FieldTargetDataType *tvs) {
538
0
    UA_LOCK_ASSERT(&psm->sc.server->serviceMutex);
539
540
0
    if(UA_PubSubState_isEnabled(dsr->head.state)) {
541
0
        UA_LOG_WARNING_PUBSUB(psm->logging, dsr,
542
0
                              "Cannot create Target Variables failed while "
543
0
                              "the DataSetReader is enabled");
544
0
        return UA_STATUSCODE_BADCONFIGURATIONERROR;
545
0
    }
546
547
0
    UA_TargetVariablesDataType newVars;
548
0
    UA_TargetVariablesDataType tmp = {tvsSize, (UA_FieldTargetDataType*)(uintptr_t)tvs};
549
0
    UA_StatusCode res = UA_TargetVariablesDataType_copy(&tmp, &newVars);   
550
0
    if(res != UA_STATUSCODE_GOOD)
551
0
        return res;
552
553
0
    UA_TargetVariablesDataType_clear(&dsr->config.subscribedDataSet.target);
554
0
    dsr->config.subscribedDataSet.target = newVars;
555
0
    return UA_STATUSCODE_GOOD;
556
0
}
557
558
static void
559
UA_DataSetReader_handleMessageReceiveTimeout(UA_PubSubManager *psm,
560
0
                                             UA_DataSetReader *dsr) {
561
0
    UA_assert(dsr->head.componentType == UA_PUBSUBCOMPONENT_DATASETREADER);
562
563
    /* Don't signal an error if we don't expect messages to arrive */
564
0
    if(dsr->head.state != UA_PUBSUBSTATE_OPERATIONAL &&
565
0
       dsr->head.state != UA_PUBSUBSTATE_PREOPERATIONAL)
566
0
        return;
567
568
0
    UA_LOG_DEBUG_PUBSUB(psm->logging, dsr, "Message receive timeout occurred");
569
570
0
    lockServer(psm->sc.server);
571
0
    UA_DataSetReader_setPubSubState(psm, dsr, UA_PUBSUBSTATE_ERROR,
572
0
                                    UA_STATUSCODE_BADTIMEOUT);
573
0
    unlockServer(psm->sc.server);
574
0
}
575
576
void
577
UA_DataSetReader_process(UA_PubSubManager *psm, UA_DataSetReader *dsr,
578
0
                         UA_DataSetMessage *msg) {
579
0
    if(!dsr || !msg || !psm)
580
0
        return;
581
582
0
    UA_LOG_DEBUG_PUBSUB(psm->logging, dsr, "Received a network message");
583
584
    /* Received a (first) message for the Reader.
585
     * Transition from PreOperational to Operational. */
586
0
    if(dsr->head.state == UA_PUBSUBSTATE_PREOPERATIONAL)
587
0
        UA_DataSetReader_setPubSubState(psm, dsr, dsr->head.state, UA_STATUSCODE_GOOD);
588
589
0
    if(dsr->head.state != UA_PUBSUBSTATE_OPERATIONAL &&
590
0
       dsr->head.state != UA_PUBSUBSTATE_PREOPERATIONAL) {
591
0
        UA_LOG_WARNING_PUBSUB(psm->logging, dsr,
592
0
                              "Received a network message but not operational");
593
0
        return;
594
0
    }
595
596
0
    if(!msg->header.dataSetMessageValid) {
597
0
        UA_LOG_INFO_PUBSUB(psm->logging, dsr,
598
0
                           "DataSetMessage is discarded: message is not valid");
599
0
        return;
600
0
    }
601
602
    /* TODO: Check ConfigurationVersion */
603
    /* if(msg->header.configVersionMajorVersionEnabled) {
604
     *     if(msg->header.configVersionMajorVersion !=
605
     *            dsr->config.dataSetMetaData.configurationVersion.majorVersion) {
606
     *         UA_LOG_WARNING(psm->logging, UA_LOGCATEGORY_SERVER,
607
     *                        "DataSetMessage is discarded: ConfigurationVersion "
608
     *                        "MajorVersion does not match");
609
     *         return;
610
     *     }
611
     * } */
612
613
0
    if(msg->header.dataSetMessageType != UA_DATASETMESSAGE_DATAKEYFRAME) {
614
0
        UA_LOG_WARNING_PUBSUB(psm->logging, dsr,
615
0
                              "DataSetMessage is discarded: Only keyframes are supported");
616
0
        return;
617
0
    }
618
619
    /* Configure / Update the timeout callback */
620
0
    if(dsr->config.messageReceiveTimeout > 0.0) {
621
0
        UA_EventLoop *el = psm->sc.server->config.eventLoop;
622
0
        if(dsr->msgRcvTimeoutTimerId == 0) {
623
0
            el->addTimer(el, (UA_Callback)UA_DataSetReader_handleMessageReceiveTimeout,
624
0
                         psm, dsr, dsr->config.messageReceiveTimeout, NULL,
625
0
                         UA_TIMERPOLICY_CURRENTTIME, &dsr->msgRcvTimeoutTimerId);
626
0
        } else {
627
            /* Reset the next execution time to now + interval */
628
0
            el->modifyTimer(el, dsr->msgRcvTimeoutTimerId,
629
0
                            dsr->config.messageReceiveTimeout, NULL,
630
0
                            UA_TIMERPOLICY_CURRENTTIME);
631
0
        }
632
0
    }
633
634
    /* Received a heartbeat with no fields */
635
0
    if(msg->fieldCount == 0)
636
0
        return;
637
638
    /* Check whether the field count matches the configuration */
639
0
    UA_TargetVariablesDataType *tvs = &dsr->config.subscribedDataSet.target;
640
0
    if(tvs->targetVariablesSize != msg->fieldCount) {
641
0
        UA_LOG_WARNING_PUBSUB(psm->logging, dsr,
642
0
                              "Number of fields does not match the "
643
0
                              "TargetVariables configuration");
644
0
        return;
645
0
    }
646
647
    /* Write the message fields. RT has the external data value configured. */
648
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
649
0
    for(size_t i = 0; i < msg->fieldCount; i++) {
650
0
        UA_FieldTargetDataType *tv = &tvs->targetVariables[i];
651
0
        UA_DataValue *field = &msg->data.keyFrameFields[i];
652
0
        if(!field->hasValue)
653
0
            continue;
654
655
        /* Write via the Write-Service */
656
0
        UA_WriteValue writeVal;
657
0
        UA_WriteValue_init(&writeVal);
658
0
        writeVal.attributeId = tv->attributeId;
659
0
        writeVal.indexRange = tv->receiverIndexRange;
660
0
        writeVal.nodeId = tv->targetNodeId;
661
0
        writeVal.value = *field;
662
0
        Operation_Write(psm->sc.server, &psm->sc.server->adminSession, &writeVal, &res);
663
0
        if(res != UA_STATUSCODE_GOOD)
664
0
            UA_LOG_INFO_PUBSUB(psm->logging, dsr,
665
0
                               "Error writing KeyFrame field %u: %s",
666
0
                               (unsigned)i, UA_StatusCode_name(res));
667
0
    }
668
0
}
669
670
/**************/
671
/* Server API */
672
/**************/
673
674
UA_StatusCode
675
UA_Server_addDataSetReader(UA_Server *server, UA_NodeId readerGroupId,
676
                           const UA_DataSetReaderConfig *config,
677
0
                           UA_NodeId *dsrId) {
678
0
    if(!server || !config)
679
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
680
0
    lockServer(server);
681
0
    UA_StatusCode res =
682
0
        UA_DataSetReader_create(getPSM(server), readerGroupId, config, dsrId);
683
0
    unlockServer(server);
684
0
    return res;
685
0
}
686
687
UA_StatusCode
688
0
UA_Server_removeDataSetReader(UA_Server *server, const UA_NodeId readerId) {
689
0
    if(!server)
690
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
691
0
    lockServer(server);
692
0
    UA_PubSubManager *psm = getPSM(server);
693
0
    UA_DataSetReader *dsr = UA_DataSetReader_find(psm, readerId);
694
0
    UA_StatusCode res = (dsr) ?
695
0
        UA_DataSetReader_remove(psm, dsr) : UA_STATUSCODE_BADNOTFOUND;
696
0
    unlockServer(server);
697
0
    return res;
698
0
}
699
700
UA_StatusCode
701
UA_Server_getDataSetReaderConfig(UA_Server *server, const UA_NodeId dsrId,
702
0
                                 UA_DataSetReaderConfig *config) {
703
0
    if(!server || !config)
704
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
705
0
    lockServer(server);
706
0
    UA_PubSubManager *psm = getPSM(server);
707
0
    UA_DataSetReader *dsr = UA_DataSetReader_find(psm, dsrId);
708
0
    UA_StatusCode res = (dsr) ?
709
0
        UA_DataSetReaderConfig_copy(&dsr->config, config) : UA_STATUSCODE_BADNOTFOUND;
710
0
    unlockServer(server);
711
0
    return res;
712
0
}
713
714
UA_StatusCode
715
UA_Server_getDataSetReaderState(UA_Server *server, const UA_NodeId dsrId,
716
0
                                UA_PubSubState *state) {
717
0
    if(!server || !state)
718
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
719
0
    lockServer(server);
720
0
    UA_DataSetReader *dsr = UA_DataSetReader_find(getPSM(server), dsrId);
721
0
    UA_StatusCode res = UA_STATUSCODE_BADNOTFOUND;
722
0
    if(dsr) {
723
0
        res = UA_STATUSCODE_GOOD;
724
0
        *state = dsr->head.state;
725
0
    }
726
0
    unlockServer(server);
727
0
    return res;
728
0
}
729
730
UA_StatusCode
731
0
UA_Server_enableDataSetReader(UA_Server *server, const UA_NodeId dsrId) {
732
0
    if(!server)
733
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
734
0
    lockServer(server);
735
0
    UA_StatusCode ret = UA_STATUSCODE_GOOD;
736
0
    UA_PubSubManager *psm = getPSM(server);
737
0
    UA_DataSetReader *dsr = UA_DataSetReader_find(psm, dsrId);
738
0
    if(dsr)
739
0
        UA_DataSetReader_setPubSubState(psm, dsr, UA_PUBSUBSTATE_OPERATIONAL,
740
0
                                        UA_STATUSCODE_GOOD);
741
0
    else
742
0
        ret = UA_STATUSCODE_BADNOTFOUND;
743
0
    unlockServer(server);
744
0
    return ret;
745
0
}
746
747
UA_StatusCode
748
0
UA_Server_disableDataSetReader(UA_Server *server, const UA_NodeId dsrId) {
749
0
    if(!server)
750
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
751
0
    lockServer(server);
752
0
    UA_StatusCode ret = UA_STATUSCODE_GOOD;
753
0
    UA_PubSubManager *psm = getPSM(server);
754
0
    UA_DataSetReader *dsr = UA_DataSetReader_find(psm, dsrId);
755
0
    if(dsr)
756
0
        UA_DataSetReader_setPubSubState(psm, dsr, UA_PUBSUBSTATE_DISABLED,
757
0
                                        UA_STATUSCODE_GOOD);
758
0
    else
759
0
        ret = UA_STATUSCODE_BADNOTFOUND;
760
0
    unlockServer(server);
761
0
    return ret;
762
0
}
763
764
UA_StatusCode
765
UA_Server_setDataSetReaderTargetVariables(UA_Server *server, const UA_NodeId dsrId,
766
                                          size_t targetVariablesSize,
767
0
                                          const UA_FieldTargetDataType *targetVariables) {
768
0
    if(!server)
769
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
770
0
    lockServer(server);
771
0
    UA_PubSubManager *psm = getPSM(server);
772
0
    UA_DataSetReader *dsr = UA_DataSetReader_find(psm, dsrId);
773
0
    UA_StatusCode res = (dsr) ?
774
0
        DataSetReader_createTargetVariables(psm, dsr, targetVariablesSize,
775
0
                                            targetVariables) : UA_STATUSCODE_BADNOTFOUND;
776
0
    unlockServer(server);
777
0
    return res;
778
0
}
779
780
UA_StatusCode
781
UA_Server_updateDataSetReaderConfig(UA_Server *server, const UA_NodeId dsrId,
782
0
                                    const UA_DataSetReaderConfig *config) {
783
0
    if(!server || !config)
784
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
785
786
0
    lockServer(server);
787
0
    UA_PubSubManager *psm = getPSM(server);
788
0
    UA_DataSetReader *dsr = UA_DataSetReader_find(psm, dsrId);
789
0
    if(!dsr) {
790
0
        unlockServer(server);
791
0
        return UA_STATUSCODE_BADNOTFOUND;
792
0
    }
793
794
0
    if(UA_PubSubState_isEnabled(dsr->head.state)) {
795
0
        UA_LOG_ERROR_PUBSUB(psm->logging, dsr,
796
0
                            "The DataSetReader must be disabled to update the config");
797
0
        unlockServer(server);
798
0
        return UA_STATUSCODE_BADINTERNALERROR;
799
0
    }
800
801
    /* Store the old config */
802
0
    UA_DataSetReaderConfig oldConfig = dsr->config;
803
804
    /* Copy the config into the new dataSetReader */
805
0
    UA_StatusCode retVal = UA_DataSetReaderConfig_copy(config, &dsr->config);
806
0
    if(retVal != UA_STATUSCODE_GOOD)
807
0
        goto errout;
808
809
    /* Change the connection to a StandaloneSubscribedDataSet */
810
0
    if(!UA_String_equal(&dsr->config.linkedStandaloneSubscribedDataSetName,
811
0
                        &oldConfig.linkedStandaloneSubscribedDataSetName)) {
812
0
        disconnectDSR2Standalone(psm, dsr);
813
0
        retVal = connectDSR2Standalone(psm, dsr);
814
0
        if(retVal != UA_STATUSCODE_GOOD)
815
0
            goto errout;
816
0
    }
817
818
    /* Validate the new config */
819
0
    retVal = validateDSRConfig(psm, dsr);
820
0
    if(retVal != UA_STATUSCODE_GOOD)
821
0
        goto errout;
822
823
    /* Clean up and return */
824
0
    UA_DataSetReaderConfig_clear(&oldConfig);
825
0
    unlockServer(server);
826
0
    return UA_STATUSCODE_GOOD;
827
828
    /* Fall back to the old config */
829
0
 errout:
830
0
    UA_DataSetReaderConfig_clear(&dsr->config);
831
0
    dsr->config = oldConfig;
832
0
    unlockServer(server);
833
0
    return retVal;
834
0
}
835
836
/**********************/
837
/* Offset Computation */
838
/**********************/
839
840
static UA_StatusCode
841
UA_PubSubDataSetReader_generateKeyFrameMessage(UA_Server *server,
842
                                               UA_DataSetMessage *dsm,
843
0
                                               UA_DataSetReader *dsr) {
844
    /* Prepare DataSetMessageContent */
845
0
    UA_TargetVariablesDataType *tv = &dsr->config.subscribedDataSet.target;
846
0
    UA_DataSetMetaDataType *metaData = &dsr->config.dataSetMetaData;
847
0
    if(tv->targetVariablesSize != metaData->fieldsSize)
848
0
        metaData = NULL;
849
0
    dsm->header.dataSetMessageValid = true;
850
0
    dsm->header.dataSetMessageType = UA_DATASETMESSAGE_DATAKEYFRAME;
851
0
    dsm->fieldCount = (UA_UInt16) tv->targetVariablesSize;
852
0
    dsm->data.keyFrameFields = (UA_DataValue *)
853
0
            UA_Array_new(tv->targetVariablesSize, &UA_TYPES[UA_TYPES_DATAVALUE]);
854
0
    if(!dsm->data.keyFrameFields)
855
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
856
857
0
     for(size_t counter = 0; counter < tv->targetVariablesSize; counter++) {
858
        /* Read the value and set the source in the reader config */
859
0
        UA_DataValue *dfv = &dsm->data.keyFrameFields[counter];
860
0
        UA_FieldTargetDataType *ftv = &tv->targetVariables[counter];
861
862
        /* Synthesize the field value from the FieldMetaData. This allows us to
863
         * prevent a read from the information model during startup. */
864
0
        UA_FieldMetaData *fieldMetaData = (metaData) ? &metaData->fields[counter] : NULL;
865
0
        if(fieldMetaData && fieldMetaData->valueRank == UA_VALUERANK_SCALAR) {
866
0
            const UA_DataType *type =
867
0
                UA_findDataTypeWithCustom(&fieldMetaData->dataType,
868
0
                                          server->config.customDataTypes);
869
0
            if(type == &UA_TYPES[UA_TYPES_STRING] && fieldMetaData->maxStringLength > 0) {
870
0
                UA_String *s = UA_String_new();
871
0
                if(!s) {
872
0
                    UA_DataSetMessage_clear(dsm);
873
0
                    return UA_STATUSCODE_BADOUTOFMEMORY;
874
0
                }
875
0
                s->data = (UA_Byte*)
876
0
                    UA_calloc(fieldMetaData->maxStringLength, sizeof(UA_Byte));
877
0
                if(!s->data) {
878
0
                    UA_free(s);
879
0
                    UA_DataSetMessage_clear(dsm);
880
0
                    return UA_STATUSCODE_BADOUTOFMEMORY;
881
0
                }
882
0
                s->length = fieldMetaData->maxStringLength;
883
0
                UA_Variant_setScalar(&dfv->value, s, type);
884
0
                dfv->hasValue = true;
885
0
            } else if(type && type->memSize < 512) {
886
0
                char buf[512];
887
0
                UA_init(buf, type);
888
0
                UA_StatusCode res = UA_Variant_setScalarCopy(&dfv->value, buf, type);
889
0
                if(res != UA_STATUSCODE_GOOD) {
890
0
                    UA_DataSetMessage_clear(dsm);
891
0
                    return res;
892
0
                }
893
0
                dfv->hasValue = true;
894
0
            }
895
0
        }
896
897
        /* Read the value from the information model */
898
0
        if(!dfv->hasValue) {
899
0
            UA_ReadValueId rvi;
900
0
            UA_ReadValueId_init(&rvi);
901
0
            rvi.nodeId = ftv->targetNodeId;
902
0
            rvi.attributeId = ftv->attributeId;
903
0
            rvi.indexRange = ftv->writeIndexRange;
904
0
            *dfv = readWithSession(server, &server->adminSession, &rvi,
905
0
                                   UA_TIMESTAMPSTORETURN_NEITHER);
906
0
        }
907
908
        /* Deactivate statuscode? */
909
0
        if(((u64)dsr->config.dataSetFieldContentMask &
910
0
            (u64)UA_DATASETFIELDCONTENTMASK_STATUSCODE) == 0)
911
0
            dfv->hasStatus = false;
912
913
        /* Deactivate timestamps */
914
0
        if(((u64)dsr->config.dataSetFieldContentMask &
915
0
            (u64)UA_DATASETFIELDCONTENTMASK_SOURCETIMESTAMP) == 0)
916
0
            dfv->hasSourceTimestamp = false;
917
0
        if(((u64)dsr->config.dataSetFieldContentMask &
918
0
            (u64)UA_DATASETFIELDCONTENTMASK_SOURCEPICOSECONDS) == 0)
919
0
            dfv->hasSourcePicoseconds = false;
920
0
        if(((u64)dsr->config.dataSetFieldContentMask &
921
0
            (u64)UA_DATASETFIELDCONTENTMASK_SERVERTIMESTAMP) == 0)
922
0
            dfv->hasServerTimestamp = false;
923
0
        if(((u64)dsr->config.dataSetFieldContentMask &
924
0
            (u64)UA_DATASETFIELDCONTENTMASK_SERVERPICOSECONDS) == 0)
925
0
            dfv->hasServerPicoseconds = false;
926
0
    }
927
928
0
    return UA_STATUSCODE_GOOD;
929
0
}
930
931
/* Generate a DataSetMessage for the given reader. */
932
UA_StatusCode
933
UA_DataSetReader_generateDataSetMessage(UA_Server *server,
934
                                        UA_DataSetMessage *dsm,
935
0
                                        UA_DataSetReader *dsr) {
936
    /* Support only for UADP configuration
937
     * TODO: JSON encoding if UA_DataSetReader_generateDataSetMessage used other
938
     * that RT configuration */
939
940
0
    UA_ExtensionObject *settings = &dsr->config.messageSettings;
941
0
    if(settings->content.decoded.type != &UA_TYPES[UA_TYPES_UADPDATASETREADERMESSAGEDATATYPE])
942
0
        return UA_STATUSCODE_BADNOTSUPPORTED;
943
944
    /* The configuration Flags are included inside the std. defined
945
     * UA_UadpDataSetReaderMessageDataType */
946
0
    UA_UadpDataSetReaderMessageDataType defaultUadpConfiguration;
947
0
    UA_UadpDataSetReaderMessageDataType *dsrMessageDataType =
948
0
        (UA_UadpDataSetReaderMessageDataType*) settings->content.decoded.data;
949
950
0
    if(!(settings->encoding == UA_EXTENSIONOBJECT_DECODED ||
951
0
         settings->encoding == UA_EXTENSIONOBJECT_DECODED_NODELETE) ||
952
0
       !dsrMessageDataType->dataSetMessageContentMask) {
953
        /* Create default flag configuration if no dataSetMessageContentMask or
954
         * even messageSettings in UadpDataSetWriterMessageDataType was
955
         * passed. */
956
0
        memset(&defaultUadpConfiguration, 0, sizeof(UA_UadpDataSetReaderMessageDataType));
957
0
        defaultUadpConfiguration.dataSetMessageContentMask = (UA_UadpDataSetMessageContentMask)
958
0
            ((u64)UA_UADPDATASETMESSAGECONTENTMASK_TIMESTAMP |
959
0
             (u64)UA_UADPDATASETMESSAGECONTENTMASK_MAJORVERSION |
960
0
             (u64)UA_UADPDATASETMESSAGECONTENTMASK_MINORVERSION);
961
0
        dsrMessageDataType = &defaultUadpConfiguration;
962
0
    }
963
964
    /* The field encoding depends on the flags inside the reader config. */
965
0
    if(dsr->config.dataSetFieldContentMask & (u64)UA_DATASETFIELDCONTENTMASK_RAWDATA) {
966
0
        dsm->header.fieldEncoding = UA_FIELDENCODING_RAWDATA;
967
0
    } else if((u64)dsr->config.dataSetFieldContentMask &
968
0
              ((u64)UA_DATASETFIELDCONTENTMASK_SOURCETIMESTAMP |
969
0
               (u64)UA_DATASETFIELDCONTENTMASK_SERVERPICOSECONDS |
970
0
               (u64)UA_DATASETFIELDCONTENTMASK_SOURCEPICOSECONDS |
971
0
               (u64)UA_DATASETFIELDCONTENTMASK_STATUSCODE)) {
972
0
        dsm->header.fieldEncoding = UA_FIELDENCODING_DATAVALUE;
973
0
    } else {
974
0
        dsm->header.fieldEncoding = UA_FIELDENCODING_VARIANT;
975
0
    }
976
977
    /* Std: 'The DataSetMessageContentMask defines the flags for the content
978
     * of the DataSetMessage header.' */
979
0
    if((u64)dsrMessageDataType->dataSetMessageContentMask &
980
0
       (u64)UA_UADPDATASETMESSAGECONTENTMASK_MAJORVERSION) {
981
0
        dsm->header.configVersionMajorVersionEnabled = true;
982
0
        dsm->header.configVersionMajorVersion =
983
0
            dsr->config.dataSetMetaData.configurationVersion.majorVersion;
984
0
    }
985
986
0
    if((u64)dsrMessageDataType->dataSetMessageContentMask &
987
0
       (u64)UA_UADPDATASETMESSAGECONTENTMASK_MINORVERSION) {
988
0
        dsm->header.configVersionMinorVersionEnabled = true;
989
0
        dsm->header.configVersionMinorVersion =
990
0
            dsr->config.dataSetMetaData.configurationVersion.minorVersion;
991
0
    }
992
993
0
    if((u64)dsrMessageDataType->dataSetMessageContentMask &
994
0
       (u64)UA_UADPDATASETMESSAGECONTENTMASK_SEQUENCENUMBER) {
995
        /* Will be modified when subscriber receives new nw msg */
996
0
        dsm->header.dataSetMessageSequenceNrEnabled = true;
997
0
        dsm->header.dataSetMessageSequenceNr = 1;
998
0
    }
999
1000
0
    if((u64)dsrMessageDataType->dataSetMessageContentMask &
1001
0
       (u64)UA_UADPDATASETMESSAGECONTENTMASK_TIMESTAMP) {
1002
0
        dsm->header.timestampEnabled = true;
1003
0
        dsm->header.timestamp = UA_DateTime_now();
1004
0
    }
1005
1006
0
    if((u64)dsrMessageDataType->dataSetMessageContentMask &
1007
0
       (u64)UA_UADPDATASETMESSAGECONTENTMASK_PICOSECONDS)
1008
0
        dsm->header.picoSecondsIncluded = false;
1009
1010
0
    if((u64)dsrMessageDataType->dataSetMessageContentMask &
1011
0
       (u64)UA_UADPDATASETMESSAGECONTENTMASK_STATUS)
1012
0
        dsm->header.statusEnabled = true;
1013
1014
    /* Not supported for Delta frames atm */
1015
0
    return UA_PubSubDataSetReader_generateKeyFrameMessage(server, dsm, dsr);
1016
0
}
1017
1018
1019
UA_StatusCode
1020
UA_Server_computeDataSetReaderOffsetTable(UA_Server *server,
1021
                                          const UA_NodeId dataSetReaderId,
1022
0
                                          UA_PubSubOffsetTable *ot) {
1023
    /* Validate the arguments */
1024
0
    if(!server || !ot)
1025
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
1026
1027
0
    lockServer(server);
1028
1029
    /* Get the DataSetReader */
1030
0
    UA_PubSubManager *psm = getPSM(server);
1031
0
    UA_DataSetReader *dsr = UA_DataSetReader_find(psm, dataSetReaderId);
1032
0
    if(!dsr) {
1033
0
        unlockServer(server);
1034
0
        return UA_STATUSCODE_BADNOTFOUND;
1035
0
    }
1036
1037
    /* Generate the DataSetMessage */
1038
0
    UA_DataSetMessage dsm;
1039
0
    memset(&dsm, 0, sizeof(UA_DataSetMessage));
1040
0
    UA_StatusCode res = UA_DataSetReader_generateDataSetMessage(server, &dsm, dsr);
1041
0
    if(res != UA_STATUSCODE_GOOD) {
1042
0
        unlockServer(server);
1043
0
        return res;
1044
0
    }
1045
1046
    /* Reset the OffsetTable */
1047
0
    memset(ot, 0, sizeof(UA_PubSubOffsetTable));
1048
1049
    /* Prepare the encoding context */
1050
0
    UA_DataSetMessage_EncodingMetaData emd;
1051
0
    memset(&emd, 0, sizeof(UA_DataSetMessage_EncodingMetaData));
1052
0
    emd.dataSetWriterId = dsr->config.dataSetWriterId;
1053
0
    emd.fields = dsr->config.dataSetMetaData.fields;
1054
0
    emd.fieldsSize = dsr->config.dataSetMetaData.fieldsSize;
1055
1056
0
    PubSubEncodeCtx ctx;
1057
0
    memset(&ctx, 0, sizeof(PubSubEncodeCtx));
1058
0
    ctx.ot = ot;
1059
0
    ctx.eo.metaData = &emd;
1060
0
    ctx.eo.metaDataSize = 1;
1061
1062
    /* Compute the offset */
1063
0
    size_t fieldindex = 0;
1064
0
    UA_FieldTargetDataType *tv = NULL;
1065
0
    size_t msgSize = UA_DataSetMessage_calcSizeBinary(&ctx, &emd, &dsm, 0);
1066
0
    if(msgSize == 0) {
1067
0
        res = UA_STATUSCODE_BADINTERNALERROR;
1068
0
        goto errout;
1069
0
    }
1070
1071
    /* Allocate the message */
1072
0
    res = UA_ByteString_allocBuffer(&ot->networkMessage, msgSize);
1073
0
    if(res != UA_STATUSCODE_GOOD)
1074
0
        goto errout;
1075
1076
    /* Create the ByteString of the encoded DataSetMessage */
1077
0
    ctx.ctx.pos = ot->networkMessage.data;
1078
0
    ctx.ctx.end = ot->networkMessage.data + ot->networkMessage.length;
1079
0
    res = UA_DataSetMessage_encodeBinary(&ctx, &emd, &dsm);
1080
0
    if(res != UA_STATUSCODE_GOOD)
1081
0
        goto errout;
1082
1083
    /* Pick up the component NodeIds */
1084
0
    for(size_t i = 0; i < ot->offsetsSize; i++) {
1085
0
        UA_PubSubOffset *o = &ot->offsets[i];
1086
0
        switch(o->offsetType) {
1087
0
        case UA_PUBSUBOFFSETTYPE_DATASETMESSAGE_SEQUENCENUMBER:
1088
0
        case UA_PUBSUBOFFSETTYPE_DATASETMESSAGE_STATUS:
1089
0
        case UA_PUBSUBOFFSETTYPE_DATASETMESSAGE_TIMESTAMP:
1090
0
        case UA_PUBSUBOFFSETTYPE_DATASETMESSAGE_PICOSECONDS:
1091
0
            res |= UA_NodeId_copy(&dsr->head.identifier, &o->component);
1092
0
            break;
1093
0
        case UA_PUBSUBOFFSETTYPE_DATASETFIELD_DATAVALUE:
1094
0
            tv = &dsr->config.subscribedDataSet.target.targetVariables[fieldindex];
1095
0
            res |= UA_NodeId_copy(&tv->targetNodeId, &o->component);
1096
0
            fieldindex++;
1097
0
            break;
1098
0
        case UA_PUBSUBOFFSETTYPE_DATASETFIELD_VARIANT:
1099
0
            tv = &dsr->config.subscribedDataSet.target.targetVariables[fieldindex];
1100
0
            res |= UA_NodeId_copy(&tv->targetNodeId, &o->component);
1101
0
            fieldindex++;
1102
0
            break;
1103
0
        case UA_PUBSUBOFFSETTYPE_DATASETFIELD_RAW:
1104
0
            tv = &dsr->config.subscribedDataSet.target.targetVariables[fieldindex];
1105
0
            res |= UA_NodeId_copy(&tv->targetNodeId, &o->component);
1106
0
            fieldindex++;
1107
0
            break;
1108
0
        default:
1109
0
            res = UA_STATUSCODE_BADINTERNALERROR;
1110
0
            break;
1111
0
        }
1112
0
    }
1113
1114
    /* Clean up */
1115
0
 errout:
1116
0
    UA_DataSetMessage_clear(&dsm);
1117
0
    if(res != UA_STATUSCODE_GOOD)
1118
0
        UA_PubSubOffsetTable_clear(ot);
1119
0
    unlockServer(server);
1120
0
    return res;
1121
0
}
1122
1123
#endif /* UA_ENABLE_PUBSUB */