Coverage Report

Created: 2026-06-30 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541_15/src/pubsub/ua_pubsub_dataset.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-2019 Fraunhofer IOSB (Author: Andreas Ebner)
6
 * Copyright (c) 2019 Fraunhofer IOSB (Author: Julius Pfrommer)
7
 * Copyright (c) 2019-2021 Kalycito Infotech Private Limited
8
 * Copyright (c) 2020 Yannick Wallerer, Siemens AG
9
 * Copyright (c) 2020 Thomas Fischer, Siemens AG
10
 * Copyright (c) 2021 Fraunhofer IOSB (Author: Jan Hermes)
11
 */
12
13
#include <open62541/server_pubsub.h>
14
#include "ua_pubsub_internal.h"
15
16
#ifdef UA_ENABLE_PUBSUB /* conditional compilation */
17
18
static void
19
0
UA_DataSetField_clear(UA_DataSetField *field) {
20
0
    UA_DataSetFieldConfig_clear(&field->config);
21
0
    UA_NodeId_clear(&field->identifier);
22
0
    UA_NodeId_clear(&field->publishedDataSet);
23
0
    UA_FieldMetaData_clear(&field->fieldMetaData);
24
0
}
25
26
UA_StatusCode
27
UA_PublishedDataSetConfig_copy(const UA_PublishedDataSetConfig *src,
28
0
                               UA_PublishedDataSetConfig *dst) {
29
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
30
0
    memcpy(dst, src, sizeof(UA_PublishedDataSetConfig));
31
0
    res |= UA_String_copy(&src->name, &dst->name);
32
0
    switch(src->publishedDataSetType) {
33
0
        case UA_PUBSUB_DATASET_PUBLISHEDITEMS:
34
            //no additional items
35
0
            break;
36
37
0
        case UA_PUBSUB_DATASET_PUBLISHEDITEMS_TEMPLATE:
38
0
            if(src->config.itemsTemplate.variablesToAddSize > 0) {
39
0
                dst->config.itemsTemplate.variablesToAdd = (UA_PublishedVariableDataType *)
40
0
                    UA_calloc(src->config.itemsTemplate.variablesToAddSize,
41
0
                              sizeof(UA_PublishedVariableDataType));
42
0
                if(!dst->config.itemsTemplate.variablesToAdd) {
43
0
                    res = UA_STATUSCODE_BADOUTOFMEMORY;
44
0
                    break;
45
0
                }
46
0
                dst->config.itemsTemplate.variablesToAddSize =
47
0
                    src->config.itemsTemplate.variablesToAddSize;
48
0
            }
49
50
0
            for(size_t i = 0; i < src->config.itemsTemplate.variablesToAddSize; i++) {
51
0
                res |= UA_PublishedVariableDataType_copy(&src->config.itemsTemplate.variablesToAdd[i],
52
0
                                                         &dst->config.itemsTemplate.variablesToAdd[i]);
53
0
            }
54
0
            res |= UA_DataSetMetaDataType_copy(&src->config.itemsTemplate.metaData,
55
0
                                               &dst->config.itemsTemplate.metaData);
56
0
            break;
57
58
0
        default:
59
0
            res = UA_STATUSCODE_BADINVALIDARGUMENT;
60
0
            break;
61
0
    }
62
63
0
    if(res != UA_STATUSCODE_GOOD)
64
0
        UA_PublishedDataSetConfig_clear(dst);
65
0
    return res;
66
0
}
67
68
UA_PublishedDataSet *
69
0
UA_PublishedDataSet_find(UA_PubSubManager *psm, const UA_NodeId id) {
70
0
    if(!psm)
71
0
        return NULL;
72
0
    UA_PublishedDataSet *tmpPDS = NULL;
73
0
    TAILQ_FOREACH(tmpPDS, &psm->publishedDataSets, listEntry) {
74
0
        if(UA_NodeId_equal(&id, &tmpPDS->head.identifier))
75
0
            break;
76
0
    }
77
0
    return tmpPDS;
78
0
}
79
80
UA_PublishedDataSet *
81
0
UA_PublishedDataSet_findByName(UA_PubSubManager *psm, const UA_String name) {
82
0
    if(!psm)
83
0
        return NULL;
84
0
    UA_PublishedDataSet *tmpPDS = NULL;
85
0
    TAILQ_FOREACH(tmpPDS, &psm->publishedDataSets, listEntry) {
86
0
        if(UA_String_equal(&name, &tmpPDS->config.name))
87
0
            break;
88
0
    }
89
0
    return tmpPDS;
90
0
}
91
92
void
93
0
UA_PublishedDataSetConfig_clear(UA_PublishedDataSetConfig *pdsConfig) {
94
    //delete pds config
95
0
    UA_String_clear(&pdsConfig->name);
96
0
    switch (pdsConfig->publishedDataSetType){
97
0
        case UA_PUBSUB_DATASET_PUBLISHEDITEMS:
98
            //no additional items
99
0
            break;
100
0
        case UA_PUBSUB_DATASET_PUBLISHEDITEMS_TEMPLATE:
101
0
            if(pdsConfig->config.itemsTemplate.variablesToAddSize > 0){
102
0
                for(size_t i = 0; i < pdsConfig->config.itemsTemplate.variablesToAddSize; i++){
103
0
                    UA_PublishedVariableDataType_clear(&pdsConfig->config.itemsTemplate.variablesToAdd[i]);
104
0
                }
105
0
                UA_free(pdsConfig->config.itemsTemplate.variablesToAdd);
106
0
            }
107
0
            UA_DataSetMetaDataType_clear(&pdsConfig->config.itemsTemplate.metaData);
108
0
            break;
109
0
        default:
110
0
            break;
111
0
    }
112
0
}
113
114
/* The fieldMetaData variable has to be cleaned up external in case of an error */
115
static UA_StatusCode
116
generateFieldMetaData(UA_PubSubManager *psm, UA_PublishedDataSet *pds,
117
0
                      UA_DataSetField *field) {
118
0
    if(field->config.dataSetFieldType != UA_PUBSUB_DATASETFIELD_VARIABLE)
119
0
        return UA_STATUSCODE_BADNOTSUPPORTED;
120
121
0
    UA_FieldMetaData *fmd = &field->fieldMetaData;
122
0
    const UA_DataSetVariableConfig *var = &field->config.field.variable;
123
124
    /* Name */
125
0
    UA_StatusCode res = UA_String_copy(&var->fieldNameAlias, &fmd->name);
126
0
    UA_CHECK_STATUS(res, return res);
127
128
    /* Description */
129
0
    res = UA_LocalizedText_copy(&var->description, &fmd->description);
130
0
    UA_CHECK_STATUS(res, return res);
131
132
    /* FieldFlags */
133
0
    if(var->promotedField)
134
0
        fmd->fieldFlags = UA_DATASETFIELDFLAGS_PROMOTEDFIELD;
135
0
    else
136
0
        fmd->fieldFlags = UA_DATASETFIELDFLAGS_NONE;
137
138
    /* DataType */
139
0
    res = readWithReadValue(psm->sc.server, &var->publishParameters.publishedVariable,
140
0
                            UA_ATTRIBUTEID_DATATYPE, &fmd->dataType);
141
0
    if(res != UA_STATUSCODE_GOOD) {
142
0
        UA_LOG_ERROR_PUBSUB(psm->logging, pds,
143
0
                            "PubSub meta data generation: Reading the DataType failed");
144
0
        return res;
145
0
    }
146
147
    /* BuiltinType */
148
0
    const UA_DataType *type =
149
0
        UA_findDataTypeWithCustom(&fmd->dataType,
150
0
                                  psm->sc.server->config.customDataTypes);
151
0
    if(!type) {
152
        /* (1) Abstract types always have the built-in type Variant since they
153
         * can result in different concrete types in a DataSetMessage. */
154
0
        fmd->builtInType = UA_DATATYPEKIND_VARIANT;
155
0
    } else if(type->typeKind == UA_DATATYPEKIND_ENUM) {
156
        /* (2) Enumeration DataTypes are encoded as Int32. */
157
0
        fmd->builtInType = UA_DATATYPEKIND_INT32;
158
0
    } else if(type->typeKind == UA_DATATYPEKIND_STRUCTURE ||
159
0
              type->typeKind == UA_DATATYPEKIND_OPTSTRUCT ||
160
0
              type->typeKind == UA_DATATYPEKIND_UNION) {
161
        /* (3) Structure and Union DataTypes are encoded as ExtensionObject. */
162
0
        fmd->builtInType = UA_DATATYPEKIND_EXTENSIONOBJECT;
163
0
    } else if(type->typeKind <= UA_DATATYPEKIND_DIAGNOSTICINFO) {
164
        /* (4) DataTypes derived from built-in types have the BuiltInType of the
165
         * corresponding base DataType. */
166
0
        fmd->builtInType = type->typeKind;
167
0
    } else {
168
        /* (5) OptionSet DataTypes are either encoded as one of the concrete
169
           UInteger DataTypes or as an instance of an OptionSetType in an
170
           ExtensionObject. */
171
0
        fmd->builtInType = UA_DATATYPEKIND_EXTENSIONOBJECT;
172
0
    }
173
174
0
    fmd->builtInType++; /* Go from UA_DataTypeKind to the builtin type index from Part 6 */
175
176
    /* ValueRank */
177
0
    res = readWithReadValue(psm->sc.server, &var->publishParameters.publishedVariable,
178
0
                            UA_ATTRIBUTEID_VALUERANK, &fmd->valueRank);
179
0
    if(res != UA_STATUSCODE_GOOD) {
180
0
        UA_LOG_ERROR_PUBSUB(psm->logging, pds,
181
0
                            "PubSub meta data generation: Reading the ValueRank failed");
182
0
        return res;
183
0
    }
184
185
    /* ArrayDimensions */
186
0
    UA_Variant value;
187
0
    UA_Variant_init(&value);
188
0
    res = readWithReadValue(psm->sc.server, &var->publishParameters.publishedVariable,
189
0
                            UA_ATTRIBUTEID_ARRAYDIMENSIONS, &value);
190
0
    if(res != UA_STATUSCODE_GOOD) {
191
0
        UA_LOG_ERROR_PUBSUB(psm->logging, pds,
192
0
                            "PubSub meta data generation: Reading the ArrayDimensions failed");
193
0
        return res;
194
0
    }
195
0
    fmd->arrayDimensions = (UA_UInt32*)value.data;
196
0
    fmd->arrayDimensionsSize = value.arrayLength;
197
0
    value.data = NULL;
198
0
    value.arrayLength = 0;
199
0
    UA_Variant_clear(&value);
200
201
    /* MaxStringLength */
202
0
    if(field->config.field.variable.maxStringLength > 0) {
203
0
        if(fmd->builtInType - 1 == UA_DATATYPEKIND_BYTESTRING ||
204
0
           fmd->builtInType - 1 == UA_DATATYPEKIND_STRING) {
205
0
            fmd->maxStringLength = field->config.field.variable.maxStringLength;
206
0
        } else {
207
0
            UA_LOG_ERROR_PUBSUB(psm->logging, pds,
208
0
                                "PubSub meta data generation: MaxStringLength with "
209
0
                                "incompatible DataType configured");
210
0
        }
211
0
    }
212
213
    /* DataSetFieldId */
214
0
    if(!UA_Guid_equal(&var->dataSetFieldId, &UA_GUID_NULL)) {
215
0
        fmd->dataSetFieldId = var->dataSetFieldId;
216
0
    } else {
217
0
        fmd->dataSetFieldId = UA_PubSubManager_generateUniqueGuid(psm);
218
0
    }
219
220
    /* Properties */
221
0
    fmd->properties = NULL;
222
0
    fmd->propertiesSize = 0;
223
224
0
    return UA_STATUSCODE_GOOD;
225
0
}
226
227
UA_DataSetFieldResult
228
UA_DataSetField_create(UA_PubSubManager *psm, const UA_NodeId publishedDataSet,
229
                       const UA_DataSetFieldConfig *fieldConfig,
230
0
                       UA_NodeId *fieldIdentifier) {
231
0
    UA_DataSetFieldResult result;
232
0
    memset(&result, 0, sizeof(UA_DataSetFieldResult));
233
0
    if(!fieldConfig) {
234
0
        result.result = UA_STATUSCODE_BADINVALIDARGUMENT;
235
0
        return result;
236
0
    }
237
238
0
    UA_PublishedDataSet *currDS =
239
0
        UA_PublishedDataSet_find(psm, publishedDataSet);
240
0
    if(!currDS) {
241
0
        result.result = UA_STATUSCODE_BADNOTFOUND;
242
0
        return result;
243
0
    }
244
245
    /* If currDS was found, psm != NULL */
246
0
    if(currDS->configurationFreezeCounter > 0) {
247
0
        UA_LOG_WARNING_PUBSUB(psm->logging, currDS,
248
0
                              "Adding DataSetField failed: PublishedDataSet already in use");
249
0
        result.result = UA_STATUSCODE_BADCONFIGURATIONERROR;
250
0
        return result;
251
0
    }
252
253
0
    if(currDS->config.publishedDataSetType != UA_PUBSUB_DATASET_PUBLISHEDITEMS) {
254
0
        result.result = UA_STATUSCODE_BADNOTIMPLEMENTED;
255
0
        return result;
256
0
    }
257
258
0
    UA_DataSetField *newField = (UA_DataSetField*)UA_calloc(1, sizeof(UA_DataSetField));
259
0
    if(!newField) {
260
0
        result.result = UA_STATUSCODE_BADINTERNALERROR;
261
0
        return result;
262
0
    }
263
264
0
    result.result = UA_DataSetFieldConfig_copy(fieldConfig, &newField->config);
265
0
    if(result.result != UA_STATUSCODE_GOOD) {
266
0
        UA_free(newField);
267
0
        return result;
268
0
    }
269
270
0
    result.result = UA_NodeId_copy(&currDS->head.identifier, &newField->publishedDataSet);
271
0
    if(result.result != UA_STATUSCODE_GOOD) {
272
0
        UA_DataSetField_clear(newField);
273
0
        UA_free(newField);
274
0
        return result;
275
0
    }
276
277
    /* Initialize the field metadata. Also generates a FieldId, if not given in config */
278
0
    result.result = generateFieldMetaData(psm, currDS, newField);
279
0
    if(result.result != UA_STATUSCODE_GOOD) {
280
0
        UA_DataSetField_clear(newField);
281
0
        UA_free(newField);
282
0
        return result;
283
0
    }
284
285
    /* Append to the metadata fields array. Point of last return. */
286
0
    result.result = UA_Array_appendCopy((void**)&currDS->dataSetMetaData.fields,
287
0
                                        &currDS->dataSetMetaData.fieldsSize,
288
0
                                        &newField->fieldMetaData,
289
0
                                        &UA_TYPES[UA_TYPES_FIELDMETADATA]);
290
0
    if(result.result != UA_STATUSCODE_GOOD) {
291
0
        UA_DataSetField_clear(newField);
292
0
        UA_free(newField);
293
0
        return result;
294
0
    }
295
296
    /* Copy the identifier from the metadata. Cannot fail with a guid NodeId. */
297
0
    newField->identifier = UA_NODEID_GUID(1, newField->fieldMetaData.dataSetFieldId);
298
0
    if(fieldIdentifier)
299
0
        UA_NodeId_copy(&newField->identifier, fieldIdentifier);
300
301
    /* Register the field. The order of DataSetFields should be the same in both
302
     * creating and publishing. So adding DataSetFields at the the end of the
303
     * DataSets using the TAILQ structure. */
304
0
    TAILQ_INSERT_TAIL(&currDS->fields, newField, listEntry);
305
0
    currDS->fieldSize++;
306
307
0
    if(newField->config.field.variable.promotedField)
308
0
        currDS->promotedFieldsCount++;
309
310
    /* Update major version of parent published data set */
311
0
    UA_EventLoop *el = psm->sc.server->config.eventLoop;
312
0
    currDS->dataSetMetaData.configurationVersion.majorVersion =
313
0
        UA_PubSubConfigurationVersionTimeDifference(el->dateTime_now(el));
314
315
0
    result.configurationVersion.majorVersion =
316
0
        currDS->dataSetMetaData.configurationVersion.majorVersion;
317
0
    result.configurationVersion.minorVersion =
318
0
        currDS->dataSetMetaData.configurationVersion.minorVersion;
319
320
0
    return result;
321
0
}
322
323
UA_DataSetFieldResult
324
0
UA_DataSetField_remove(UA_PubSubManager *psm, UA_DataSetField *currentField) {
325
0
    UA_DataSetFieldResult result;
326
0
    memset(&result, 0, sizeof(UA_DataSetFieldResult));
327
328
0
    if(!currentField) {
329
0
        result.result = UA_STATUSCODE_BADNOTFOUND;
330
0
        return result;
331
0
    }
332
333
0
    UA_PublishedDataSet *pds =
334
0
        UA_PublishedDataSet_find(psm, currentField->publishedDataSet);
335
0
    if(!pds) {
336
0
        result.result = UA_STATUSCODE_BADNOTFOUND;
337
0
        return result;
338
0
    }
339
340
0
    if(pds->configurationFreezeCounter > 0) {
341
0
        UA_LOG_WARNING_PUBSUB(psm->logging, pds,
342
0
                              "Remove DataSetField failed: PublishedDataSet is in use");
343
0
        result.result = UA_STATUSCODE_BADCONFIGURATIONERROR;
344
0
        return result;
345
0
    }
346
347
    /* Reduce the counters before the config is cleaned up */
348
0
    if(currentField->config.field.variable.promotedField)
349
0
        pds->promotedFieldsCount--;
350
351
    /* Remove field from DataSetMetaData */
352
0
    pds->dataSetMetaData.fieldsSize--;
353
0
    if(pds->dataSetMetaData.fieldsSize == 0) {
354
0
        UA_FieldMetaData_delete(pds->dataSetMetaData.fields);
355
0
        pds->dataSetMetaData.fields = NULL;
356
0
    } else {
357
        /* Clear entry and move later fields to fill the gap */
358
0
        size_t i = 0;
359
0
        for(; i < pds->dataSetMetaData.fieldsSize + 1; i++) {
360
0
            if(UA_Guid_equal(&currentField->fieldMetaData.dataSetFieldId,
361
0
                             &pds->dataSetMetaData.fields[i].dataSetFieldId))
362
0
                break;
363
0
        }
364
0
        UA_FieldMetaData_clear(&pds->dataSetMetaData.fields[i]);
365
0
        for(; i < pds->dataSetMetaData.fieldsSize; i++) {
366
0
            pds->dataSetMetaData.fields[i] = pds->dataSetMetaData.fields[i+1];
367
0
        }
368
0
    }
369
370
    /* Remove */
371
0
    pds->fieldSize--;
372
0
    TAILQ_REMOVE(&pds->fields, currentField, listEntry);
373
0
    UA_DataSetField_clear(currentField);
374
0
    UA_free(currentField);
375
376
    /* Update major version of PublishedDataSet */
377
0
    UA_EventLoop *el = psm->sc.server->config.eventLoop;
378
0
    pds->dataSetMetaData.configurationVersion.majorVersion =
379
0
        UA_PubSubConfigurationVersionTimeDifference(el->dateTime_now(el));
380
381
0
    result.configurationVersion.majorVersion =
382
0
        pds->dataSetMetaData.configurationVersion.majorVersion;
383
0
    result.configurationVersion.minorVersion =
384
0
        pds->dataSetMetaData.configurationVersion.minorVersion;
385
386
0
    return result;
387
0
}
388
389
UA_StatusCode
390
UA_DataSetFieldConfig_copy(const UA_DataSetFieldConfig *src,
391
0
                           UA_DataSetFieldConfig *dst) {
392
0
    if(src->dataSetFieldType != UA_PUBSUB_DATASETFIELD_VARIABLE)
393
0
        return UA_STATUSCODE_BADNOTSUPPORTED;
394
0
    memcpy(dst, src, sizeof(UA_DataSetFieldConfig));
395
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
396
0
    res |= UA_String_copy(&src->field.variable.fieldNameAlias,
397
0
                          &dst->field.variable.fieldNameAlias);
398
0
    res |= UA_PublishedVariableDataType_copy(&src->field.variable.publishParameters,
399
0
                                             &dst->field.variable.publishParameters);
400
0
    res |= UA_LocalizedText_copy(&src->field.variable.description,
401
0
                                   &dst->field.variable.description);
402
0
    if(res != UA_STATUSCODE_GOOD)
403
0
        UA_DataSetFieldConfig_clear(dst);
404
0
    return res;
405
0
}
406
407
UA_DataSetField *
408
0
UA_DataSetField_find(UA_PubSubManager *psm, const UA_NodeId id) {
409
0
    if(!psm)
410
0
        return NULL;
411
0
    UA_PublishedDataSet *tmpPDS;
412
0
    TAILQ_FOREACH(tmpPDS, &psm->publishedDataSets, listEntry) {
413
0
        UA_DataSetField *tmpField;
414
0
        TAILQ_FOREACH(tmpField, &tmpPDS->fields, listEntry) {
415
0
            if(UA_NodeId_equal(&id, &tmpField->identifier))
416
0
                return tmpField;
417
0
        }
418
0
    }
419
0
    return NULL;
420
0
}
421
422
void
423
0
UA_DataSetFieldConfig_clear(UA_DataSetFieldConfig *dataSetFieldConfig) {
424
0
    if(dataSetFieldConfig->dataSetFieldType == UA_PUBSUB_DATASETFIELD_VARIABLE) {
425
0
        UA_String_clear(&dataSetFieldConfig->field.variable.fieldNameAlias);
426
0
        UA_PublishedVariableDataType_clear(&dataSetFieldConfig->field.variable.publishParameters);
427
0
        UA_LocalizedText_clear(&dataSetFieldConfig->field.variable.description);
428
0
    }
429
0
}
430
431
/* Obtain the latest value for a specific DataSetField. This method is currently
432
 * called inside the DataSetMessage generation process. */
433
void
434
UA_PubSubDataSetField_sampleValue(UA_PubSubManager *psm, UA_DataSetField *field,
435
0
                                  UA_DataValue *value) {
436
0
    UA_PublishedVariableDataType *params = &field->config.field.variable.publishParameters;
437
438
0
    UA_ReadValueId rvid;
439
0
    UA_ReadValueId_init(&rvid);
440
0
    rvid.nodeId = params->publishedVariable;
441
0
    rvid.attributeId = params->attributeId;
442
0
    rvid.indexRange = params->indexRange;
443
0
    *value = readWithSession(psm->sc.server, &psm->sc.server->adminSession,
444
0
                             &rvid, UA_TIMESTAMPSTORETURN_BOTH);
445
0
}
446
447
UA_AddPublishedDataSetResult
448
UA_PublishedDataSet_create(UA_PubSubManager *psm,
449
                           const UA_PublishedDataSetConfig *publishedDataSetConfig,
450
0
                           UA_NodeId *pdsIdentifier) {
451
0
    UA_AddPublishedDataSetResult result = {UA_STATUSCODE_BADINVALIDARGUMENT, 0, NULL, {0, 0}};
452
0
    if(!psm) {
453
0
        result.addResult = UA_STATUSCODE_BADINTERNALERROR;
454
0
        return result;
455
0
    }
456
457
0
    if(!publishedDataSetConfig) {
458
0
        UA_LOG_ERROR(psm->logging, UA_LOGCATEGORY_PUBSUB,
459
0
                     "PublishedDataSet creation failed. No config passed in.");
460
0
        return result;
461
0
    }
462
463
0
    if(publishedDataSetConfig->publishedDataSetType != UA_PUBSUB_DATASET_PUBLISHEDITEMS){
464
0
        UA_LOG_ERROR(psm->logging, UA_LOGCATEGORY_PUBSUB,
465
0
                     "PublishedDataSet creation failed. Unsupported PublishedDataSet type.");
466
0
        return result;
467
0
    }
468
469
0
    if(UA_String_isEmpty(&publishedDataSetConfig->name)) {
470
        // DataSet has to have a valid name
471
0
        UA_LOG_ERROR(psm->logging, UA_LOGCATEGORY_PUBSUB,
472
0
                     "PublishedDataSet creation failed. Invalid name.");
473
0
        return result;
474
0
    }
475
476
0
    if(UA_PublishedDataSet_findByName(psm, publishedDataSetConfig->name)) {
477
        // DataSet name has to be unique in the publisher
478
0
        UA_LOG_ERROR(psm->logging, UA_LOGCATEGORY_PUBSUB,
479
0
                     "PublishedDataSet creation failed. DataSet with the same name already exists.");
480
0
        result.addResult = UA_STATUSCODE_BADBROWSENAMEDUPLICATED;
481
0
        return result;
482
0
    }
483
484
    /* Create new PDS and add to UA_PubSubManager */
485
0
    UA_PublishedDataSet *newPDS = (UA_PublishedDataSet *)
486
0
        UA_calloc(1, sizeof(UA_PublishedDataSet));
487
0
    if(!newPDS) {
488
0
        UA_LOG_ERROR(psm->logging, UA_LOGCATEGORY_PUBSUB,
489
0
                     "PublishedDataSet creation failed. Out of Memory.");
490
0
        result.addResult = UA_STATUSCODE_BADOUTOFMEMORY;
491
0
        return result;
492
0
    }
493
0
    TAILQ_INIT(&newPDS->fields);
494
495
0
    newPDS->head.componentType = UA_PUBSUBCOMPONENT_PUBLISHEDDATASET;
496
497
0
    UA_PublishedDataSetConfig *newConfig = &newPDS->config;
498
499
    /* Deep copy the given connection config */
500
0
    UA_StatusCode res = UA_PublishedDataSetConfig_copy(publishedDataSetConfig, newConfig);
501
0
    if(res != UA_STATUSCODE_GOOD){
502
0
        UA_free(newPDS);
503
0
        UA_LOG_ERROR(psm->logging, UA_LOGCATEGORY_PUBSUB,
504
0
                     "PublishedDataSet creation failed. Configuration copy failed.");
505
0
        result.addResult = UA_STATUSCODE_BADINTERNALERROR;
506
0
        return result;
507
0
    }
508
509
    /* TODO: Parse template config and add fields (later PubSub batch) */
510
0
    if(newConfig->publishedDataSetType == UA_PUBSUB_DATASET_PUBLISHEDITEMS_TEMPLATE) {
511
0
    }
512
513
    /* Fill the DataSetMetaData */
514
0
    UA_EventLoop *el = psm->sc.server->config.eventLoop;
515
0
    result.configurationVersion.majorVersion =
516
0
        UA_PubSubConfigurationVersionTimeDifference(el->dateTime_now(el));
517
0
    result.configurationVersion.minorVersion =
518
0
        UA_PubSubConfigurationVersionTimeDifference(el->dateTime_now(el));
519
0
    switch(newConfig->publishedDataSetType) {
520
0
    case UA_PUBSUB_DATASET_PUBLISHEDEVENTS_TEMPLATE:
521
0
    case UA_PUBSUB_DATASET_PUBLISHEDEVENTS:
522
0
        res = UA_STATUSCODE_BADNOTSUPPORTED;
523
0
        break;
524
0
    case UA_PUBSUB_DATASET_PUBLISHEDITEMS:
525
0
        newPDS->dataSetMetaData.configurationVersion.majorVersion =
526
0
            UA_PubSubConfigurationVersionTimeDifference(el->dateTime_now(el));
527
0
        newPDS->dataSetMetaData.configurationVersion.minorVersion =
528
0
            UA_PubSubConfigurationVersionTimeDifference(el->dateTime_now(el));
529
0
        newPDS->dataSetMetaData.description = UA_LOCALIZEDTEXT_ALLOC("", "");
530
0
        newPDS->dataSetMetaData.dataSetClassId = UA_GUID_NULL;
531
0
        res = UA_String_copy(&newConfig->name, &newPDS->dataSetMetaData.name);
532
0
        break;
533
0
    case UA_PUBSUB_DATASET_PUBLISHEDITEMS_TEMPLATE:
534
0
        res = UA_DataSetMetaDataType_copy(&newConfig->config.itemsTemplate.metaData,
535
0
                                          &newPDS->dataSetMetaData);
536
0
        break;
537
0
    default:
538
0
        res = UA_STATUSCODE_BADINTERNALERROR;
539
0
    }
540
541
    /* Abort? */
542
0
    result.addResult = res;
543
0
    if(result.addResult != UA_STATUSCODE_GOOD) {
544
0
        UA_PublishedDataSetConfig_clear(newConfig);
545
0
        UA_free(newPDS);
546
0
        return result;
547
0
    }
548
549
    /* Insert into the queue of the manager */
550
0
    TAILQ_INSERT_TAIL(&psm->publishedDataSets, newPDS, listEntry);
551
0
    psm->publishedDataSetsSize++;
552
553
0
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
554
    /* Create representation and unique id */
555
0
    addPublishedDataItemsRepresentation(psm->sc.server, newPDS);
556
#else
557
    /* Generate unique nodeId */
558
    UA_PubSubManager_generateUniqueNodeId(psm, &newPDS->head.identifier);
559
#endif
560
561
    /* Cache the log string */
562
0
    char tmpLogIdStr[128];
563
0
    mp_snprintf(tmpLogIdStr, 128, "PublishedDataset %N\t| ", newPDS->head.identifier);
564
0
    newPDS->head.logIdString = UA_STRING_ALLOC(tmpLogIdStr);
565
566
0
    UA_LOG_INFO_PUBSUB(psm->logging, newPDS, "DataSet created");
567
568
    /* Return the created identifier */
569
0
    if(pdsIdentifier)
570
0
        UA_NodeId_copy(&newPDS->head.identifier, pdsIdentifier);
571
0
    return result;
572
0
}
573
574
UA_StatusCode
575
0
UA_PublishedDataSet_remove(UA_PubSubManager *psm, UA_PublishedDataSet *pds) {
576
0
    if(pds->configurationFreezeCounter > 0) {
577
0
        UA_LOG_WARNING_PUBSUB(psm->logging, pds,
578
0
                              "Cannot remove PublishedDataSet failed while in use");
579
0
        return UA_STATUSCODE_BADCONFIGURATIONERROR;
580
0
    }
581
582
    /* Search for referenced writers -> delete this writers. (Standard: writer
583
     * must be connected with PDS) */
584
0
    UA_PubSubConnection *conn;
585
0
    TAILQ_FOREACH(conn, &psm->connections, listEntry) {
586
0
        UA_WriterGroup *wg;
587
0
        LIST_FOREACH(wg, &conn->writerGroups, listEntry) {
588
0
            UA_DataSetWriter *dsw, *tmpWriter;
589
0
            LIST_FOREACH_SAFE(dsw, &wg->writers, listEntry, tmpWriter) {
590
0
                if(dsw->connectedDataSet == pds)
591
0
                    UA_DataSetWriter_remove(psm, dsw);
592
0
            }
593
0
        }
594
0
    }
595
596
0
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
597
0
    deleteNode(psm->sc.server, pds->head.identifier, true);
598
0
#endif
599
600
0
    UA_LOG_INFO_PUBSUB(psm->logging, pds, "PublishedDataSet deleted");
601
602
    /* Unlink from the server */
603
0
    TAILQ_REMOVE(&psm->publishedDataSets, pds, listEntry);
604
0
    psm->publishedDataSetsSize--;
605
606
    /* Clean up the PublishedDataSet */
607
0
    UA_DataSetField *field, *tmpField;
608
0
    TAILQ_FOREACH_SAFE(field, &pds->fields, listEntry, tmpField) {
609
0
        UA_DataSetField_clear(field);
610
0
        TAILQ_REMOVE(&pds->fields, field, listEntry);
611
0
        UA_free(field);
612
0
    }
613
0
    UA_PublishedDataSetConfig_clear(&pds->config);
614
0
    UA_DataSetMetaDataType_clear(&pds->dataSetMetaData);
615
0
    UA_PubSubComponentHead_clear(&pds->head);
616
0
    UA_free(pds);
617
618
0
    return UA_STATUSCODE_GOOD;
619
0
}
620
621
UA_SubscribedDataSet *
622
0
UA_SubscribedDataSet_find(UA_PubSubManager *psm, const UA_NodeId id) {
623
0
    if(!psm)
624
0
        return NULL;
625
0
    UA_SubscribedDataSet *sds;
626
0
    TAILQ_FOREACH(sds, &psm->subscribedDataSets, listEntry) {
627
0
        if(UA_NodeId_equal(&id, &sds->head.identifier))
628
0
            return sds;
629
0
    }
630
0
    return NULL;
631
0
}
632
633
UA_SubscribedDataSet *
634
0
UA_SubscribedDataSet_findByName(UA_PubSubManager *psm, const UA_String name) {
635
0
    if(!psm)
636
0
        return NULL;
637
0
    UA_SubscribedDataSet *sds;
638
0
    TAILQ_FOREACH(sds, &psm->subscribedDataSets, listEntry) {
639
0
        if(UA_String_equal(&name, &sds->config.name))
640
0
            return sds;
641
0
    }
642
0
    return NULL;
643
0
}
644
645
UA_StatusCode
646
UA_SubscribedDataSetConfig_copy(const UA_SubscribedDataSetConfig *src,
647
0
                                UA_SubscribedDataSetConfig *dst) {
648
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
649
0
    memcpy(dst, src, sizeof(UA_SubscribedDataSetConfig));
650
0
    res = UA_DataSetMetaDataType_copy(&src->dataSetMetaData, &dst->dataSetMetaData);
651
0
    res |= UA_String_copy(&src->name, &dst->name);
652
0
    if(src->subscribedDataSetType == UA_PUBSUB_SDS_TARGET) {
653
0
        res |= UA_TargetVariablesDataType_copy(&src->subscribedDataSet.target,
654
0
                                               &dst->subscribedDataSet.target);
655
0
    }
656
0
    if(res != UA_STATUSCODE_GOOD)
657
0
        UA_SubscribedDataSetConfig_clear(dst);
658
0
    return res;
659
0
}
660
661
void
662
0
UA_SubscribedDataSetConfig_clear(UA_SubscribedDataSetConfig *sdsConfig) {
663
0
    UA_String_clear(&sdsConfig->name);
664
0
    UA_DataSetMetaDataType_clear(&sdsConfig->dataSetMetaData);
665
0
    UA_TargetVariablesDataType_clear(&sdsConfig->subscribedDataSet.target);
666
0
}
667
668
static UA_StatusCode
669
addSubscribedDataSet(UA_PubSubManager *psm,
670
                     const UA_SubscribedDataSetConfig *sdsConfig,
671
0
                     UA_NodeId *sdsIdentifier) {
672
0
    if(!psm)
673
0
        return UA_STATUSCODE_BADINTERNALERROR;
674
675
0
    UA_LOCK_ASSERT(&psm->sc.server->serviceMutex);
676
677
0
    if(!sdsConfig) {
678
0
        UA_LOG_ERROR(psm->logging, UA_LOGCATEGORY_PUBSUB,
679
0
                     "SubscribedDataSet creation failed. No config passed in.");
680
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
681
0
    }
682
683
0
    UA_SubscribedDataSetConfig tmpSubscribedDataSetConfig;
684
0
    memset(&tmpSubscribedDataSetConfig, 0, sizeof(UA_SubscribedDataSetConfig));
685
0
    UA_StatusCode res = UA_SubscribedDataSetConfig_copy(sdsConfig,
686
0
                                                        &tmpSubscribedDataSetConfig);
687
0
    if(res != UA_STATUSCODE_GOOD){
688
0
        UA_LOG_ERROR(psm->logging, UA_LOGCATEGORY_PUBSUB,
689
0
                     "SubscribedDataSet creation failed. Configuration copy failed.");
690
0
        return res;
691
0
    }
692
693
    /* Create new PDS and add to UA_PubSubManager */
694
0
    UA_SubscribedDataSet *newSubscribedDataSet = (UA_SubscribedDataSet *)
695
0
            UA_calloc(1, sizeof(UA_SubscribedDataSet));
696
0
    if(!newSubscribedDataSet) {
697
0
        UA_SubscribedDataSetConfig_clear(&tmpSubscribedDataSetConfig);
698
0
        UA_LOG_ERROR(psm->logging, UA_LOGCATEGORY_PUBSUB,
699
0
                     "SubscribedDataSet creation failed. Out of Memory.");
700
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
701
0
    }
702
703
0
    newSubscribedDataSet->head.componentType = UA_PUBSUBCOMPONENT_SUBSCRIBEDDDATASET;
704
0
    newSubscribedDataSet->config = tmpSubscribedDataSetConfig;
705
0
    newSubscribedDataSet->connectedReader = NULL;
706
707
0
    TAILQ_INSERT_TAIL(&psm->subscribedDataSets, newSubscribedDataSet, listEntry);
708
0
    psm->subscribedDataSetsSize++;
709
710
0
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
711
0
    addSubscribedDataSetRepresentation(psm->sc.server, newSubscribedDataSet);
712
#else
713
    UA_PubSubManager_generateUniqueNodeId(psm, &newSubscribedDataSet->head.identifier);
714
#endif
715
716
0
    if(sdsIdentifier)
717
0
        UA_NodeId_copy(&newSubscribedDataSet->head.identifier, sdsIdentifier);
718
719
0
    return UA_STATUSCODE_GOOD;
720
0
}
721
722
void
723
0
UA_SubscribedDataSet_remove(UA_PubSubManager *psm, UA_SubscribedDataSet *sds) {
724
    /* Search for referenced readers */
725
0
    UA_PubSubConnection *conn;
726
0
    TAILQ_FOREACH(conn, &psm->connections, listEntry) {
727
0
        UA_ReaderGroup *rg;
728
0
        LIST_FOREACH(rg, &conn->readerGroups, listEntry) {
729
0
            UA_DataSetReader *dsr, *tmpReader;
730
0
            LIST_FOREACH_SAFE(dsr, &rg->readers, listEntry, tmpReader) {
731
                /* TODO: What if the reader is still operational?
732
                 * This should be checked before calling _remove. */
733
0
                if(dsr == sds->connectedReader)
734
0
                    UA_DataSetReader_remove(psm, dsr);
735
0
            }
736
0
        }
737
0
    }
738
739
0
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
740
0
    deleteNode(psm->sc.server, sds->head.identifier, true);
741
0
#endif
742
743
    /* Unlink from the server */
744
0
    TAILQ_REMOVE(&psm->subscribedDataSets, sds, listEntry);
745
0
    psm->subscribedDataSetsSize--;
746
747
    /* Clean up */
748
0
    UA_SubscribedDataSetConfig_clear(&sds->config);
749
0
    UA_PubSubComponentHead_clear(&sds->head);
750
0
    UA_free(sds);
751
0
}
752
753
/**************/
754
/* Server API */
755
/**************/
756
757
UA_StatusCode
758
UA_Server_getPublishedDataSetConfig(UA_Server *server, const UA_NodeId pdsId,
759
0
                                    UA_PublishedDataSetConfig *config) {
760
0
    if(!server || !config)
761
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
762
0
    lockServer(server);
763
0
    UA_PublishedDataSet *pds = UA_PublishedDataSet_find(getPSM(server), pdsId);
764
0
    UA_StatusCode res = (pds) ?
765
0
        UA_PublishedDataSetConfig_copy(&pds->config, config) : UA_STATUSCODE_BADNOTFOUND;
766
0
    unlockServer(server);
767
0
    return res;
768
0
}
769
770
UA_StatusCode
771
UA_Server_getPublishedDataSetMetaData(UA_Server *server, const UA_NodeId pdsId,
772
0
                                      UA_DataSetMetaDataType *metaData) {
773
0
    if(!server || !metaData)
774
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
775
0
    lockServer(server);
776
0
    UA_PublishedDataSet *pds = UA_PublishedDataSet_find(getPSM(server), pdsId);
777
0
    UA_StatusCode res = (pds) ?
778
0
        UA_DataSetMetaDataType_copy(&pds->dataSetMetaData, metaData) : UA_STATUSCODE_BADNOTFOUND;
779
0
    unlockServer(server);
780
0
    return res;
781
0
}
782
783
UA_DataSetFieldResult
784
UA_Server_addDataSetField(UA_Server *server, const UA_NodeId publishedDataSet,
785
                          const UA_DataSetFieldConfig *fieldConfig,
786
0
                          UA_NodeId *fieldIdentifier) {
787
0
    UA_DataSetFieldResult res;
788
0
    if(!server || !fieldConfig) {
789
0
        memset(&res, 0, sizeof(UA_DataSetFieldResult));
790
0
        res.result = UA_STATUSCODE_BADINVALIDARGUMENT;
791
0
        return res;
792
0
    }
793
0
    lockServer(server);
794
0
    res = UA_DataSetField_create(getPSM(server), publishedDataSet, fieldConfig, fieldIdentifier);
795
0
    unlockServer(server);
796
0
    return res;
797
0
}
798
799
UA_DataSetFieldResult
800
0
UA_Server_removeDataSetField(UA_Server *server, const UA_NodeId dsf) {
801
0
    UA_DataSetFieldResult res;
802
0
    if(!server) {
803
0
        memset(&res, 0, sizeof(UA_DataSetFieldResult));
804
0
        res.result = UA_STATUSCODE_BADINVALIDARGUMENT;
805
0
        return res;
806
0
    }
807
0
    lockServer(server);
808
0
    UA_PubSubManager *psm = getPSM(server);
809
0
    UA_DataSetField *field = UA_DataSetField_find(psm, dsf);
810
0
    res = UA_DataSetField_remove(psm, field);
811
0
    unlockServer(server);
812
0
    return res;
813
0
}
814
815
UA_StatusCode
816
UA_Server_getDataSetFieldConfig(UA_Server *server, const UA_NodeId dsfId,
817
0
                                UA_DataSetFieldConfig *config) {
818
0
    if(!server || !config)
819
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
820
0
    lockServer(server);
821
0
    UA_DataSetField *dsf = UA_DataSetField_find(getPSM(server), dsfId);
822
0
    UA_StatusCode res = (dsf) ?
823
0
        UA_DataSetFieldConfig_copy(&dsf->config, config) : UA_STATUSCODE_BADNOTFOUND;
824
0
    unlockServer(server);
825
0
    return res;
826
0
}
827
828
UA_AddPublishedDataSetResult
829
UA_Server_addPublishedDataSet(UA_Server *server,
830
                              const UA_PublishedDataSetConfig *publishedDataSetConfig,
831
0
                              UA_NodeId *pdsIdentifier) {
832
0
    UA_AddPublishedDataSetResult res;
833
0
    if(!server || !publishedDataSetConfig) {
834
0
        memset(&res, 0, sizeof(UA_AddPublishedDataSetResult));
835
0
        res.addResult = UA_STATUSCODE_BADINTERNALERROR;
836
0
        return res;
837
0
    }
838
0
    lockServer(server);
839
0
    res = UA_PublishedDataSet_create(getPSM(server), publishedDataSetConfig, pdsIdentifier);
840
0
    unlockServer(server);
841
0
    return res;
842
0
}
843
844
UA_StatusCode
845
0
UA_Server_removePublishedDataSet(UA_Server *server, const UA_NodeId pdsId) {
846
0
    if(!server)
847
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
848
0
    lockServer(server);
849
0
    UA_PubSubManager *psm = getPSM(server);
850
0
    UA_PublishedDataSet *pds = UA_PublishedDataSet_find(psm, pdsId);
851
0
    UA_StatusCode res = (pds) ?
852
0
        UA_PublishedDataSet_remove(psm, pds) : UA_STATUSCODE_BADNOTFOUND;
853
0
    unlockServer(server);
854
0
    return res;
855
0
}
856
857
UA_StatusCode
858
UA_Server_addSubscribedDataSet(UA_Server *server,
859
                               const UA_SubscribedDataSetConfig *sdsConfig,
860
0
                               UA_NodeId *sdsIdentifier) {
861
0
    if(!server || !sdsConfig)
862
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
863
0
    lockServer(server);
864
0
    UA_StatusCode res = addSubscribedDataSet(getPSM(server), sdsConfig, sdsIdentifier);
865
0
    unlockServer(server);
866
0
    return res;
867
0
}
868
869
UA_StatusCode
870
0
UA_Server_removeSubscribedDataSet(UA_Server *server, const UA_NodeId sdsId) {
871
0
    if(!server)
872
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
873
0
    lockServer(server);
874
0
    UA_PubSubManager *psm = getPSM(server);
875
0
    UA_SubscribedDataSet *sds = UA_SubscribedDataSet_find(psm, sdsId);
876
0
    UA_StatusCode res = UA_STATUSCODE_BADNOTFOUND;
877
0
    if(sds) {
878
0
        UA_SubscribedDataSet_remove(psm, sds);
879
0
        res = UA_STATUSCODE_GOOD;
880
0
    }
881
0
    unlockServer(server);
882
0
    return res;
883
0
}
884
885
#endif /* UA_ENABLE_PUBSUB */