Coverage Report

Created: 2026-03-31 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541/src/pubsub/ua_pubsub_ns0.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-2021 Kalycito Infotech Private Limited
7
 * Copyright (c) 2020 Yannick Wallerer, Siemens AG
8
 * Copyright (c) 2020-2022 Thomas Fischer, Siemens AG
9
 * Copyright (c) 2022 Linutronix GmbH (Author: Muddasir Shakil)
10
 * Copyright (c) 2025 Fraunhofer IOSB (Author: Julius Pfrommer)
11
 */
12
13
#include "ua_pubsub_internal.h"
14
15
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL /* conditional compilation */
16
17
typedef struct {
18
    UA_NodeId parentNodeId;
19
    UA_UInt32 parentClassifier;
20
    UA_UInt32 elementClassiefier;
21
} UA_NodePropertyContext;
22
23
static UA_StatusCode
24
writePubSubNs0VariableArray(UA_Server *server, const UA_NodeId id, void *v,
25
553
                            size_t length, const UA_DataType *type) {
26
553
    UA_LOCK_ASSERT(&server->serviceMutex);
27
553
    UA_Variant var;
28
553
    UA_Variant_init(&var);
29
553
    UA_Variant_setArray(&var, v, length, type);
30
553
    return writeValueAttribute(server, id, &var);
31
553
}
32
33
static UA_NodeId
34
findSingleChildNode(UA_Server *server, UA_QualifiedName targetName,
35
0
                    UA_NodeId referenceTypeId, UA_NodeId startingNode){
36
0
    UA_LOCK_ASSERT(&server->serviceMutex);
37
0
    UA_NodeId resultNodeId;
38
0
    UA_RelativePathElement rpe;
39
0
    UA_RelativePathElement_init(&rpe);
40
0
    rpe.referenceTypeId = referenceTypeId;
41
0
    rpe.isInverse = false;
42
0
    rpe.includeSubtypes = false;
43
0
    rpe.targetName = targetName;
44
0
    UA_BrowsePath bp;
45
0
    UA_BrowsePath_init(&bp);
46
0
    bp.startingNode = startingNode;
47
0
    bp.relativePath.elementsSize = 1;
48
0
    bp.relativePath.elements = &rpe;
49
0
    UA_BrowsePathResult bpr = translateBrowsePathToNodeIds(server, &bp);
50
0
    if(bpr.statusCode != UA_STATUSCODE_GOOD ||
51
0
       bpr.targetsSize < 1)
52
0
        return UA_NODEID_NULL;
53
0
    UA_StatusCode res = UA_NodeId_copy(&bpr.targets[0].targetId.nodeId, &resultNodeId);
54
0
    if(res != UA_STATUSCODE_GOOD){
55
0
        UA_BrowsePathResult_clear(&bpr);
56
0
        return UA_NODEID_NULL;
57
0
    }
58
0
    UA_BrowsePathResult_clear(&bpr);
59
0
    return resultNodeId;
60
0
}
61
62
static UA_StatusCode
63
findPubSubComponentFromStatus(UA_Server *server, const UA_NodeId *statusObjectId,
64
                              UA_NodeId *componentNodeId, UA_PubSubComponentType *componentType,
65
0
                              void **component, UA_Boolean *isPublishSubscribeObject) {
66
0
    UA_LOCK_ASSERT(&server->serviceMutex);
67
68
0
    *isPublishSubscribeObject = false;
69
    /* Find the parent PubSub component by browsing up from the Status object */
70
0
    UA_BrowseDescription bd;
71
0
    UA_BrowseDescription_init(&bd);
72
0
    bd.nodeId = *statusObjectId;
73
0
    bd.browseDirection = UA_BROWSEDIRECTION_INVERSE;
74
0
    bd.referenceTypeId = UA_NS0ID(HASCOMPONENT);
75
0
    bd.includeSubtypes = false;
76
0
    bd.nodeClassMask = UA_NODECLASS_OBJECT;
77
0
    bd.resultMask = UA_BROWSERESULTMASK_REFERENCETYPEID | UA_BROWSERESULTMASK_TYPEDEFINITION;
78
79
0
    UA_BrowseResult br = UA_Server_browse(server, 0, &bd);
80
0
    if(br.statusCode != UA_STATUSCODE_GOOD || br.referencesSize == 0) {
81
0
        UA_BrowseResult_clear(&br);
82
0
        return UA_STATUSCODE_BADNOTFOUND;
83
0
    }
84
85
0
    *componentNodeId = br.references[0].nodeId.nodeId;
86
0
    UA_NodeId parentTypeId = br.references[0].typeDefinition.nodeId;
87
0
    UA_BrowseResult_clear(&br);
88
89
0
    UA_PubSubManager *psm = getPSM(server);
90
0
    if(!psm)
91
0
        return UA_STATUSCODE_BADINTERNALERROR;
92
93
    /* Identify component type and find the component */
94
0
    UA_NodeId pubsubconnectionTypeId = UA_NS0ID(PUBSUBCONNECTIONTYPE);
95
0
    UA_NodeId writergroupTypeId = UA_NS0ID(WRITERGROUPTYPE);
96
0
    UA_NodeId readergroupTypeId = UA_NS0ID(READERGROUPTYPE);
97
0
    UA_NodeId datasetreaderTypeId = UA_NS0ID(DATASETREADERTYPE);
98
0
    UA_NodeId datasetwriterTypeId = UA_NS0ID(DATASETWRITERTYPE);
99
0
    UA_NodeId publishsubscribeTypeId = UA_NS0ID(PUBLISHSUBSCRIBETYPE);
100
101
0
    if(UA_NodeId_equal(&parentTypeId, &publishsubscribeTypeId)) {
102
0
        *isPublishSubscribeObject = true;
103
0
        *componentType = UA_PUBSUBCOMPONENT_CONNECTION;
104
0
        *component = psm;
105
0
        return UA_STATUSCODE_GOOD;
106
0
    } else if(UA_NodeId_equal(&parentTypeId, &pubsubconnectionTypeId)) {
107
0
        *componentType = UA_PUBSUBCOMPONENT_CONNECTION;
108
0
        *component = UA_PubSubConnection_find(psm, *componentNodeId);
109
0
    } else if(UA_NodeId_equal(&parentTypeId, &writergroupTypeId)) {
110
0
        *componentType = UA_PUBSUBCOMPONENT_WRITERGROUP;
111
0
        *component = UA_WriterGroup_find(psm, *componentNodeId);
112
0
    } else if(UA_NodeId_equal(&parentTypeId, &readergroupTypeId)) {
113
0
        *componentType = UA_PUBSUBCOMPONENT_READERGROUP;
114
0
        *component = UA_ReaderGroup_find(psm, *componentNodeId);
115
0
    } else if(UA_NodeId_equal(&parentTypeId, &datasetreaderTypeId)) {
116
0
        *componentType = UA_PUBSUBCOMPONENT_DATASETREADER;
117
0
        *component = UA_DataSetReader_find(psm, *componentNodeId);
118
0
    } else if(UA_NodeId_equal(&parentTypeId, &datasetwriterTypeId)) {
119
0
        *componentType = UA_PUBSUBCOMPONENT_DATASETWRITER;
120
0
        *component = UA_DataSetWriter_find(psm, *componentNodeId);
121
0
    } else {
122
0
        return UA_STATUSCODE_BADNOTSUPPORTED;
123
0
    }
124
125
0
    if(!*component)
126
0
        return UA_STATUSCODE_BADNOTFOUND;
127
128
0
    return UA_STATUSCODE_GOOD;
129
0
}
130
131
static UA_StatusCode
132
pubSubStateVariableDataSourceRead(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext,
133
                                  const UA_NodeId *nodeid, void *context, UA_Boolean includeSourceTimeStamp,
134
0
                                  const UA_NumericRange *range, UA_DataValue *value) {
135
0
    UA_LOCK_ASSERT(&server->serviceMutex);
136
137
    /* Find the parent Status object by browsing inverse from State variable */
138
0
    UA_BrowseDescription bd;
139
0
    UA_BrowseDescription_init(&bd);
140
0
    bd.nodeId = *nodeid;
141
0
    bd.browseDirection = UA_BROWSEDIRECTION_INVERSE;
142
0
    bd.referenceTypeId = UA_NS0ID(HASCOMPONENT);
143
0
    bd.includeSubtypes = false;
144
0
    bd.nodeClassMask = UA_NODECLASS_OBJECT;
145
0
    bd.resultMask = UA_BROWSERESULTMASK_REFERENCETYPEID;
146
147
0
    UA_BrowseResult br = UA_Server_browse(server, 0, &bd);
148
0
    if(br.statusCode != UA_STATUSCODE_GOOD || br.referencesSize == 0) {
149
0
        UA_BrowseResult_clear(&br);
150
0
        return UA_STATUSCODE_BADINTERNALERROR;
151
0
    }
152
153
0
    UA_NodeId statusObjectId = br.references[0].nodeId.nodeId;
154
0
    UA_BrowseResult_clear(&br);
155
156
0
    UA_NodeId componentNodeId;
157
0
    UA_PubSubComponentType componentType;
158
0
    void *component = NULL;
159
0
    UA_Boolean isPublishSubscribeObject = false;
160
    
161
0
    UA_StatusCode retVal = findPubSubComponentFromStatus(server, &statusObjectId,
162
0
                                                        &componentNodeId, &componentType, &component, &isPublishSubscribeObject);
163
0
    if(retVal != UA_STATUSCODE_GOOD)
164
0
        return retVal;
165
166
0
    UA_PubSubState state = UA_PUBSUBSTATE_DISABLED;
167
    
168
0
    if(isPublishSubscribeObject) {
169
0
        UA_PubSubManager *psm = (UA_PubSubManager*)component;
170
0
        state = (psm->sc.state == UA_LIFECYCLESTATE_STARTED) ? 
171
0
                UA_PUBSUBSTATE_OPERATIONAL : UA_PUBSUBSTATE_DISABLED;
172
0
    } else {
173
0
        switch(componentType) {
174
0
        case UA_PUBSUBCOMPONENT_CONNECTION:
175
0
            state = ((UA_PubSubConnection*)component)->head.state;
176
0
            break;
177
0
        case UA_PUBSUBCOMPONENT_WRITERGROUP:
178
0
            state = ((UA_WriterGroup*)component)->head.state;
179
0
            break;
180
0
        case UA_PUBSUBCOMPONENT_READERGROUP:
181
0
            state = ((UA_ReaderGroup*)component)->head.state;
182
0
            break;
183
0
        case UA_PUBSUBCOMPONENT_DATASETREADER:
184
0
            state = ((UA_DataSetReader*)component)->head.state;
185
0
            break;
186
0
        case UA_PUBSUBCOMPONENT_DATASETWRITER:
187
0
            state = ((UA_DataSetWriter*)component)->head.state;
188
0
            break;
189
0
        default:
190
0
            return UA_STATUSCODE_BADNOTSUPPORTED;
191
0
        }
192
0
    }
193
194
0
    value->hasValue = true;
195
0
    return UA_Variant_setScalarCopy(&value->value, &state, &UA_TYPES[UA_TYPES_PUBSUBSTATE]);
196
0
}
197
198
static UA_StatusCode
199
enablePubSubObjectAction(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext,
200
                         const UA_NodeId *methodId, void *methodContext,
201
                         const UA_NodeId *objectId, void *objectContext,
202
                         size_t inputSize, const UA_Variant *input,
203
0
                         size_t outputSize, UA_Variant *output) {
204
0
    UA_LOCK_ASSERT(&server->serviceMutex);
205
206
    /* Find the State variable within the Status object */
207
0
    UA_NodeId stateNodeId = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "State"),
208
0
                                               UA_NS0ID(HASCOMPONENT), *objectId);
209
0
    if(UA_NodeId_isNull(&stateNodeId))
210
0
        return UA_STATUSCODE_BADNOTFOUND;
211
212
    /* Use helper function to identify and find the PubSub component */
213
0
    UA_NodeId componentNodeId;
214
0
    UA_PubSubComponentType componentType;
215
0
    void *component = NULL;
216
0
    UA_Boolean isPublishSubscribeObject = false;
217
    
218
0
    UA_StatusCode retVal = findPubSubComponentFromStatus(server, objectId,
219
0
                                                        &componentNodeId, &componentType, &component, &isPublishSubscribeObject);
220
0
    if(retVal != UA_STATUSCODE_GOOD)
221
0
        return retVal;
222
223
0
    UA_PubSubManager *psm = getPSM(server);
224
0
    if(!psm)
225
0
        return UA_STATUSCODE_BADINTERNALERROR;
226
227
0
    if(isPublishSubscribeObject) {
228
0
        if(psm->sc.state != UA_LIFECYCLESTATE_STOPPED)
229
0
            return UA_STATUSCODE_BADINVALIDSTATE;
230
0
        UA_PubSubManager_setState(psm, UA_LIFECYCLESTATE_STARTED);
231
0
        return UA_STATUSCODE_GOOD;
232
0
    }
233
234
0
    switch(componentType) {
235
0
    case UA_PUBSUBCOMPONENT_CONNECTION: {
236
0
        UA_PubSubConnection *conn = (UA_PubSubConnection*)component;
237
        /* OPC UA Standard: "The Server shall reject Enable Method calls if the current State is not Disabled." */
238
0
        if(conn->head.state != UA_PUBSUBSTATE_DISABLED)
239
0
            return UA_STATUSCODE_BADINVALIDSTATE;
240
0
        retVal = UA_PubSubConnection_setPubSubState(psm, conn, UA_PUBSUBSTATE_OPERATIONAL);
241
0
        break;
242
0
    }
243
0
    case UA_PUBSUBCOMPONENT_WRITERGROUP: {
244
0
        UA_WriterGroup *wg = (UA_WriterGroup*)component;
245
0
        if(wg->head.state != UA_PUBSUBSTATE_DISABLED)
246
0
            return UA_STATUSCODE_BADINVALIDSTATE;
247
0
        retVal = UA_WriterGroup_setPubSubState(psm, wg, UA_PUBSUBSTATE_OPERATIONAL);
248
0
        break;
249
0
    }
250
0
    case UA_PUBSUBCOMPONENT_READERGROUP: {
251
0
        UA_ReaderGroup *rg = (UA_ReaderGroup*)component;
252
0
        if(rg->head.state != UA_PUBSUBSTATE_DISABLED)
253
0
            return UA_STATUSCODE_BADINVALIDSTATE;
254
0
        retVal = UA_ReaderGroup_setPubSubState(psm, rg, UA_PUBSUBSTATE_OPERATIONAL);
255
0
        break;
256
0
    }
257
0
    case UA_PUBSUBCOMPONENT_DATASETREADER: {
258
0
        UA_DataSetReader *dsr = (UA_DataSetReader*)component;
259
0
        if(dsr->head.state != UA_PUBSUBSTATE_DISABLED)
260
0
            return UA_STATUSCODE_BADINVALIDSTATE;
261
0
        retVal = UA_DataSetReader_setPubSubState(psm, dsr, UA_PUBSUBSTATE_OPERATIONAL, UA_STATUSCODE_GOOD);
262
0
        break;
263
0
    }
264
0
    case UA_PUBSUBCOMPONENT_DATASETWRITER: {
265
0
        UA_DataSetWriter *dsw = (UA_DataSetWriter*)component;
266
0
        if(dsw->head.state != UA_PUBSUBSTATE_DISABLED)
267
0
            return UA_STATUSCODE_BADINVALIDSTATE;
268
0
        retVal = UA_DataSetWriter_setPubSubState(psm, dsw, UA_PUBSUBSTATE_OPERATIONAL);
269
0
        break;
270
0
    }
271
0
    default:
272
0
        return UA_STATUSCODE_BADNOTSUPPORTED;
273
0
    }
274
275
0
    return retVal;
276
0
}
277
278
static UA_StatusCode
279
disablePubSubObjectAction(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext,
280
                          const UA_NodeId *methodId, void *methodContext,
281
                          const UA_NodeId *objectId, void *objectContext,
282
                          size_t inputSize, const UA_Variant *input,
283
0
                          size_t outputSize, UA_Variant *output) {
284
0
    UA_LOCK_ASSERT(&server->serviceMutex);
285
286
    /* Find the State variable within the Status object */
287
0
    UA_NodeId stateNodeId = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "State"),
288
0
                                               UA_NS0ID(HASCOMPONENT), *objectId);
289
0
    if(UA_NodeId_isNull(&stateNodeId))
290
0
        return UA_STATUSCODE_BADNOTFOUND;
291
292
    /* Use helper function to identify and find the PubSub component */
293
0
    UA_NodeId componentNodeId;
294
0
    UA_PubSubComponentType componentType;
295
0
    void *component = NULL;
296
0
    UA_Boolean isPublishSubscribeObject = false;
297
    
298
0
    UA_StatusCode retVal = findPubSubComponentFromStatus(server, objectId,
299
0
                                                        &componentNodeId, &componentType, &component, &isPublishSubscribeObject);
300
0
    if(retVal != UA_STATUSCODE_GOOD)
301
0
        return retVal;
302
303
0
    UA_PubSubManager *psm = getPSM(server);
304
0
    if(!psm)
305
0
        return UA_STATUSCODE_BADINTERNALERROR;
306
307
    /* Handle PublishSubscribe object separately */
308
0
    if(isPublishSubscribeObject) {
309
        /* For PublishSubscribe object, check PubSubManager lifecycle state */
310
0
        if(psm->sc.state == UA_LIFECYCLESTATE_STOPPED)
311
0
            return UA_STATUSCODE_BADINVALIDSTATE;
312
        /* Disable the PubSubManager by stopping it */
313
0
        UA_PubSubManager_setState(psm, UA_LIFECYCLESTATE_STOPPED);
314
0
        return UA_STATUSCODE_GOOD;
315
0
    }
316
317
    /* Disable the appropriate PubSub component with state validation */
318
0
    switch(componentType) {
319
0
    case UA_PUBSUBCOMPONENT_CONNECTION: {
320
0
        UA_PubSubConnection *conn = (UA_PubSubConnection*)component;
321
        /* OPC UA Standard: "The Server shall reject Disable Method calls if the current State is Disabled." */
322
0
        if(conn->head.state == UA_PUBSUBSTATE_DISABLED)
323
0
            return UA_STATUSCODE_BADINVALIDSTATE;
324
0
        retVal = UA_PubSubConnection_setPubSubState(psm, conn, UA_PUBSUBSTATE_DISABLED);
325
0
        break;
326
0
    }
327
0
    case UA_PUBSUBCOMPONENT_WRITERGROUP: {
328
0
        UA_WriterGroup *wg = (UA_WriterGroup*)component;
329
0
        if(wg->head.state == UA_PUBSUBSTATE_DISABLED)
330
0
            return UA_STATUSCODE_BADINVALIDSTATE;
331
0
        retVal = UA_WriterGroup_setPubSubState(psm, wg, UA_PUBSUBSTATE_DISABLED);
332
0
        break;
333
0
    }
334
0
    case UA_PUBSUBCOMPONENT_READERGROUP: {
335
0
        UA_ReaderGroup *rg = (UA_ReaderGroup*)component;
336
0
        if(rg->head.state == UA_PUBSUBSTATE_DISABLED)
337
0
            return UA_STATUSCODE_BADINVALIDSTATE;
338
0
        retVal = UA_ReaderGroup_setPubSubState(psm, rg, UA_PUBSUBSTATE_DISABLED);
339
0
        break;
340
0
    }
341
0
    case UA_PUBSUBCOMPONENT_DATASETREADER: {
342
0
        UA_DataSetReader *dsr = (UA_DataSetReader*)component;
343
0
        if(dsr->head.state == UA_PUBSUBSTATE_DISABLED)
344
0
            return UA_STATUSCODE_BADINVALIDSTATE;
345
0
        retVal = UA_DataSetReader_setPubSubState(psm, dsr, UA_PUBSUBSTATE_DISABLED, UA_STATUSCODE_GOOD);
346
0
        break;
347
0
    }
348
0
    case UA_PUBSUBCOMPONENT_DATASETWRITER: {
349
0
        UA_DataSetWriter *dsw = (UA_DataSetWriter*)component;
350
0
        if(dsw->head.state == UA_PUBSUBSTATE_DISABLED)
351
0
            return UA_STATUSCODE_BADINVALIDSTATE;
352
0
        retVal = UA_DataSetWriter_setPubSubState(psm, dsw, UA_PUBSUBSTATE_DISABLED);
353
0
        break;
354
0
    }
355
0
    default:
356
0
        return UA_STATUSCODE_BADNOTSUPPORTED;
357
0
    }
358
359
0
    return retVal;
360
0
}
361
362
static UA_StatusCode
363
ReadCallback(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext,
364
             const UA_NodeId *nodeid, void *context, UA_Boolean includeSourceTimeStamp,
365
0
             const UA_NumericRange *range, UA_DataValue *value) {
366
0
    UA_LOCK_ASSERT(&server->serviceMutex);
367
368
0
    UA_PubSubManager *psm = getPSM(server);
369
0
    if(!psm)
370
0
        return UA_STATUSCODE_BADINTERNALERROR;
371
372
0
    const UA_NodePropertyContext *nodeContext = (const UA_NodePropertyContext*)context;
373
0
    const UA_NodeId *myNodeId = &nodeContext->parentNodeId;
374
375
0
    switch(nodeContext->parentClassifier) {
376
0
    case UA_NS0ID_PUBSUBCONNECTIONTYPE: {
377
0
        UA_PubSubConnection *pubSubConnection = UA_PubSubConnection_find(psm, *myNodeId);
378
0
        if(!pubSubConnection)
379
0
            return UA_STATUSCODE_BADNOTFOUND;
380
0
        if(nodeContext->elementClassiefier == UA_NS0ID_PUBSUBCONNECTIONTYPE_PUBLISHERID) {
381
0
            UA_Variant tmp;
382
0
            UA_PublisherId_toVariant(&pubSubConnection->config.publisherId, &tmp);
383
0
            value->hasValue = true;
384
0
            return UA_Variant_copy(&tmp, &value->value);
385
0
        }
386
0
        break;
387
0
    }
388
0
    case UA_NS0ID_READERGROUPTYPE: {
389
0
        UA_ReaderGroup *readerGroup = UA_ReaderGroup_find(psm, *myNodeId);
390
0
        if(!readerGroup)
391
0
            return UA_STATUSCODE_BADNOTFOUND;
392
0
        if(nodeContext->elementClassiefier == UA_NS0ID_PUBSUBGROUPTYPE_STATUS_STATE) {
393
0
            value->hasValue = true;
394
0
            return UA_Variant_setScalarCopy(&value->value, &readerGroup->head.state,
395
0
                                            &UA_TYPES[UA_TYPES_PUBSUBSTATE]);
396
0
        }
397
0
        break;
398
0
    }
399
0
    case UA_NS0ID_DATASETREADERTYPE: {
400
0
        UA_DataSetReader *dataSetReader = UA_DataSetReader_find(psm, *myNodeId);
401
0
        if(!dataSetReader)
402
0
            return UA_STATUSCODE_BADNOTFOUND;
403
0
        switch(nodeContext->elementClassiefier) {
404
0
        case UA_NS0ID_DATASETREADERTYPE_PUBLISHERID: {
405
0
            UA_Variant tmp;
406
0
            UA_PublisherId_toVariant(&dataSetReader->config.publisherId, &tmp);
407
0
            value->hasValue = true;
408
0
            return UA_Variant_copy(&tmp, &value->value);
409
0
        }
410
0
        case UA_NS0ID_DATASETREADERTYPE_STATUS_STATE:
411
0
            value->hasValue = true;
412
0
            return UA_Variant_setScalarCopy(&value->value, &dataSetReader->head.state,
413
0
                                            &UA_TYPES[UA_TYPES_PUBSUBSTATE]);
414
0
        default: break;
415
0
        }
416
0
        break;
417
0
    }
418
0
    case UA_NS0ID_WRITERGROUPTYPE: {
419
0
        UA_WriterGroup *writerGroup = UA_WriterGroup_find(psm, *myNodeId);
420
0
        if(!writerGroup)
421
0
            return UA_STATUSCODE_BADNOTFOUND;
422
0
        switch(nodeContext->elementClassiefier){
423
0
        case UA_NS0ID_WRITERGROUPTYPE_PUBLISHINGINTERVAL:
424
0
            value->hasValue = true;
425
0
            return UA_Variant_setScalarCopy(&value->value,
426
0
                                            &writerGroup->config.publishingInterval,
427
0
                                            &UA_TYPES[UA_TYPES_DURATION]);
428
0
            break;
429
0
        case UA_NS0ID_PUBSUBGROUPTYPE_STATUS_STATE:
430
0
            value->hasValue = true;
431
0
            return UA_Variant_setScalarCopy(&value->value, &writerGroup->head.state,
432
0
                                            &UA_TYPES[UA_TYPES_PUBSUBSTATE]);
433
0
            break;
434
0
        default: break;
435
0
        }
436
0
        break;
437
0
    }
438
0
    case UA_NS0ID_DATASETWRITERTYPE: {
439
0
        UA_DataSetWriter *dataSetWriter = UA_DataSetWriter_find(psm, *myNodeId);
440
0
        if(!dataSetWriter)
441
0
            return UA_STATUSCODE_BADNOTFOUND;
442
0
        switch(nodeContext->elementClassiefier) {
443
0
        case UA_NS0ID_DATASETWRITERTYPE_DATASETWRITERID:
444
0
            value->hasValue = true;
445
0
            return UA_Variant_setScalarCopy(&value->value, &dataSetWriter->config.dataSetWriterId,
446
0
                                            &UA_TYPES[UA_TYPES_UINT16]);
447
0
        case UA_NS0ID_DATASETWRITERTYPE_STATUS_STATE:
448
0
            value->hasValue = true;
449
0
            return UA_Variant_setScalarCopy(&value->value, &dataSetWriter->head.state,
450
0
                                            &UA_TYPES[UA_TYPES_PUBSUBSTATE]);
451
0
        default: break;
452
0
        }
453
0
        break;
454
0
    }
455
0
    case UA_NS0ID_PUBLISHEDDATAITEMSTYPE: {
456
0
        UA_PublishedDataSet *publishedDataSet = UA_PublishedDataSet_find(psm, *myNodeId);
457
0
        if(!publishedDataSet)
458
0
            return UA_STATUSCODE_BADNOTFOUND;
459
0
        switch(nodeContext->elementClassiefier) {
460
0
        case UA_NS0ID_PUBLISHEDDATAITEMSTYPE_PUBLISHEDDATA: {
461
0
            UA_PublishedVariableDataType *pvd = (UA_PublishedVariableDataType *)
462
0
                UA_calloc(publishedDataSet->fieldSize, sizeof(UA_PublishedVariableDataType));
463
0
            size_t counter = 0;
464
0
            UA_DataSetField *field;
465
0
            TAILQ_FOREACH(field, &publishedDataSet->fields, listEntry) {
466
0
                pvd[counter].attributeId = UA_ATTRIBUTEID_VALUE;
467
0
                pvd[counter].publishedVariable =
468
0
                    field->config.field.variable.publishParameters.publishedVariable;
469
0
                UA_NodeId_copy(&field->config.field.variable.publishParameters.publishedVariable,
470
0
                               &pvd[counter].publishedVariable);
471
0
                counter++;
472
0
            }
473
0
            value->hasValue = true;
474
0
            UA_Variant_setArray(&value->value, pvd, publishedDataSet->fieldSize,
475
0
                                &UA_TYPES[UA_TYPES_PUBLISHEDVARIABLEDATATYPE]);
476
0
            return UA_STATUSCODE_GOOD;
477
0
        }
478
0
        case UA_NS0ID_PUBLISHEDDATASETTYPE_DATASETMETADATA:
479
0
            value->hasValue = true;
480
0
            return UA_Variant_setScalarCopy(&value->value, &publishedDataSet->dataSetMetaData,
481
0
                                            &UA_TYPES[UA_TYPES_DATASETMETADATATYPE]);
482
0
        case UA_NS0ID_PUBLISHEDDATASETTYPE_CONFIGURATIONVERSION:
483
0
            value->hasValue = true;
484
0
            return UA_Variant_setScalarCopy(&value->value,
485
0
                                            &publishedDataSet->dataSetMetaData.configurationVersion,
486
0
                                            &UA_TYPES[UA_TYPES_CONFIGURATIONVERSIONDATATYPE]);
487
0
        default: break;
488
0
        }
489
0
        break;
490
0
    }
491
0
    case UA_NS0ID_STANDALONESUBSCRIBEDDATASETREFDATATYPE: {
492
0
        UA_SubscribedDataSet *sds = UA_SubscribedDataSet_find(psm, *myNodeId);
493
0
        if(!sds)
494
0
            return UA_STATUSCODE_BADNOTFOUND;
495
0
        switch(nodeContext->elementClassiefier) {
496
0
        case UA_NS0ID_STANDALONESUBSCRIBEDDATASETTYPE_ISCONNECTED: {
497
0
            UA_Boolean isConnected = (sds->connectedReader != NULL);
498
0
            value->hasValue = true;
499
0
            return UA_Variant_setScalarCopy(&value->value, &isConnected,
500
0
                                            &UA_TYPES[UA_TYPES_BOOLEAN]);
501
0
        }
502
0
        case UA_NS0ID_STANDALONESUBSCRIBEDDATASETTYPE_DATASETMETADATA: {
503
0
            value->hasValue = true;
504
0
            return UA_Variant_setScalarCopy(&value->value, &sds->config.dataSetMetaData,
505
0
                                            &UA_TYPES[UA_TYPES_DATASETMETADATATYPE]);
506
0
        }
507
0
        default: break;
508
0
        }
509
0
        break;
510
0
    }
511
0
    default: break;
512
0
    }
513
514
0
    UA_LOG_WARNING(server->config.logging, UA_LOGCATEGORY_SERVER,
515
0
                   "Read error! Unknown property");
516
0
    return UA_STATUSCODE_BADINTERNALERROR;
517
0
}
518
519
static UA_StatusCode
520
WriteCallback(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext,
521
              const UA_NodeId *nodeId, void *nodeContext,
522
0
              const UA_NumericRange *range, const UA_DataValue *data) {
523
0
    UA_LOCK_ASSERT(&server->serviceMutex);
524
525
0
    UA_NodePropertyContext *npc = (UA_NodePropertyContext *)nodeContext;
526
527
0
    UA_PubSubManager *psm = getPSM(server);
528
0
    if(!psm)
529
0
        return UA_STATUSCODE_BADNOTFOUND;
530
531
0
    UA_WriterGroup *writerGroup = NULL;
532
0
    UA_StatusCode res = UA_STATUSCODE_BADNOTWRITABLE;
533
0
    switch(npc->parentClassifier) {
534
0
    case UA_NS0ID_PUBSUBCONNECTIONTYPE:
535
0
        break;
536
0
    case UA_NS0ID_WRITERGROUPTYPE: {
537
0
        writerGroup = UA_WriterGroup_find(psm, npc->parentNodeId);
538
0
        if(!writerGroup)
539
0
            return UA_STATUSCODE_BADNOTFOUND;
540
0
        switch(npc->elementClassiefier) {
541
0
        case UA_NS0ID_WRITERGROUPTYPE_PUBLISHINGINTERVAL: {
542
0
            if(!UA_Variant_hasScalarType(&data->value, &UA_TYPES[UA_TYPES_DURATION]) &&
543
0
               !UA_Variant_hasScalarType(&data->value, &UA_TYPES[UA_TYPES_DOUBLE]))
544
0
                return UA_STATUSCODE_BADTYPEMISMATCH;
545
0
            UA_Duration interval = *((UA_Duration *) data->value.data);
546
0
            if(interval <= 0.0)
547
0
                return UA_STATUSCODE_BADOUTOFRANGE;
548
0
            writerGroup->config.publishingInterval = interval;
549
0
            if(writerGroup->head.state == UA_PUBSUBSTATE_OPERATIONAL) {
550
0
                UA_WriterGroup_removePublishCallback(psm, writerGroup);
551
0
                UA_WriterGroup_addPublishCallback(psm, writerGroup);
552
0
            }
553
0
            return UA_STATUSCODE_GOOD;
554
0
        }
555
0
        default: break;
556
0
        }
557
0
        break;
558
0
    }
559
0
    default: break;
560
0
    }
561
562
0
    UA_LOG_WARNING(server->config.logging, UA_LOGCATEGORY_SERVER,
563
0
                   "Changing the ReaderGroupConfig failed");
564
0
    return res;
565
0
}
566
567
static UA_StatusCode
568
setVariableValueSource(UA_Server *server, const UA_CallbackValueSource evs,
569
553
                       UA_NodeId node, UA_NodePropertyContext *context){
570
553
    UA_LOCK_ASSERT(&server->serviceMutex);
571
553
    setNodeContext(server, node, context);
572
553
    return setVariableNode_callbackValueSource(server, node, evs);
573
553
}
574
575
static UA_StatusCode
576
addPubSubConnectionConfig(UA_Server *server, UA_PubSubConnectionDataType *pubsubConnection,
577
0
                          UA_NodeId *connectionId) {
578
0
    UA_LOCK_ASSERT(&server->serviceMutex);
579
580
0
    UA_PubSubManager *psm = getPSM(server);
581
0
    if(!psm)
582
0
        return UA_STATUSCODE_BADINTERNALERROR;
583
584
0
    UA_StatusCode retVal = UA_STATUSCODE_GOOD;
585
0
    UA_NetworkAddressUrlDataType networkAddressUrl;
586
0
    memset(&networkAddressUrl, 0, sizeof(networkAddressUrl));
587
0
    UA_ExtensionObject *eo = &pubsubConnection->address;
588
0
    if(eo->encoding == UA_EXTENSIONOBJECT_DECODED &&
589
0
       eo->content.decoded.type == &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]) {
590
0
        void *data = eo->content.decoded.data;
591
0
        retVal =
592
0
            UA_NetworkAddressUrlDataType_copy((UA_NetworkAddressUrlDataType *)data,
593
0
                                              &networkAddressUrl);
594
0
        if(retVal != UA_STATUSCODE_GOOD)
595
0
            return retVal;
596
0
    }
597
598
0
    UA_PubSubConnectionConfig connectionConfig;
599
0
    memset(&connectionConfig, 0, sizeof(UA_PubSubConnectionConfig));
600
0
    connectionConfig.transportProfileUri = pubsubConnection->transportProfileUri;
601
0
    connectionConfig.name = pubsubConnection->name;
602
0
    UA_Variant_setScalar(&connectionConfig.address, &networkAddressUrl,
603
0
                         &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]);
604
605
0
    retVal |= UA_PublisherId_fromVariant(&connectionConfig.publisherId,
606
0
                                         &pubsubConnection->publisherId);
607
0
    retVal |= UA_PubSubConnection_create(psm, &connectionConfig, connectionId);
608
0
    UA_NetworkAddressUrlDataType_clear(&networkAddressUrl);
609
0
    return retVal;
610
0
}
611
612
/**
613
 * **WriterGroup handling**
614
 *
615
 * The WriterGroup (WG) is part of the connection and contains the primary
616
 * configuration parameters for the message creation. */
617
static UA_StatusCode
618
addWriterGroupConfig(UA_Server *server, UA_NodeId connectionId,
619
0
                     UA_WriterGroupDataType *writerGroup, UA_NodeId *writerGroupId){
620
0
    UA_LOCK_ASSERT(&server->serviceMutex);
621
622
0
    UA_PubSubManager *psm = getPSM(server);
623
0
    if(!psm)
624
0
        return UA_STATUSCODE_BADINTERNALERROR;
625
626
    /* Now we create a new WriterGroupConfig and add the group to the existing
627
     * PubSubConnection. */
628
0
    UA_WriterGroupConfig writerGroupConfig;
629
0
    memset(&writerGroupConfig, 0, sizeof(UA_WriterGroupConfig));
630
0
    writerGroupConfig.name = writerGroup->name;
631
0
    writerGroupConfig.publishingInterval = writerGroup->publishingInterval;
632
0
    writerGroupConfig.writerGroupId = writerGroup->writerGroupId;
633
0
    writerGroupConfig.priority = writerGroup->priority;
634
635
0
    UA_ExtensionObject *eoWG = &writerGroup->messageSettings;
636
0
    UA_UadpWriterGroupMessageDataType uadpWriterGroupMessage;
637
0
    UA_JsonWriterGroupMessageDataType jsonWriterGroupMessage;
638
0
    if(eoWG->encoding == UA_EXTENSIONOBJECT_DECODED){
639
0
        writerGroupConfig.messageSettings.encoding  = UA_EXTENSIONOBJECT_DECODED;
640
0
        if(eoWG->content.decoded.type == &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE]){
641
0
            writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP;
642
0
            if(UA_UadpWriterGroupMessageDataType_copy(
643
0
                    (UA_UadpWriterGroupMessageDataType *)eoWG->content.decoded.data,
644
0
                    &uadpWriterGroupMessage) != UA_STATUSCODE_GOOD) {
645
0
                return UA_STATUSCODE_BADOUTOFMEMORY;
646
0
            }
647
0
            writerGroupConfig.messageSettings.content.decoded.type = &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE];
648
0
            writerGroupConfig.messageSettings.content.decoded.data = &uadpWriterGroupMessage;
649
0
        } else if(eoWG->content.decoded.type == &UA_TYPES[UA_TYPES_JSONWRITERGROUPMESSAGEDATATYPE]) {
650
0
            writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_JSON;
651
0
            if(UA_JsonWriterGroupMessageDataType_copy(
652
0
                   (UA_JsonWriterGroupMessageDataType *)eoWG->content.decoded.data,
653
0
                   &jsonWriterGroupMessage) != UA_STATUSCODE_GOOD) {
654
0
                return UA_STATUSCODE_BADOUTOFMEMORY;
655
0
            }
656
0
            writerGroupConfig.messageSettings.content.decoded.type = &UA_TYPES[UA_TYPES_JSONWRITERGROUPMESSAGEDATATYPE];
657
0
            writerGroupConfig.messageSettings.content.decoded.data = &jsonWriterGroupMessage;
658
0
        }
659
0
    }
660
661
0
    eoWG = &writerGroup->transportSettings;
662
0
    UA_BrokerWriterGroupTransportDataType brokerWriterGroupTransport;
663
0
    UA_DatagramWriterGroupTransportDataType datagramWriterGroupTransport;
664
0
    if(eoWG->encoding == UA_EXTENSIONOBJECT_DECODED) {
665
0
        writerGroupConfig.transportSettings.encoding = UA_EXTENSIONOBJECT_DECODED;
666
0
        if(eoWG->content.decoded.type == &UA_TYPES[UA_TYPES_BROKERWRITERGROUPTRANSPORTDATATYPE]) {
667
0
            if(UA_BrokerWriterGroupTransportDataType_copy(
668
0
                    (UA_BrokerWriterGroupTransportDataType*)eoWG->content.decoded.data,
669
0
                    &brokerWriterGroupTransport) != UA_STATUSCODE_GOOD) {
670
0
                return UA_STATUSCODE_BADOUTOFMEMORY;
671
0
            }
672
0
            writerGroupConfig.transportSettings.content.decoded.type = &UA_TYPES[UA_TYPES_BROKERWRITERGROUPTRANSPORTDATATYPE];
673
0
            writerGroupConfig.transportSettings.content.decoded.data = &brokerWriterGroupTransport;
674
0
        } else if(eoWG->content.decoded.type == &UA_TYPES[UA_TYPES_DATAGRAMWRITERGROUPTRANSPORTDATATYPE]) {
675
0
            if(UA_DatagramWriterGroupTransportDataType_copy(
676
0
                   (UA_DatagramWriterGroupTransportDataType *)eoWG->content.decoded.data,
677
0
                   &datagramWriterGroupTransport) != UA_STATUSCODE_GOOD) {
678
0
                return UA_STATUSCODE_BADOUTOFMEMORY;
679
0
            }
680
0
            writerGroupConfig.transportSettings.content.decoded.type = &UA_TYPES[UA_TYPES_DATAGRAMWRITERGROUPTRANSPORTDATATYPE];
681
0
            writerGroupConfig.transportSettings.content.decoded.data = &datagramWriterGroupTransport;
682
0
        }
683
0
    }
684
0
    if (writerGroupConfig.encodingMimeType == UA_PUBSUB_ENCODING_JSON
685
0
        && (writerGroupConfig.transportSettings.encoding != UA_EXTENSIONOBJECT_DECODED ||
686
0
        writerGroupConfig.transportSettings.content.decoded.type !=
687
0
            &UA_TYPES[UA_TYPES_BROKERWRITERGROUPTRANSPORTDATATYPE])) {
688
0
        UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER,
689
0
                     "JSON encoding is supported only for MQTT transport");
690
0
        return UA_STATUSCODE_BADCONFIGURATIONERROR;
691
0
    }
692
693
0
    return UA_WriterGroup_create(psm, connectionId, &writerGroupConfig, writerGroupId);
694
0
}
695
696
/**
697
 * **DataSetWriter handling**
698
 *
699
 * A DataSetWriter (DSW) is the glue between the WG and the PDS. The DSW is
700
 * linked to exactly one PDS and contains additional informations for the
701
 * message generation. */
702
static UA_StatusCode
703
addDataSetWriterConfig(UA_Server *server, const UA_NodeId *writerGroupId,
704
                       UA_DataSetWriterDataType *dataSetWriter,
705
0
                       UA_NodeId *dataSetWriterId) {
706
0
    UA_LOCK_ASSERT(&server->serviceMutex);
707
708
0
    UA_PubSubManager *psm = getPSM(server);
709
0
    if(!psm)
710
0
        return UA_STATUSCODE_BADINTERNALERROR;
711
712
0
    UA_NodeId publishedDataSetId = UA_NODEID_NULL;
713
0
    UA_PublishedDataSet *tmpPDS;
714
0
    TAILQ_FOREACH(tmpPDS, &psm->publishedDataSets, listEntry){
715
0
        if(UA_String_equal(&dataSetWriter->dataSetName, &tmpPDS->config.name)) {
716
0
            publishedDataSetId = tmpPDS->head.identifier;
717
0
            break;
718
0
        }
719
0
    }
720
721
0
    if(UA_NodeId_isNull(&publishedDataSetId))
722
0
        return UA_STATUSCODE_BADPARENTNODEIDINVALID;
723
724
    /* We need now a DataSetWriter within the WriterGroup. This means we must
725
     * create a new DataSetWriterConfig and add call the addWriterGroup function. */
726
0
    UA_DataSetWriterConfig dataSetWriterConfig;
727
0
    memset(&dataSetWriterConfig, 0, sizeof(UA_DataSetWriterConfig));
728
0
    dataSetWriterConfig.name = dataSetWriter->name;
729
0
    dataSetWriterConfig.dataSetWriterId = dataSetWriter->dataSetWriterId;
730
0
    dataSetWriterConfig.keyFrameCount = dataSetWriter->keyFrameCount;
731
0
    dataSetWriterConfig.dataSetFieldContentMask =  dataSetWriter->dataSetFieldContentMask;
732
0
    return UA_DataSetWriter_create(psm, *writerGroupId, publishedDataSetId,
733
0
                                   &dataSetWriterConfig, dataSetWriterId);
734
0
}
735
736
/**
737
 * **ReaderGroup**
738
 *
739
 * ReaderGroup is used to group a list of DataSetReaders. All ReaderGroups are
740
 * created within a PubSubConnection and automatically deleted if the connection
741
 * is removed. All network message related filters are only available in the DataSetReader. */
742
/* Add ReaderGroup to the created connection */
743
static UA_StatusCode
744
addReaderGroupConfig(UA_Server *server, UA_NodeId connectionId,
745
                     UA_ReaderGroupDataType *readerGroup,
746
0
                     UA_NodeId *readerGroupId) {
747
0
    UA_LOCK_ASSERT(&server->serviceMutex);
748
749
0
    UA_PubSubManager *psm = getPSM(server);
750
0
    if(!psm)
751
0
        return UA_STATUSCODE_BADINTERNALERROR;
752
753
0
    UA_ReaderGroupConfig readerGroupConfig;
754
0
    memset(&readerGroupConfig, 0, sizeof(UA_ReaderGroupConfig));
755
0
    readerGroupConfig.name = readerGroup->name;
756
0
    return UA_ReaderGroup_create(psm, connectionId,
757
0
                                 &readerGroupConfig, readerGroupId);
758
0
}
759
760
/**
761
 * **SubscribedDataSet**
762
 *
763
 * Set SubscribedDataSet type to TargetVariables data type.
764
 * Add subscribedvariables to the DataSetReader */
765
static UA_StatusCode
766
addSubscribedVariables(UA_Server *server, UA_NodeId dataSetReaderId,
767
                       UA_DataSetReaderDataType *dataSetReader,
768
0
                       UA_DataSetMetaDataType *pMetaData) {
769
0
    UA_LOCK_ASSERT(&server->serviceMutex);
770
771
0
    UA_PubSubManager *psm = getPSM(server);
772
0
    if(!psm)
773
0
        return UA_STATUSCODE_BADINTERNALERROR;
774
775
0
    UA_ExtensionObject *eoTargetVar = &dataSetReader->subscribedDataSet;
776
0
    if(eoTargetVar->encoding != UA_EXTENSIONOBJECT_DECODED ||
777
0
       eoTargetVar->content.decoded.type != &UA_TYPES[UA_TYPES_TARGETVARIABLESDATATYPE])
778
0
        return UA_STATUSCODE_BADUNEXPECTEDERROR;
779
780
0
    const UA_TargetVariablesDataType *targetVars =
781
0
        (UA_TargetVariablesDataType*)eoTargetVar->content.decoded.data;
782
783
0
    UA_DataSetReader *dsr = UA_DataSetReader_find(psm, dataSetReaderId);
784
0
    if(!dsr)
785
0
        return UA_STATUSCODE_BADINTERNALERROR;
786
787
0
    UA_NodeId folderId;
788
0
    UA_String folderName = pMetaData->name;
789
0
    UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
790
0
    UA_QualifiedName folderBrowseName;
791
0
    if(folderName.length > 0) {
792
0
        oAttr.displayName.locale = UA_STRING("");
793
0
        oAttr.displayName.text = folderName;
794
0
        folderBrowseName.namespaceIndex = 1;
795
0
        folderBrowseName.name = folderName;
796
0
    } else {
797
0
        oAttr.displayName = UA_LOCALIZEDTEXT("", "Subscribed Variables");
798
0
        folderBrowseName = UA_QUALIFIEDNAME(1, "Subscribed Variables");
799
0
    }
800
801
0
    addNode(server, UA_NODECLASS_OBJECT, UA_NODEID_NULL,
802
0
            UA_NS0ID(OBJECTSFOLDER), UA_NS0ID(ORGANIZES),
803
0
            folderBrowseName, UA_NS0ID(BASEOBJECTTYPE),
804
0
            &oAttr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],
805
0
            NULL, &folderId);
806
807
    /* The SubscribedDataSet option TargetVariables defines a list of Variable
808
     * mappings between received DataSet fields and target Variables in the
809
     * Subscriber AddressSpace. The values subscribed from the Publisher are
810
     * updated in the value field of these variables */
811
812
    /* Add variable for the fields */
813
0
    for(size_t i = 0; i < targetVars->targetVariablesSize; i++) {
814
0
        UA_VariableAttributes vAttr = UA_VariableAttributes_default;
815
0
        vAttr.description = pMetaData->fields[i].description;
816
0
        vAttr.displayName.locale = UA_STRING("");
817
0
        vAttr.displayName.text = pMetaData->fields[i].name;
818
0
        vAttr.dataType = pMetaData->fields[i].dataType;
819
0
        UA_QualifiedName varname = {1, pMetaData->fields[i].name};
820
0
        addNode(server, UA_NODECLASS_VARIABLE,
821
0
                targetVars->targetVariables[i].targetNodeId, folderId,
822
0
                UA_NS0ID(HASCOMPONENT), varname, UA_NS0ID(BASEDATAVARIABLETYPE),
823
0
                &vAttr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],
824
0
                NULL, &targetVars->targetVariables[i].targetNodeId);
825
0
    }
826
827
    /* Set the TargetVariables in the DSR config */
828
0
    return DataSetReader_createTargetVariables(psm, dsr,
829
0
                                               targetVars->targetVariablesSize,
830
0
                                               targetVars->targetVariables);
831
0
}
832
833
/**
834
 * **DataSetReader**
835
 *
836
 * DataSetReader can receive NetworkMessages with the DataSetMessage
837
 * of interest sent by the Publisher. DataSetReader provides
838
 * the configuration necessary to receive and process DataSetMessages
839
 * on the Subscriber side. DataSetReader must be linked with a
840
 * SubscribedDataSet and be contained within a ReaderGroup. */
841
static UA_StatusCode
842
addDataSetReaderConfig(UA_Server *server, UA_NodeId readerGroupId,
843
                       UA_DataSetReaderDataType *dataSetReader,
844
0
                       UA_NodeId *dataSetReaderId) {
845
0
    UA_LOCK_ASSERT(&server->serviceMutex);
846
847
0
    UA_PubSubManager *psm = getPSM(server);
848
0
    if(!psm)
849
0
        return UA_STATUSCODE_BADINTERNALERROR;
850
851
0
    UA_DataSetReaderConfig readerConfig;
852
0
    memset(&readerConfig, 0, sizeof(UA_DataSetReaderConfig));
853
854
0
    UA_StatusCode retVal =
855
0
        UA_PublisherId_fromVariant(&readerConfig.publisherId,
856
0
                                   &dataSetReader->publisherId);
857
0
    readerConfig.name = dataSetReader->name;
858
0
    readerConfig.writerGroupId = dataSetReader->writerGroupId;
859
0
    readerConfig.dataSetWriterId = dataSetReader->dataSetWriterId;
860
861
    /* Setting up Meta data configuration in DataSetReader */
862
0
    UA_DataSetMetaDataType *pMetaData;
863
0
    pMetaData = &readerConfig.dataSetMetaData;
864
0
    UA_DataSetMetaDataType_init (pMetaData);
865
0
    pMetaData->name =  dataSetReader->dataSetMetaData.name;
866
0
    pMetaData->fieldsSize = dataSetReader->dataSetMetaData.fieldsSize;
867
0
    pMetaData->fields = (UA_FieldMetaData*)UA_Array_new (pMetaData->fieldsSize,
868
0
                        &UA_TYPES[UA_TYPES_FIELDMETADATA]);
869
0
    if(pMetaData->fieldsSize > 0 && !pMetaData->fields) {
870
0
        UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_PUBSUB,
871
0
                     "Failed to allocate memory for DataSetReader MetaData fields");
872
0
        UA_PublisherId_clear(&readerConfig.publisherId);
873
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
874
0
    }
875
0
    for(size_t i = 0; i < pMetaData->fieldsSize; i++){
876
0
        UA_FieldMetaData_init (&pMetaData->fields[i]);
877
0
        UA_NodeId_copy(&dataSetReader->dataSetMetaData.fields[i].dataType,
878
0
                       &pMetaData->fields[i].dataType);
879
0
        pMetaData->fields[i].builtInType = dataSetReader->dataSetMetaData.fields[i].builtInType;
880
0
        pMetaData->fields[i].name = dataSetReader->dataSetMetaData.fields[i].name;
881
0
        pMetaData->fields[i].valueRank = dataSetReader->dataSetMetaData.fields[i].valueRank;
882
0
    }
883
884
0
    retVal |= UA_DataSetReader_create(psm, readerGroupId,
885
0
                                      &readerConfig, dataSetReaderId);
886
0
    UA_PublisherId_clear(&readerConfig.publisherId);
887
0
    if(retVal != UA_STATUSCODE_GOOD) {
888
0
        UA_free(pMetaData->fields);
889
0
        return retVal;
890
0
    }
891
892
0
    retVal |= addSubscribedVariables(server, *dataSetReaderId, dataSetReader, pMetaData);
893
0
    UA_free(pMetaData->fields);
894
0
    return retVal;
895
0
}
896
897
/*************************************************/
898
/*            PubSubConnection                   */
899
/*************************************************/
900
901
UA_StatusCode
902
0
addPubSubConnectionRepresentation(UA_Server *server, UA_PubSubConnection *connection) {
903
0
    UA_StatusCode retVal = UA_STATUSCODE_GOOD;
904
0
    if(connection->config.name.length > 512)
905
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
906
0
    char connectionName[513];
907
0
    memcpy(connectionName, connection->config.name.data, connection->config.name.length);
908
0
    connectionName[connection->config.name.length] = '\0';
909
910
0
    UA_ObjectAttributes attr = UA_ObjectAttributes_default;
911
0
    attr.displayName = UA_LOCALIZEDTEXT("", connectionName);
912
0
    retVal |= addNode_begin(server, UA_NODECLASS_OBJECT,
913
0
                            UA_NODEID_NUMERIC(1, 0), /* Generate a new id */
914
0
                            UA_NS0ID(PUBLISHSUBSCRIBE),
915
0
                            UA_NS0ID(HASPUBSUBCONNECTION),
916
0
                            UA_QUALIFIEDNAME(0, connectionName),
917
0
                            UA_NS0ID(PUBSUBCONNECTIONTYPE),
918
0
                            (const UA_NodeAttributes*)&attr,
919
0
                            &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],
920
0
                            NULL, &connection->head.identifier);
921
922
0
    attr.displayName = UA_LOCALIZEDTEXT("", "Address");
923
0
    retVal |= addNode(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(1, 0),
924
0
                      connection->head.identifier, UA_NS0ID(HASCOMPONENT),
925
0
                      UA_QUALIFIEDNAME(0, "Address"), UA_NS0ID(NETWORKADDRESSURLTYPE),
926
0
                      &attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES], NULL, NULL);
927
928
0
    retVal |= addNode_finish(server, &server->adminSession, &connection->head.identifier);
929
930
0
    UA_NodeId addressNode =
931
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Address"),
932
0
                            UA_NS0ID(HASCOMPONENT), connection->head.identifier);
933
0
    UA_NodeId urlNode =
934
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Url"),
935
0
                            UA_NS0ID(HASCOMPONENT), addressNode);
936
0
    UA_NodeId interfaceNode =
937
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "NetworkInterface"),
938
0
                            UA_NS0ID(HASCOMPONENT), addressNode);
939
0
    UA_NodeId publisherIdNode =
940
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublisherId"),
941
0
                            UA_NS0ID(HASPROPERTY), connection->head.identifier);
942
0
    UA_NodeId connectionPropertyNode =
943
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "ConnectionProperties"),
944
0
                            UA_NS0ID(HASPROPERTY), connection->head.identifier);
945
0
    UA_NodeId transportProfileUri =
946
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "TransportProfileUri"),
947
0
                            UA_NS0ID(HASCOMPONENT), connection->head.identifier);
948
0
    UA_NodeId statusIdNode =
949
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Status"),
950
0
                            UA_NS0ID(HASCOMPONENT), connection->head.identifier);
951
952
0
    if(UA_NodeId_isNull(&addressNode) || UA_NodeId_isNull(&urlNode) ||
953
0
       UA_NodeId_isNull(&interfaceNode) || UA_NodeId_isNull(&publisherIdNode) ||
954
0
       UA_NodeId_isNull(&connectionPropertyNode) ||
955
0
       UA_NodeId_isNull(&transportProfileUri)) {
956
0
        return UA_STATUSCODE_BADNOTFOUND;
957
0
    }
958
959
0
    retVal |= writePubSubNs0VariableArray(server, connectionPropertyNode,
960
0
                                          connection->config.connectionProperties.map,
961
0
                                          connection->config.connectionProperties.mapSize,
962
0
                                          &UA_TYPES[UA_TYPES_KEYVALUEPAIR]);
963
964
0
    UA_NetworkAddressUrlDataType *networkAddressUrl=
965
0
        ((UA_NetworkAddressUrlDataType*)connection->config.address.data);
966
0
    UA_Variant value;
967
0
    UA_Variant_init(&value);
968
969
0
    UA_Variant_setScalar(&value, &networkAddressUrl->url, &UA_TYPES[UA_TYPES_STRING]);
970
0
    writeValueAttribute(server, urlNode, &value);
971
972
0
    UA_Variant_setScalar(&value, &networkAddressUrl->networkInterface, &UA_TYPES[UA_TYPES_STRING]);
973
0
    writeValueAttribute(server, interfaceNode, &value);
974
975
0
    UA_Variant_setScalar(&value, &connection->config.transportProfileUri, &UA_TYPES[UA_TYPES_STRING]);
976
0
    writeValueAttribute(server, transportProfileUri, &value);
977
978
0
    UA_NodePropertyContext *connectionPublisherIdContext =
979
0
        (UA_NodePropertyContext *)UA_malloc(sizeof(UA_NodePropertyContext));
980
0
    connectionPublisherIdContext->parentNodeId = connection->head.identifier;
981
0
    connectionPublisherIdContext->parentClassifier = UA_NS0ID_PUBSUBCONNECTIONTYPE;
982
0
    connectionPublisherIdContext->elementClassiefier = UA_NS0ID_PUBSUBCONNECTIONTYPE_PUBLISHERID;
983
984
0
    UA_CallbackValueSource valueCallback;
985
0
    valueCallback.read = ReadCallback;
986
0
    valueCallback.write = NULL;
987
0
    retVal |= setVariableValueSource(server, valueCallback, publisherIdNode,
988
0
                                     connectionPublisherIdContext);
989
990
0
    if(!UA_NodeId_isNull(&statusIdNode)) {
991
0
        UA_NodeId stateNodeId = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "State"),
992
0
                                                   UA_NS0ID(HASCOMPONENT), statusIdNode);
993
0
        if(!UA_NodeId_isNull(&stateNodeId)) {
994
0
            UA_DataSource stateDataSource;
995
0
            stateDataSource.read = pubSubStateVariableDataSourceRead;
996
0
            stateDataSource.write = NULL;
997
0
            retVal |= UA_Server_setVariableNode_dataSource(server, stateNodeId, stateDataSource);
998
0
        }
999
0
    }
1000
1001
0
    if(server->config.pubSubConfig.enableInformationModelMethods) {
1002
0
        retVal |= addRef(server, connection->head.identifier, UA_NS0ID(HASCOMPONENT),
1003
0
                         UA_NS0ID(PUBSUBCONNECTIONTYPE_ADDWRITERGROUP), true);
1004
0
        retVal |= addRef(server, connection->head.identifier, UA_NS0ID(HASCOMPONENT),
1005
0
                         UA_NS0ID(PUBSUBCONNECTIONTYPE_ADDREADERGROUP), true);
1006
0
        retVal |= addRef(server, connection->head.identifier, UA_NS0ID(HASCOMPONENT),
1007
0
                         UA_NS0ID(PUBSUBCONNECTIONTYPE_REMOVEGROUP), true);
1008
        
1009
0
        if(!UA_NodeId_isNull(&statusIdNode)) {
1010
0
            retVal |= addRef(server, statusIdNode,
1011
0
                            UA_NS0ID(HASCOMPONENT),
1012
0
                            UA_NS0ID(PUBSUBSTATUSTYPE_ENABLE), true);
1013
0
            retVal |= addRef(server, statusIdNode,
1014
0
                            UA_NS0ID(HASCOMPONENT),
1015
0
                            UA_NS0ID(PUBSUBSTATUSTYPE_DISABLE), true);
1016
0
        }
1017
0
    }
1018
0
    return retVal;
1019
0
}
1020
1021
static UA_StatusCode
1022
addPubSubConnectionAction(UA_Server *server,
1023
                          const UA_NodeId *sessionId, void *sessionContext,
1024
                          const UA_NodeId *methodId, void *methodContext,
1025
                          const UA_NodeId *objectId, void *objectContext,
1026
                          size_t inputSize, const UA_Variant *input,
1027
0
                          size_t outputSize, UA_Variant *output) {
1028
0
    UA_LOCK_ASSERT(&server->serviceMutex);
1029
1030
0
    UA_PubSubManager *psm = getPSM(server);
1031
0
    if(!psm)
1032
0
        return UA_STATUSCODE_BADINTERNALERROR;
1033
1034
0
    UA_StatusCode retVal = UA_STATUSCODE_GOOD;
1035
0
    UA_PubSubConnectionDataType *pubSubConnection =
1036
0
        (UA_PubSubConnectionDataType *) input[0].data;
1037
1038
    //call API function and create the connection
1039
0
    UA_NodeId connectionId;
1040
0
    retVal |= addPubSubConnectionConfig(server, pubSubConnection, &connectionId);
1041
0
    if(retVal != UA_STATUSCODE_GOOD) {
1042
0
        UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER,
1043
0
                     "addPubSubConnection failed");
1044
0
        return retVal;
1045
0
    }
1046
1047
0
    for(size_t i = 0; i < pubSubConnection->writerGroupsSize; i++) {
1048
0
        UA_NodeId writerGroupId;
1049
0
        UA_WriterGroupDataType *writerGroup = &pubSubConnection->writerGroups[i];
1050
0
        retVal |= addWriterGroupConfig(server, connectionId, writerGroup, &writerGroupId);
1051
0
        if(retVal != UA_STATUSCODE_GOOD) {
1052
0
            UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER,
1053
0
                         "addWriterGroup failed");
1054
0
            return retVal;
1055
0
        }
1056
1057
0
        for(size_t j = 0; j < writerGroup->dataSetWritersSize; j++) {
1058
0
            UA_DataSetWriterDataType *dataSetWriter = &writerGroup->dataSetWriters[j];
1059
0
            retVal |= addDataSetWriterConfig(server, &writerGroupId, dataSetWriter, NULL);
1060
0
            if(retVal != UA_STATUSCODE_GOOD) {
1061
0
                UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER,
1062
0
                             "addDataSetWriter failed");
1063
0
                return retVal;
1064
0
            }
1065
0
        }
1066
1067
        /* TODO: Need to handle the UA_Server_setWriterGroupOperational based on
1068
         * the status variable in information model */
1069
0
        UA_WriterGroup *wg = UA_WriterGroup_find(psm, writerGroupId);
1070
0
        if(!wg)
1071
0
            continue;
1072
0
        if(pubSubConnection->enabled) {
1073
0
            UA_WriterGroup_setPubSubState(psm, wg, UA_PUBSUBSTATE_OPERATIONAL);
1074
0
        } else {
1075
0
            UA_WriterGroup_setPubSubState(psm, wg, UA_PUBSUBSTATE_DISABLED);
1076
0
        }
1077
0
    }
1078
1079
0
    for(size_t i = 0; i < pubSubConnection->readerGroupsSize; i++){
1080
0
        UA_NodeId readerGroupId;
1081
0
        UA_ReaderGroupDataType *readerGroup = &pubSubConnection->readerGroups[i];
1082
0
        retVal |= addReaderGroupConfig(server, connectionId, readerGroup, &readerGroupId);
1083
0
        if(retVal != UA_STATUSCODE_GOOD) {
1084
0
            UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER,
1085
0
                         "addReaderGroup failed");
1086
0
            return retVal;
1087
0
        }
1088
1089
0
        for(size_t j = 0; j < readerGroup->dataSetReadersSize; j++) {
1090
0
            UA_NodeId dataSetReaderId;
1091
0
            UA_DataSetReaderDataType *dataSetReader = &readerGroup->dataSetReaders[j];
1092
0
            retVal |= addDataSetReaderConfig(server, readerGroupId,
1093
0
                                             dataSetReader, &dataSetReaderId);
1094
0
            if(retVal != UA_STATUSCODE_GOOD) {
1095
0
                UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER,
1096
0
                             "addDataSetReader failed");
1097
0
                return retVal;
1098
0
            }
1099
1100
0
        }
1101
1102
        /* TODO: Need to handle the UA_Server_setReaderGroupOperational based on
1103
         * the status variable in information model */
1104
0
        UA_ReaderGroup *rg = UA_ReaderGroup_find(psm, readerGroupId);
1105
0
        if(!rg)
1106
0
            continue;
1107
0
        if(pubSubConnection->enabled) {
1108
0
            UA_ReaderGroup_setPubSubState(psm, rg, UA_PUBSUBSTATE_OPERATIONAL);
1109
0
        } else {
1110
0
            UA_ReaderGroup_setPubSubState(psm, rg, UA_PUBSUBSTATE_DISABLED);
1111
0
        }
1112
0
    }
1113
1114
    /* Set ouput value */
1115
0
    UA_Variant_setScalarCopy(output, &connectionId, &UA_TYPES[UA_TYPES_NODEID]);
1116
0
    return UA_STATUSCODE_GOOD;
1117
0
}
1118
1119
static UA_StatusCode
1120
removeConnectionAction(UA_Server *server,
1121
                       const UA_NodeId *sessionId, void *sessionContext,
1122
                       const UA_NodeId *methodId, void *methodContext,
1123
                       const UA_NodeId *objectId, void *objectContext,
1124
                       size_t inputSize, const UA_Variant *input,
1125
0
                       size_t outputSize, UA_Variant *output){
1126
0
    UA_StatusCode retVal = UA_STATUSCODE_GOOD;
1127
0
    UA_NodeId nodeToRemove = *((UA_NodeId *) input[0].data);
1128
0
    retVal |= UA_Server_removePubSubConnection(server, nodeToRemove);
1129
0
    if(retVal == UA_STATUSCODE_BADNOTFOUND)
1130
0
        retVal = UA_STATUSCODE_BADNODEIDUNKNOWN;
1131
0
    return retVal;
1132
0
}
1133
1134
/**********************************************/
1135
/*               DataSetReader                */
1136
/**********************************************/
1137
1138
UA_StatusCode
1139
0
addDataSetReaderRepresentation(UA_Server *server, UA_DataSetReader *dataSetReader){
1140
0
    UA_LOCK_ASSERT(&server->serviceMutex);
1141
1142
0
    if(dataSetReader->config.name.length > 512)
1143
0
        return UA_STATUSCODE_BADCONFIGURATIONERROR;
1144
1145
0
    char dsrName[513];
1146
0
    memcpy(dsrName, dataSetReader->config.name.data, dataSetReader->config.name.length);
1147
0
    dsrName[dataSetReader->config.name.length] = '\0';
1148
1149
0
    UA_StatusCode retVal = UA_STATUSCODE_GOOD;
1150
0
    UA_NodeId publisherIdNode, writerGroupIdNode, dataSetwriterIdNode, statusIdNode, stateIdNode;
1151
1152
0
    UA_ObjectAttributes object_attr = UA_ObjectAttributes_default;
1153
0
    object_attr.displayName = UA_LOCALIZEDTEXT("", dsrName);
1154
0
    retVal = addNode(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(1, 0), /* create an id */
1155
0
                     dataSetReader->linkedReaderGroup->head.identifier,
1156
0
                     UA_NODEID_NUMERIC(0, UA_NS0ID_HASDATASETREADER),
1157
0
                     UA_QUALIFIEDNAME(0, dsrName),
1158
0
                     UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETREADERTYPE),
1159
0
                     &object_attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],
1160
0
                     NULL, &dataSetReader->head.identifier);
1161
1162
    /* Add childNodes such as PublisherId, WriterGroupId and DataSetWriterId in
1163
     * DataSetReader object */
1164
0
    publisherIdNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublisherId"),
1165
0
                                          UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
1166
0
                                          dataSetReader->head.identifier);
1167
0
    writerGroupIdNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "WriterGroupId"),
1168
0
                                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
1169
0
                                            dataSetReader->head.identifier);
1170
0
    dataSetwriterIdNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "DataSetWriterId"),
1171
0
                                              UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
1172
0
                                              dataSetReader->head.identifier);
1173
0
    statusIdNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Status"),
1174
0
                                              UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
1175
0
                                              dataSetReader->head.identifier);
1176
1177
0
    if(UA_NodeId_isNull(&statusIdNode))
1178
0
        return UA_STATUSCODE_BADNOTFOUND;
1179
1180
0
    stateIdNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "State"),
1181
0
                                              UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
1182
0
                                              statusIdNode);
1183
1184
0
    if(UA_NodeId_isNull(&publisherIdNode) ||
1185
0
       UA_NodeId_isNull(&writerGroupIdNode) ||
1186
0
       UA_NodeId_isNull(&dataSetwriterIdNode) ||
1187
0
       UA_NodeId_isNull(&stateIdNode))
1188
0
        return UA_STATUSCODE_BADNOTFOUND;
1189
1190
0
    UA_NodePropertyContext *dataSetReaderPublisherIdContext =
1191
0
        (UA_NodePropertyContext *) UA_malloc(sizeof(UA_NodePropertyContext));
1192
0
    dataSetReaderPublisherIdContext->parentNodeId = dataSetReader->head.identifier;
1193
0
    dataSetReaderPublisherIdContext->parentClassifier = UA_NS0ID_DATASETREADERTYPE;
1194
0
    dataSetReaderPublisherIdContext->elementClassiefier = UA_NS0ID_DATASETREADERTYPE_PUBLISHERID;
1195
0
    UA_CallbackValueSource valueCallback;
1196
0
    valueCallback.read = ReadCallback;
1197
0
    valueCallback.write = NULL;
1198
0
    retVal |= setVariableValueSource(server, valueCallback, publisherIdNode,
1199
0
                                     dataSetReaderPublisherIdContext);
1200
1201
0
    UA_NodeId stateNodeId = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "State"),
1202
0
                                               UA_NS0ID(HASCOMPONENT), statusIdNode);
1203
0
    if(!UA_NodeId_isNull(&stateNodeId)) {
1204
0
        UA_DataSource stateDataSource;
1205
0
        stateDataSource.read = pubSubStateVariableDataSourceRead;
1206
0
        stateDataSource.write = NULL; 
1207
0
        retVal |= UA_Server_setVariableNode_dataSource(server, stateNodeId, stateDataSource);
1208
0
    }
1209
1210
    /* Update childNode with values from Publisher */
1211
0
    UA_Variant value;
1212
0
    UA_Variant_init(&value);
1213
0
    UA_Variant_setScalar(&value, &dataSetReader->config.writerGroupId,
1214
0
                         &UA_TYPES[UA_TYPES_UINT16]);
1215
0
    writeValueAttribute(server, writerGroupIdNode, &value);
1216
0
    UA_Variant_setScalar(&value, &dataSetReader->config.dataSetWriterId,
1217
0
                         &UA_TYPES[UA_TYPES_UINT16]);
1218
0
    writeValueAttribute(server, dataSetwriterIdNode, &value);
1219
1220
0
    if(server->config.pubSubConfig.enableInformationModelMethods) {
1221
0
        retVal |= addRef(server, statusIdNode, UA_NS0ID(HASCOMPONENT), 
1222
0
                        UA_NS0ID(PUBSUBSTATUSTYPE_ENABLE), true);
1223
0
        retVal |= addRef(server, statusIdNode, UA_NS0ID(HASCOMPONENT), 
1224
0
                        UA_NS0ID(PUBSUBSTATUSTYPE_DISABLE), true);
1225
0
    }
1226
    
1227
0
    return retVal;
1228
0
}
1229
1230
static UA_StatusCode
1231
addDataSetReaderAction(UA_Server *server,
1232
                       const UA_NodeId *sessionId, void *sessionContext,
1233
                       const UA_NodeId *methodId, void *methodContext,
1234
                       const UA_NodeId *objectId, void *objectContext,
1235
                       size_t inputSize, const UA_Variant *input,
1236
0
                       size_t outputSize, UA_Variant *output) {
1237
0
    UA_LOCK_ASSERT(&server->serviceMutex);
1238
1239
0
    UA_NodeId dataSetReaderId;
1240
0
    UA_DataSetReaderDataType *dataSetReader= (UA_DataSetReaderDataType *) input[0].data;
1241
0
    UA_StatusCode retVal =
1242
0
        addDataSetReaderConfig(server, *objectId, dataSetReader, &dataSetReaderId);
1243
0
    if(retVal != UA_STATUSCODE_GOOD) {
1244
0
        UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER,
1245
0
                     "AddDataSetReader failed");
1246
0
        return retVal;
1247
0
    }
1248
1249
0
    UA_Variant_setScalarCopy(output, &dataSetReaderId, &UA_TYPES[UA_TYPES_NODEID]);
1250
0
    return retVal;
1251
0
}
1252
1253
static UA_StatusCode
1254
removeDataSetReaderAction(UA_Server *server,
1255
                          const UA_NodeId *sessionId, void *sessionContext,
1256
                          const UA_NodeId *methodId, void *methodContext,
1257
                          const UA_NodeId *objectId, void *objectContext,
1258
                          size_t inputSize, const UA_Variant *input,
1259
0
                          size_t outputSize, UA_Variant *output){
1260
0
    UA_NodeId nodeToRemove = *((UA_NodeId *)input[0].data);
1261
0
    return UA_Server_removeDataSetReader(server, nodeToRemove);
1262
0
}
1263
1264
/*************************************************/
1265
/*                PublishedDataSet               */
1266
/*************************************************/
1267
1268
static UA_StatusCode
1269
addDataSetFolderAction(UA_Server *server,
1270
                       const UA_NodeId *sessionId, void *sessionContext,
1271
                       const UA_NodeId *methodId, void *methodContext,
1272
                       const UA_NodeId *objectId, void *objectContext,
1273
                       size_t inputSize, const UA_Variant *input,
1274
0
                       size_t outputSize, UA_Variant *output){
1275
    /* defined in R 1.04 9.1.4.5.7 */
1276
0
    UA_StatusCode retVal = UA_STATUSCODE_GOOD;
1277
0
    UA_String newFolderName = *((UA_String *) input[0].data);
1278
0
    UA_NodeId generatedId;
1279
0
    UA_ObjectAttributes objectAttributes = UA_ObjectAttributes_default;
1280
0
    UA_LocalizedText name = {UA_STRING(""), newFolderName};
1281
0
    objectAttributes.displayName = name;
1282
0
    retVal |= UA_Server_addObjectNode(server, UA_NODEID_NULL, *objectId,
1283
0
                                      UA_NS0ID(ORGANIZES),
1284
0
                                      UA_QUALIFIEDNAME(0, "DataSetFolder"),
1285
0
                                      UA_NS0ID(DATASETFOLDERTYPE),
1286
0
                                      objectAttributes, NULL, &generatedId);
1287
0
    UA_Variant_setScalarCopy(output, &generatedId, &UA_TYPES[UA_TYPES_NODEID]);
1288
1289
0
    if(server->config.pubSubConfig.enableInformationModelMethods) {
1290
0
        retVal |= UA_Server_addReference(server, generatedId, UA_NS0ID(HASCOMPONENT),
1291
0
                                         UA_NS0EXID(DATASETFOLDERTYPE_ADDPUBLISHEDDATAITEMS), true);
1292
0
        retVal |= UA_Server_addReference(server, generatedId, UA_NS0ID(HASCOMPONENT),
1293
0
                                         UA_NS0EXID(DATASETFOLDERTYPE_REMOVEPUBLISHEDDATASET), true);
1294
0
        retVal |= UA_Server_addReference(server, generatedId, UA_NS0ID(HASCOMPONENT),
1295
0
                                         UA_NS0EXID(DATASETFOLDERTYPE_ADDDATASETFOLDER), true);
1296
0
        retVal |= UA_Server_addReference(server, generatedId, UA_NS0ID(HASCOMPONENT),
1297
0
                                         UA_NS0EXID(DATASETFOLDERTYPE_REMOVEDATASETFOLDER), true);
1298
0
    }
1299
0
    return retVal;
1300
0
}
1301
1302
static UA_StatusCode
1303
removeDataSetFolderAction(UA_Server *server,
1304
                          const UA_NodeId *sessionId, void *sessionContext,
1305
                          const UA_NodeId *methodId, void *methodContext,
1306
                          const UA_NodeId *objectId, void *objectContext,
1307
                          size_t inputSize, const UA_Variant *input,
1308
0
                          size_t outputSize, UA_Variant *output) {
1309
0
    UA_NodeId nodeToRemove = *((UA_NodeId *) input[0].data);
1310
0
    return UA_Server_deleteNode(server, nodeToRemove, true);
1311
0
}
1312
1313
UA_StatusCode
1314
addPublishedDataItemsRepresentation(UA_Server *server,
1315
0
                                    UA_PublishedDataSet *publishedDataSet) {
1316
0
    UA_LOCK_ASSERT(&server->serviceMutex);
1317
1318
0
    UA_StatusCode retVal = UA_STATUSCODE_GOOD;
1319
0
    if(publishedDataSet->config.name.length > 512)
1320
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
1321
0
    char pdsName[513];
1322
0
    memcpy(pdsName, publishedDataSet->config.name.data, publishedDataSet->config.name.length);
1323
0
    pdsName[publishedDataSet->config.name.length] = '\0';
1324
1325
0
    UA_ObjectAttributes object_attr = UA_ObjectAttributes_default;
1326
0
    object_attr.displayName = UA_LOCALIZEDTEXT("", pdsName);
1327
0
    retVal = addNode(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(1, 0), /* Create a new id */
1328
0
                     UA_NS0ID(PUBLISHSUBSCRIBE_PUBLISHEDDATASETS),
1329
0
                     UA_NS0ID(HASCOMPONENT),
1330
0
                     UA_QUALIFIEDNAME(0, pdsName),
1331
0
                     UA_NS0ID(PUBLISHEDDATAITEMSTYPE),
1332
0
                     &object_attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],
1333
0
                     NULL, &publishedDataSet->head.identifier);
1334
0
    UA_CHECK_STATUS(retVal, return retVal);
1335
1336
0
    UA_CallbackValueSource valueCallback;
1337
0
    valueCallback.read = ReadCallback;
1338
0
    valueCallback.write = NULL;
1339
    //ToDo: Need to move the browse name from namespaceindex 0 to 1
1340
0
    UA_NodeId configurationVersionNode =
1341
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "ConfigurationVersion"),
1342
0
                            UA_NS0ID(HASPROPERTY), publishedDataSet->head.identifier);
1343
0
    if(UA_NodeId_isNull(&configurationVersionNode))
1344
0
        return UA_STATUSCODE_BADNOTFOUND;
1345
1346
0
    UA_NodePropertyContext *configurationVersionContext = (UA_NodePropertyContext *)
1347
0
        UA_malloc(sizeof(UA_NodePropertyContext));
1348
0
    configurationVersionContext->parentNodeId = publishedDataSet->head.identifier;
1349
0
    configurationVersionContext->parentClassifier = UA_NS0ID_PUBLISHEDDATAITEMSTYPE;
1350
0
    configurationVersionContext->elementClassiefier =
1351
0
        UA_NS0ID_PUBLISHEDDATASETTYPE_CONFIGURATIONVERSION;
1352
0
    retVal |= setVariableValueSource(server, valueCallback, configurationVersionNode,
1353
0
                                     configurationVersionContext);
1354
1355
0
    UA_NodeId publishedDataNode =
1356
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublishedData"),
1357
0
                            UA_NS0ID(HASPROPERTY), publishedDataSet->head.identifier);
1358
0
    if(UA_NodeId_isNull(&publishedDataNode))
1359
0
        return UA_STATUSCODE_BADNOTFOUND;
1360
1361
0
    UA_NodePropertyContext * publishingIntervalContext = (UA_NodePropertyContext *)
1362
0
        UA_malloc(sizeof(UA_NodePropertyContext));
1363
0
    publishingIntervalContext->parentNodeId = publishedDataSet->head.identifier;
1364
0
    publishingIntervalContext->parentClassifier = UA_NS0ID_PUBLISHEDDATAITEMSTYPE;
1365
0
    publishingIntervalContext->elementClassiefier = UA_NS0ID_PUBLISHEDDATAITEMSTYPE_PUBLISHEDDATA;
1366
0
    retVal |= setVariableValueSource(server, valueCallback, publishedDataNode,
1367
0
                                     publishingIntervalContext);
1368
1369
0
    UA_NodeId dataSetMetaDataNode =
1370
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "DataSetMetaData"),
1371
0
                            UA_NS0ID(HASPROPERTY), publishedDataSet->head.identifier);
1372
0
    if(UA_NodeId_isNull(&dataSetMetaDataNode))
1373
0
        return UA_STATUSCODE_BADNOTFOUND;
1374
1375
0
    UA_NodePropertyContext *metaDataContext = (UA_NodePropertyContext *)
1376
0
        UA_malloc(sizeof(UA_NodePropertyContext));
1377
0
    metaDataContext->parentNodeId = publishedDataSet->head.identifier;
1378
0
    metaDataContext->parentClassifier = UA_NS0ID_PUBLISHEDDATAITEMSTYPE;
1379
0
    metaDataContext->elementClassiefier = UA_NS0ID_PUBLISHEDDATASETTYPE_DATASETMETADATA;
1380
0
    retVal |= setVariableValueSource(server, valueCallback,
1381
0
                                     dataSetMetaDataNode, metaDataContext);
1382
1383
0
    if(server->config.pubSubConfig.enableInformationModelMethods) {
1384
0
        retVal |= addRef(server, publishedDataSet->head.identifier, UA_NS0ID(HASCOMPONENT),
1385
0
                         UA_NS0ID(PUBLISHEDDATAITEMSTYPE_ADDVARIABLES), true);
1386
0
        retVal |= addRef(server, publishedDataSet->head.identifier, UA_NS0ID(HASCOMPONENT),
1387
0
                         UA_NS0ID(PUBLISHEDDATAITEMSTYPE_REMOVEVARIABLES), true);
1388
0
    }
1389
0
    return retVal;
1390
0
}
1391
1392
static UA_StatusCode
1393
addPublishedDataItemsAction(UA_Server *server,
1394
                            const UA_NodeId *sessionId, void *sessionContext,
1395
                            const UA_NodeId *methodId, void *methodContext,
1396
                            const UA_NodeId *objectId, void *objectContext,
1397
                            size_t inputSize, const UA_Variant *input,
1398
0
                            size_t outputSize, UA_Variant *output){
1399
0
    UA_StatusCode retVal = UA_STATUSCODE_GOOD;
1400
0
    size_t fieldNameAliasesSize = input[1].arrayLength;
1401
0
    UA_String * fieldNameAliases = (UA_String *) input[1].data;
1402
0
    size_t fieldFlagsSize = input[2].arrayLength;
1403
0
    UA_DataSetFieldFlags * fieldFlags = (UA_DataSetFieldFlags *) input[2].data;
1404
0
    size_t variablesToAddSize = input[3].arrayLength;
1405
0
    UA_PublishedVariableDataType *eoAddVar =
1406
0
        (UA_PublishedVariableDataType *)input[3].data;
1407
1408
0
    if(fieldNameAliasesSize != fieldFlagsSize ||
1409
0
       fieldFlagsSize != variablesToAddSize)
1410
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
1411
1412
0
    UA_PublishedDataSetConfig publishedDataSetConfig;
1413
0
    memset(&publishedDataSetConfig, 0, sizeof(publishedDataSetConfig));
1414
0
    publishedDataSetConfig.name = *((UA_String *) input[0].data);
1415
0
    publishedDataSetConfig.publishedDataSetType = UA_PUBSUB_DATASET_PUBLISHEDITEMS;
1416
1417
0
    UA_NodeId dataSetItemsNodeId;
1418
0
    retVal |= UA_Server_addPublishedDataSet(server, &publishedDataSetConfig,
1419
0
                                            &dataSetItemsNodeId).addResult;
1420
0
    if(retVal != UA_STATUSCODE_GOOD) {
1421
0
        UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER,
1422
0
                     "addPublishedDataset failed");
1423
0
        return retVal;
1424
0
    }
1425
1426
0
    UA_DataSetFieldConfig dataSetFieldConfig;
1427
0
    for(size_t j = 0; j < variablesToAddSize; ++j) {
1428
        /* Prepare the config */
1429
0
        memset(&dataSetFieldConfig, 0, sizeof(UA_DataSetFieldConfig));
1430
0
        dataSetFieldConfig.dataSetFieldType = UA_PUBSUB_DATASETFIELD_VARIABLE;
1431
0
        dataSetFieldConfig.field.variable.fieldNameAlias = fieldNameAliases[j];
1432
0
        dataSetFieldConfig.field.variable.publishParameters = eoAddVar[j];
1433
0
        if(fieldFlags[j] == UA_DATASETFIELDFLAGS_PROMOTEDFIELD)
1434
0
            dataSetFieldConfig.field.variable.promotedField = true;
1435
0
        retVal |= UA_Server_addDataSetField(server, dataSetItemsNodeId,
1436
0
                                            &dataSetFieldConfig, NULL).result;
1437
0
        if(retVal != UA_STATUSCODE_GOOD) {
1438
0
           UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER,
1439
0
                        "addDataSetField failed");
1440
0
           return retVal;
1441
0
        }
1442
0
    }
1443
1444
0
    UA_Variant_setScalarCopy(output, &dataSetItemsNodeId, &UA_TYPES[UA_TYPES_NODEID]);
1445
0
    return retVal;
1446
0
}
1447
1448
static UA_StatusCode
1449
addVariablesAction(UA_Server *server,
1450
                   const UA_NodeId *sessionId, void *sessionContext,
1451
                   const UA_NodeId *methodId, void *methodContext,
1452
                   const UA_NodeId *objectId, void *objectContext,
1453
                   size_t inputSize, const UA_Variant *input,
1454
0
                   size_t outputSize, UA_Variant *output){
1455
0
    return UA_STATUSCODE_GOOD;
1456
0
}
1457
1458
static UA_StatusCode
1459
removeVariablesAction(UA_Server *server,
1460
                      const UA_NodeId *sessionId, void *sessionContext,
1461
                      const UA_NodeId *methodId, void *methodContext,
1462
                      const UA_NodeId *objectId, void *objectContext,
1463
                      size_t inputSize, const UA_Variant *input,
1464
0
                      size_t outputSize, UA_Variant *output){
1465
0
    return UA_STATUSCODE_GOOD;
1466
0
}
1467
1468
static UA_StatusCode
1469
removePublishedDataSetAction(UA_Server *server,
1470
                             const UA_NodeId *sessionId, void *sessionContext,
1471
                             const UA_NodeId *methodId, void *methodContext,
1472
                             const UA_NodeId *objectId, void *objectContext,
1473
                             size_t inputSize, const UA_Variant *input,
1474
0
                             size_t outputSize, UA_Variant *output){
1475
0
    UA_NodeId nodeToRemove = *((UA_NodeId *) input[0].data);
1476
0
    return UA_Server_removePublishedDataSet(server, nodeToRemove);
1477
0
}
1478
1479
/*********************/
1480
/* SubscribedDataSet */
1481
/*********************/
1482
1483
UA_StatusCode
1484
addSubscribedDataSetRepresentation(UA_Server *server,
1485
0
                                   UA_SubscribedDataSet *subscribedDataSet) {
1486
0
    UA_LOCK_ASSERT(&server->serviceMutex);
1487
1488
0
    UA_StatusCode ret = UA_STATUSCODE_GOOD;
1489
0
    if(subscribedDataSet->config.name.length > 512)
1490
0
        return UA_STATUSCODE_BADCONFIGURATIONERROR;
1491
1492
0
    UA_STACKARRAY(char, sdsName, sizeof(char) * subscribedDataSet->config.name.length +1);
1493
0
    memcpy(sdsName, subscribedDataSet->config.name.data, subscribedDataSet->config.name.length);
1494
0
    sdsName[subscribedDataSet->config.name.length] = '\0';
1495
1496
0
    UA_ObjectAttributes object_attr = UA_ObjectAttributes_default;
1497
0
    object_attr.displayName = UA_LOCALIZEDTEXT("", sdsName);
1498
0
    addNode(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(1, 0), /* Create a new id */
1499
0
            UA_NS0ID(PUBLISHSUBSCRIBE_SUBSCRIBEDDATASETS),
1500
0
            UA_NS0ID(HASCOMPONENT),
1501
0
            UA_QUALIFIEDNAME(0, sdsName),
1502
0
            UA_NS0ID(STANDALONESUBSCRIBEDDATASETTYPE),
1503
0
            &object_attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],
1504
0
            NULL, &subscribedDataSet->head.identifier);
1505
0
    UA_NodeId sdsObjectNode =
1506
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "SubscribedDataSet"),
1507
0
                            UA_NS0ID(HASCOMPONENT), subscribedDataSet->head.identifier);
1508
0
    UA_NodeId metaDataId =
1509
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "DataSetMetaData"),
1510
0
                            UA_NS0ID(HASPROPERTY), subscribedDataSet->head.identifier);
1511
0
    UA_NodeId connectedId =
1512
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "IsConnected"),
1513
0
                            UA_NS0ID(HASPROPERTY), subscribedDataSet->head.identifier);
1514
1515
0
    if(UA_NodeId_equal(&sdsObjectNode, &UA_NODEID_NULL) ||
1516
0
       UA_NodeId_equal(&metaDataId, &UA_NODEID_NULL) ||
1517
0
       UA_NodeId_equal(&connectedId, &UA_NODEID_NULL)) {
1518
0
        return UA_STATUSCODE_BADNOTFOUND;
1519
0
    }
1520
0
    if(subscribedDataSet->config.subscribedDataSetType == UA_PUBSUB_SDS_TARGET){
1521
0
        UA_VariableAttributes attr = UA_VariableAttributes_default;
1522
0
        UA_NodeId targetVarsId;
1523
0
        attr.displayName = UA_LOCALIZEDTEXT("", "TargetVariables");
1524
0
        attr.dataType = UA_TYPES[UA_TYPES_FIELDTARGETDATATYPE].typeId;
1525
0
        attr.valueRank = UA_VALUERANK_ONE_DIMENSION;
1526
0
        attr.arrayDimensionsSize = 1;
1527
0
        UA_UInt32 arrayDimensions[1];
1528
0
        arrayDimensions[0] = (UA_UInt32)
1529
0
            subscribedDataSet->config.subscribedDataSet.target.targetVariablesSize;
1530
0
        attr.arrayDimensions = arrayDimensions;
1531
0
        attr.accessLevel = UA_ACCESSLEVELMASK_READ;
1532
0
        UA_Variant_setArray(&attr.value,
1533
0
                            subscribedDataSet->config.subscribedDataSet.target.targetVariables,
1534
0
                            subscribedDataSet->config.subscribedDataSet.target.targetVariablesSize,
1535
0
                            &UA_TYPES[UA_TYPES_FIELDTARGETDATATYPE]);
1536
0
        ret |= addNode(server, UA_NODECLASS_VARIABLE, UA_NODEID_NULL, sdsObjectNode,
1537
0
                       UA_NS0ID(HASPROPERTY), UA_QUALIFIEDNAME(0, "TargetVariables"),
1538
0
                       UA_NS0ID(PROPERTYTYPE), &attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],
1539
0
                       NULL, &targetVarsId);
1540
0
    }
1541
1542
0
    UA_NodePropertyContext *isConnectedNodeContext = (UA_NodePropertyContext *)
1543
0
        UA_malloc(sizeof(UA_NodePropertyContext));
1544
0
    isConnectedNodeContext->parentNodeId = subscribedDataSet->head.identifier;
1545
0
    isConnectedNodeContext->parentClassifier = UA_NS0ID_STANDALONESUBSCRIBEDDATASETREFDATATYPE;
1546
0
    isConnectedNodeContext->elementClassiefier = UA_NS0ID_STANDALONESUBSCRIBEDDATASETTYPE_ISCONNECTED;
1547
1548
0
    UA_CallbackValueSource valueCallback;
1549
0
    valueCallback.read = ReadCallback;
1550
0
    valueCallback.write = NULL;
1551
0
    ret |= setVariableValueSource(server, valueCallback, connectedId, isConnectedNodeContext);
1552
1553
0
    UA_NodePropertyContext *metaDataContext = (UA_NodePropertyContext *)
1554
0
        UA_malloc(sizeof(UA_NodePropertyContext));
1555
0
    metaDataContext->parentNodeId = subscribedDataSet->head.identifier;
1556
0
    metaDataContext->parentClassifier = UA_NS0ID_STANDALONESUBSCRIBEDDATASETREFDATATYPE;
1557
0
    metaDataContext->elementClassiefier = UA_NS0ID_STANDALONESUBSCRIBEDDATASETTYPE_DATASETMETADATA;
1558
0
    ret |= setVariableValueSource(server, valueCallback, metaDataId, metaDataContext);
1559
1560
0
    return ret;
1561
0
}
1562
1563
/**********************************************/
1564
/*               WriterGroup                  */
1565
/**********************************************/
1566
1567
static UA_StatusCode
1568
readContentMask(UA_Server *server, const UA_NodeId *sessionId,
1569
                void *sessionContext, const UA_NodeId *nodeId,
1570
                void *nodeContext, UA_Boolean includeSourceTimeStamp,
1571
0
                const UA_NumericRange *range, UA_DataValue *value) {
1572
0
    UA_WriterGroup *writerGroup = (UA_WriterGroup*)nodeContext;
1573
0
    if((writerGroup->config.messageSettings.encoding != UA_EXTENSIONOBJECT_DECODED &&
1574
0
        writerGroup->config.messageSettings.encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE) ||
1575
0
       writerGroup->config.messageSettings.content.decoded.type !=
1576
0
       &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE])
1577
0
        return UA_STATUSCODE_BADINTERNALERROR;
1578
0
    UA_UadpWriterGroupMessageDataType *wgm = (UA_UadpWriterGroupMessageDataType*)
1579
0
        writerGroup->config.messageSettings.content.decoded.data;
1580
1581
0
    UA_Variant_setScalarCopy(&value->value, &wgm->networkMessageContentMask,
1582
0
                             &UA_TYPES[UA_TYPES_UADPNETWORKMESSAGECONTENTMASK]);
1583
0
    value->hasValue = true;
1584
0
    return UA_STATUSCODE_GOOD;
1585
0
}
1586
1587
static UA_StatusCode
1588
writeContentMask(UA_Server *server, const UA_NodeId *sessionId,
1589
                 void *sessionContext, const UA_NodeId *nodeId,
1590
                 void *nodeContext, const UA_NumericRange *range,
1591
0
                 const UA_DataValue *value) {
1592
0
    UA_WriterGroup *writerGroup = (UA_WriterGroup*)nodeContext;
1593
0
    if((writerGroup->config.messageSettings.encoding != UA_EXTENSIONOBJECT_DECODED &&
1594
0
        writerGroup->config.messageSettings.encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE) ||
1595
0
       writerGroup->config.messageSettings.content.decoded.type !=
1596
0
       &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE])
1597
0
        return UA_STATUSCODE_BADINTERNALERROR;
1598
0
    UA_UadpWriterGroupMessageDataType *wgm = (UA_UadpWriterGroupMessageDataType*)
1599
0
        writerGroup->config.messageSettings.content.decoded.data;
1600
1601
0
    if(!value->value.type)
1602
0
        return UA_STATUSCODE_BADTYPEMISMATCH;
1603
0
    if(value->value.type->typeKind != UA_DATATYPEKIND_ENUM &&
1604
0
       value->value.type->typeKind != UA_DATATYPEKIND_INT32)
1605
0
        return UA_STATUSCODE_BADTYPEMISMATCH;
1606
1607
0
    wgm->networkMessageContentMask = *(UA_UadpNetworkMessageContentMask*)value->value.data;
1608
0
    return UA_STATUSCODE_GOOD;
1609
0
}
1610
1611
static UA_StatusCode
1612
readGroupVersion(UA_Server *server, const UA_NodeId *sessionId,
1613
                void *sessionContext, const UA_NodeId *nodeId,
1614
                void *nodeContext, UA_Boolean includeSourceTimeStamp,
1615
0
                const UA_NumericRange *range, UA_DataValue *value) {
1616
0
    UA_WriterGroup *writerGroup = (UA_WriterGroup*)nodeContext;
1617
0
    if((writerGroup->config.messageSettings.encoding != UA_EXTENSIONOBJECT_DECODED &&
1618
0
        writerGroup->config.messageSettings.encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE) ||
1619
0
       writerGroup->config.messageSettings.content.decoded.type !=
1620
0
       &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE])
1621
0
        return UA_STATUSCODE_BADINTERNALERROR;
1622
0
    UA_UadpWriterGroupMessageDataType *wgm = (UA_UadpWriterGroupMessageDataType*)
1623
0
        writerGroup->config.messageSettings.content.decoded.data;
1624
1625
0
    UA_Variant_setScalarCopy(&value->value, &wgm->groupVersion,
1626
0
                             &UA_TYPES[UA_DATATYPEKIND_UINT32]);
1627
0
    value->hasValue = true;
1628
0
    return UA_STATUSCODE_GOOD;
1629
0
}
1630
1631
UA_StatusCode
1632
0
addWriterGroupRepresentation(UA_Server *server, UA_WriterGroup *writerGroup) {
1633
0
    UA_LOCK_ASSERT(&server->serviceMutex);
1634
1635
0
    UA_StatusCode retVal = UA_STATUSCODE_GOOD;
1636
0
    if(writerGroup->config.name.length > 512)
1637
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
1638
0
    char wgName[513];
1639
0
    memcpy(wgName, writerGroup->config.name.data, writerGroup->config.name.length);
1640
0
    wgName[writerGroup->config.name.length] = '\0';
1641
1642
0
    UA_ObjectAttributes object_attr = UA_ObjectAttributes_default;
1643
0
    object_attr.displayName = UA_LOCALIZEDTEXT("", wgName);
1644
0
    retVal = addNode(server, UA_NODECLASS_OBJECT,
1645
0
                     UA_NODEID_NUMERIC(1, 0), /* create a new id */
1646
0
                     writerGroup->linkedConnection->head.identifier, UA_NS0ID(HASCOMPONENT),
1647
0
                     UA_QUALIFIEDNAME(0, wgName), UA_NS0ID(WRITERGROUPTYPE), &object_attr,
1648
0
                     &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES], NULL, &writerGroup->head.identifier);
1649
1650
0
    UA_NodeId keepAliveNode =
1651
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "KeepAliveTime"),
1652
0
                            UA_NS0ID(HASPROPERTY), writerGroup->head.identifier);
1653
0
    UA_NodeId publishingIntervalNode =
1654
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublishingInterval"),
1655
0
                            UA_NS0ID(HASPROPERTY), writerGroup->head.identifier);
1656
0
    UA_NodeId statusIdNode = 
1657
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Status"),
1658
0
                            UA_NS0ID(HASCOMPONENT), writerGroup->head.identifier);
1659
1660
0
    if(UA_NodeId_isNull(&statusIdNode))
1661
0
        return UA_STATUSCODE_BADNOTFOUND;
1662
1663
0
    UA_NodeId stateIdNode = 
1664
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "State"),
1665
0
                            UA_NS0ID(HASCOMPONENT), statusIdNode);
1666
1667
0
    if(UA_NodeId_isNull(&keepAliveNode) ||
1668
0
       UA_NodeId_isNull(&publishingIntervalNode) ||
1669
0
       UA_NodeId_isNull(&stateIdNode))
1670
0
        return UA_STATUSCODE_BADNOTFOUND;
1671
1672
0
    UA_NodePropertyContext * publishingIntervalContext = (UA_NodePropertyContext *)
1673
0
        UA_malloc(sizeof(UA_NodePropertyContext));
1674
0
    publishingIntervalContext->parentNodeId = writerGroup->head.identifier;
1675
0
    publishingIntervalContext->parentClassifier = UA_NS0ID_WRITERGROUPTYPE;
1676
0
    publishingIntervalContext->elementClassiefier = UA_NS0ID_WRITERGROUPTYPE_PUBLISHINGINTERVAL;
1677
0
    UA_CallbackValueSource valueCallback;
1678
0
    valueCallback.read = ReadCallback;
1679
0
    valueCallback.write = WriteCallback;
1680
0
    retVal |= setVariableValueSource(server, valueCallback,
1681
0
                                     publishingIntervalNode, publishingIntervalContext);
1682
0
    writeAccessLevelAttribute(server, publishingIntervalNode,
1683
0
                              UA_ACCESSLEVELMASK_READ ^ UA_ACCESSLEVELMASK_WRITE);
1684
1685
0
    UA_NodeId stateNodeId = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "State"),
1686
0
                                               UA_NS0ID(HASCOMPONENT), statusIdNode);
1687
0
    if(!UA_NodeId_isNull(&stateNodeId)) {
1688
0
        UA_DataSource stateDataSource;
1689
0
        stateDataSource.read = pubSubStateVariableDataSourceRead;
1690
0
        stateDataSource.write = NULL;
1691
0
        retVal |= UA_Server_setVariableNode_dataSource(server, stateNodeId, stateDataSource);
1692
0
    }
1693
1694
0
    UA_NodeId priorityNode =
1695
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Priority"),
1696
0
                            UA_NS0ID(HASPROPERTY), writerGroup->head.identifier);
1697
0
    UA_NodeId writerGroupIdNode =
1698
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "WriterGroupId"),
1699
0
                            UA_NS0ID(HASPROPERTY), writerGroup->head.identifier);
1700
1701
0
    UA_Variant value;
1702
0
    UA_Variant_init(&value);
1703
0
    UA_Variant_setScalar(&value, &writerGroup->config.publishingInterval, &UA_TYPES[UA_TYPES_DURATION]);
1704
0
    writeValueAttribute(server, publishingIntervalNode, &value);
1705
0
    UA_Variant_setScalar(&value, &writerGroup->config.keepAliveTime, &UA_TYPES[UA_TYPES_DURATION]);
1706
0
    writeValueAttribute(server, keepAliveNode, &value);
1707
0
    UA_Variant_setScalar(&value, &writerGroup->config.priority, &UA_TYPES[UA_TYPES_BYTE]);
1708
0
    writeValueAttribute(server, priorityNode, &value);
1709
0
    UA_Variant_setScalar(&value, &writerGroup->config.writerGroupId, &UA_TYPES[UA_TYPES_UINT16]);
1710
0
    writeValueAttribute(server, writerGroupIdNode, &value);
1711
1712
0
    object_attr.displayName = UA_LOCALIZEDTEXT("", "MessageSettings");
1713
0
    retVal |= addNode(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(1, 0),
1714
0
                      writerGroup->head.identifier, UA_NS0ID(HASCOMPONENT),
1715
0
                      UA_QUALIFIEDNAME(0, "MessageSettings"),
1716
0
                      UA_NS0ID(UADPWRITERGROUPMESSAGETYPE), &object_attr,
1717
0
                      &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],
1718
0
                      NULL, NULL);
1719
1720
    /* Find the variable with the content mask */
1721
1722
0
    UA_NodeId messageSettingsId =
1723
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "MessageSettings"),
1724
0
                            UA_NS0ID(HASCOMPONENT), writerGroup->head.identifier);
1725
0
    UA_NodeId contentMaskId =
1726
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "NetworkMessageContentMask"),
1727
0
                            UA_NS0ID(HASPROPERTY), messageSettingsId);
1728
0
    if(!UA_NodeId_isNull(&contentMaskId)) {
1729
        /* Set the callback */
1730
0
        UA_CallbackValueSource ds;
1731
0
        ds.read = readContentMask;
1732
0
        ds.write = writeContentMask;
1733
0
        setVariableNode_callbackValueSource(server, contentMaskId, ds);
1734
0
        setNodeContext(server, contentMaskId, writerGroup);
1735
1736
        /* Make writable */
1737
0
        writeAccessLevelAttribute(server, contentMaskId,
1738
0
                                  UA_ACCESSLEVELMASK_WRITE | UA_ACCESSLEVELMASK_READ);
1739
1740
0
    }
1741
0
    UA_NodeId groupVersionId =
1742
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "GroupVersion"),
1743
0
                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), messageSettingsId);
1744
0
    if(!UA_NodeId_isNull(&groupVersionId)) {
1745
        /* Set the callback */
1746
0
        UA_CallbackValueSource ds;
1747
0
        ds.read = readGroupVersion;
1748
0
        ds.write = NULL;
1749
0
        setVariableNode_callbackValueSource(server, groupVersionId, ds);
1750
0
        setNodeContext(server, groupVersionId, writerGroup);
1751
1752
        /* Read only */
1753
0
        writeAccessLevelAttribute(server, groupVersionId,
1754
0
                                  UA_ACCESSLEVELMASK_READ);
1755
1756
0
    }
1757
1758
    /* Add reference to methods */
1759
0
    if(server->config.pubSubConfig.enableInformationModelMethods) {
1760
0
        retVal |= addRef(server, writerGroup->head.identifier,
1761
0
                         UA_NS0ID(HASCOMPONENT),
1762
0
                         UA_NS0ID(WRITERGROUPTYPE_ADDDATASETWRITER), true);
1763
0
        retVal |= addRef(server, writerGroup->head.identifier,
1764
0
                         UA_NS0ID(HASCOMPONENT),
1765
0
                         UA_NS0ID(WRITERGROUPTYPE_REMOVEDATASETWRITER), true);
1766
0
        retVal |= addRef(server, statusIdNode, UA_NS0ID(HASCOMPONENT), 
1767
0
                        UA_NS0ID(PUBSUBSTATUSTYPE_ENABLE), true);
1768
0
        retVal |= addRef(server, statusIdNode, UA_NS0ID(HASCOMPONENT), 
1769
0
                        UA_NS0ID(PUBSUBSTATUSTYPE_DISABLE), true);
1770
0
    }
1771
0
    return retVal;
1772
0
}
1773
1774
static UA_StatusCode
1775
addWriterGroupAction(UA_Server *server,
1776
                     const UA_NodeId *sessionId, void *sessionContext,
1777
                     const UA_NodeId *methodId, void *methodContext,
1778
                     const UA_NodeId *objectId, void *objectContext,
1779
                     size_t inputSize, const UA_Variant *input,
1780
0
                     size_t outputSize, UA_Variant *output) {
1781
0
    UA_LOCK_ASSERT(&server->serviceMutex);
1782
1783
0
    UA_NodeId writerGroupId;
1784
0
    UA_WriterGroupDataType *writerGroup = (UA_WriterGroupDataType *)input->data;
1785
0
    UA_StatusCode retVal = addWriterGroupConfig(server, *objectId, writerGroup, &writerGroupId);
1786
0
    if(retVal != UA_STATUSCODE_GOOD) {
1787
0
        UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER, "addWriterGroup failed");
1788
0
        return retVal;
1789
0
    }
1790
    // TODO: Need to handle the UA_Server_setWriterGroupOperational based on the
1791
    // status variable in information model
1792
1793
0
    UA_Variant_setScalarCopy(output, &writerGroupId, &UA_TYPES[UA_TYPES_NODEID]);
1794
0
    return retVal;
1795
0
}
1796
1797
static UA_StatusCode
1798
removeGroupAction(UA_Server *server,
1799
                  const UA_NodeId *sessionId, void *sessionContext,
1800
                  const UA_NodeId *methodId, void *methodContext,
1801
                  const UA_NodeId *objectId, void *objectContext,
1802
                  size_t inputSize, const UA_Variant *input,
1803
0
                  size_t outputSize, UA_Variant *output){
1804
0
    UA_PubSubManager *psm = getPSM(server);
1805
0
    if(!psm)
1806
0
        return UA_STATUSCODE_BADINTERNALERROR;
1807
1808
0
    UA_NodeId nodeToRemove = *((UA_NodeId *)input->data);
1809
0
    if(UA_WriterGroup_find(psm, nodeToRemove)) {
1810
0
        return UA_Server_removeWriterGroup(server, nodeToRemove);
1811
0
    } else {
1812
0
        return UA_Server_removeReaderGroup(server, nodeToRemove);
1813
0
    }
1814
0
}
1815
1816
/**********************************************/
1817
/*               ReserveIds                   */
1818
/**********************************************/
1819
1820
static UA_StatusCode
1821
addReserveIdsAction(UA_Server *server,
1822
                    const UA_NodeId *sessionId, void *sessionContext,
1823
                    const UA_NodeId *methodId, void *methodContext,
1824
                    const UA_NodeId *objectId, void *objectContext,
1825
                    size_t inputSize, const UA_Variant *input,
1826
0
                    size_t outputSize, UA_Variant *output){
1827
0
    UA_LOCK_ASSERT(&server->serviceMutex);
1828
1829
0
    UA_PubSubManager *psm = getPSM(server);
1830
0
    if(!psm)
1831
0
        return UA_STATUSCODE_BADINTERNALERROR;
1832
1833
0
    UA_StatusCode retVal = UA_STATUSCODE_GOOD;
1834
0
    UA_String transportProfileUri = *((UA_String *)input[0].data);
1835
0
    UA_UInt16 numRegWriterGroupIds = *((UA_UInt16 *)input[1].data);
1836
0
    UA_UInt16 numRegDataSetWriterIds = *((UA_UInt16 *)input[2].data);
1837
1838
0
    UA_UInt16 *writerGroupIds;
1839
0
    UA_UInt16 *dataSetWriterIds;
1840
1841
0
    retVal |= UA_PubSubManager_reserveIds(psm, *sessionId, numRegWriterGroupIds,
1842
0
                                          numRegDataSetWriterIds, transportProfileUri,
1843
0
                                          &writerGroupIds, &dataSetWriterIds);
1844
0
    if(retVal != UA_STATUSCODE_GOOD) {
1845
0
        UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER, "addReserveIds failed");
1846
0
        return retVal;
1847
0
    }
1848
1849
    /* Check the transportProfileUri */
1850
0
    UA_String profile_1 = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-mqtt-uadp");
1851
0
    UA_String profile_2 = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-mqtt-json");
1852
1853
0
    if(UA_String_equal(&transportProfileUri, &profile_1) ||
1854
0
       UA_String_equal(&transportProfileUri, &profile_2)) {
1855
0
        UA_LOG_INFO(server->config.logging, UA_LOGCATEGORY_SERVER, "ApplicationUri: %S",
1856
0
                    server->config.applicationDescription.applicationUri);
1857
0
        retVal |= UA_Variant_setScalarCopy(&output[0],
1858
0
                                           &server->config.applicationDescription.applicationUri,
1859
0
                                           &UA_TYPES[UA_TYPES_STRING]);
1860
0
    } else {
1861
0
        retVal |= UA_Variant_setScalarCopy(&output[0], &psm->defaultPublisherId,
1862
0
                                           &UA_TYPES[UA_TYPES_UINT64]);
1863
0
    }
1864
1865
0
    UA_Variant_setArray(&output[1], writerGroupIds,
1866
0
                        numRegWriterGroupIds, &UA_TYPES[UA_TYPES_UINT16]);
1867
0
    UA_Variant_setArray(&output[2], dataSetWriterIds,
1868
0
                        numRegDataSetWriterIds, &UA_TYPES[UA_TYPES_UINT16]);
1869
1870
0
    return retVal;
1871
0
}
1872
1873
/**********************************************/
1874
/*               ReaderGroup                  */
1875
/**********************************************/
1876
1877
UA_StatusCode
1878
0
addReaderGroupRepresentation(UA_Server *server, UA_ReaderGroup *readerGroup) {
1879
0
    UA_LOCK_ASSERT(&server->serviceMutex);
1880
0
    if(readerGroup->config.name.length > 512)
1881
0
        return UA_STATUSCODE_BADCONFIGURATIONERROR;
1882
0
    char rgName[513];
1883
0
    memcpy(rgName, readerGroup->config.name.data, readerGroup->config.name.length);
1884
0
    rgName[readerGroup->config.name.length] = '\0';
1885
1886
0
    UA_ObjectAttributes object_attr = UA_ObjectAttributes_default;
1887
0
    object_attr.displayName = UA_LOCALIZEDTEXT("", rgName);
1888
0
    UA_StatusCode retVal =
1889
0
        addNode(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(1, 0), /* create an id */
1890
0
                readerGroup->linkedConnection->head.identifier,
1891
0
                UA_NS0ID(HASCOMPONENT),
1892
0
                UA_QUALIFIEDNAME(0, rgName), UA_NS0ID(READERGROUPTYPE),
1893
0
                &object_attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],
1894
0
                NULL, &readerGroup->head.identifier);
1895
1896
0
    UA_NodeId statusIdNode = 
1897
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Status"),
1898
0
                            UA_NS0ID(HASCOMPONENT), readerGroup->head.identifier);
1899
1900
0
    if(UA_NodeId_isNull(&statusIdNode))
1901
0
        return UA_STATUSCODE_BADNOTFOUND;
1902
1903
0
    UA_NodeId stateIdNode = 
1904
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "State"),
1905
0
                            UA_NS0ID(HASCOMPONENT), statusIdNode);
1906
1907
0
    if(UA_NodeId_isNull(&stateIdNode))
1908
0
        return UA_STATUSCODE_BADNOTFOUND;
1909
1910
0
    UA_DataSource stateDataSource;
1911
0
    stateDataSource.read = pubSubStateVariableDataSourceRead;
1912
0
    stateDataSource.write = NULL;
1913
0
    retVal |= UA_Server_setVariableNode_dataSource(server, stateIdNode, stateDataSource);
1914
1915
0
    if(server->config.pubSubConfig.enableInformationModelMethods) {
1916
0
        retVal |= addRef(server, readerGroup->head.identifier, UA_NS0ID(HASCOMPONENT),
1917
0
                         UA_NS0ID(READERGROUPTYPE_ADDDATASETREADER), true);
1918
0
        retVal |= addRef(server, readerGroup->head.identifier, UA_NS0ID(HASCOMPONENT),
1919
0
                         UA_NS0ID(READERGROUPTYPE_REMOVEDATASETREADER), true);
1920
0
        retVal |= addRef(server, statusIdNode, UA_NS0ID(HASCOMPONENT), 
1921
0
                        UA_NS0ID(PUBSUBSTATUSTYPE_ENABLE), true);
1922
0
        retVal |= addRef(server, statusIdNode, UA_NS0ID(HASCOMPONENT), 
1923
0
                        UA_NS0ID(PUBSUBSTATUSTYPE_DISABLE), true);
1924
0
    }
1925
0
    return retVal;
1926
0
}
1927
1928
static UA_StatusCode
1929
addReaderGroupAction(UA_Server *server,
1930
                     const UA_NodeId *sessionId, void *sessionContext,
1931
                     const UA_NodeId *methodId, void *methodContext,
1932
                     const UA_NodeId *objectId, void *objectContext,
1933
                     size_t inputSize, const UA_Variant *input,
1934
0
                     size_t outputSize, UA_Variant *output) {
1935
0
    UA_LOCK_ASSERT(&server->serviceMutex);
1936
1937
0
    UA_StatusCode retVal = UA_STATUSCODE_GOOD;
1938
0
    UA_ReaderGroupDataType *readerGroup = ((UA_ReaderGroupDataType *) input->data);
1939
0
    UA_NodeId readerGroupId;
1940
0
    retVal |= addReaderGroupConfig(server, *objectId, readerGroup, &readerGroupId);
1941
0
    if(retVal != UA_STATUSCODE_GOOD) {
1942
0
        UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER, "addReaderGroup failed");
1943
0
        return retVal;
1944
0
    }
1945
    // TODO: Need to handle the UA_Server_setReaderGroupOperational based on the
1946
    // status variable in information model
1947
1948
0
    UA_Variant_setScalarCopy(output, &readerGroupId, &UA_TYPES[UA_TYPES_NODEID]);
1949
0
    return retVal;
1950
0
}
1951
1952
/**********************************************/
1953
/*               DataSetWriter                */
1954
/**********************************************/
1955
1956
UA_StatusCode
1957
0
addDataSetWriterRepresentation(UA_Server *server, UA_DataSetWriter *dataSetWriter) {
1958
1959
0
    UA_LOCK_ASSERT(&server->serviceMutex);
1960
1961
0
    UA_StatusCode retVal = UA_STATUSCODE_GOOD;
1962
0
    if(dataSetWriter->config.name.length > 512)
1963
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
1964
1965
0
    char dswName[513];
1966
0
    memcpy(dswName, dataSetWriter->config.name.data, dataSetWriter->config.name.length);
1967
0
    dswName[dataSetWriter->config.name.length] = '\0';
1968
1969
0
    UA_ObjectAttributes object_attr = UA_ObjectAttributes_default;
1970
0
    object_attr.displayName = UA_LOCALIZEDTEXT("", dswName);
1971
0
    retVal =
1972
0
        addNode(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(1, 0), /* create an id */
1973
0
                dataSetWriter->linkedWriterGroup->head.identifier, UA_NS0ID(HASDATASETWRITER),
1974
0
                UA_QUALIFIEDNAME(0, dswName), UA_NS0ID(DATASETWRITERTYPE), &object_attr,
1975
0
                &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],
1976
0
                     NULL, &dataSetWriter->head.identifier);
1977
    //if connected dataset is null this means it's configured for heartbeats
1978
0
    if(dataSetWriter->connectedDataSet) {
1979
0
        retVal |= addRef(server, dataSetWriter->connectedDataSet->head.identifier,
1980
0
                         UA_NS0ID(DATASETTOWRITER), dataSetWriter->head.identifier, true);
1981
0
    }
1982
1983
0
    UA_NodeId dataSetWriterIdNode =
1984
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "DataSetWriterId"),
1985
0
                            UA_NS0ID(HASPROPERTY), dataSetWriter->head.identifier);
1986
0
    UA_NodeId keyFrameNode =
1987
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "KeyFrameCount"),
1988
0
                            UA_NS0ID(HASPROPERTY), dataSetWriter->head.identifier);
1989
0
    UA_NodeId dataSetFieldContentMaskNode =
1990
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "DataSetFieldContentMask"),
1991
0
                            UA_NS0ID(HASPROPERTY), dataSetWriter->head.identifier);
1992
1993
0
    UA_NodeId statusIdNode = 
1994
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Status"),
1995
0
                            UA_NS0ID(HASCOMPONENT), dataSetWriter->head.identifier);
1996
    
1997
0
    if(UA_NodeId_isNull(&statusIdNode))
1998
0
        return UA_STATUSCODE_BADNOTFOUND;
1999
2000
0
    UA_NodeId stateIdNode = 
2001
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "State"),
2002
0
                            UA_NS0ID(HASCOMPONENT), statusIdNode);
2003
2004
    // TODO: The keyFrameNode is NULL here, should be check
2005
    // does not depend on the pubsub changes
2006
0
    if(UA_NodeId_isNull(&dataSetWriterIdNode) ||
2007
0
       UA_NodeId_isNull(&dataSetFieldContentMaskNode) ||
2008
0
       UA_NodeId_isNull(&stateIdNode))
2009
0
            return UA_STATUSCODE_BADNOTFOUND;
2010
2011
0
    UA_NodePropertyContext *dataSetWriterIdContext = (UA_NodePropertyContext *)
2012
0
        UA_malloc(sizeof(UA_NodePropertyContext));
2013
0
    dataSetWriterIdContext->parentNodeId = dataSetWriter->head.identifier;
2014
0
    dataSetWriterIdContext->parentClassifier = UA_NS0ID_DATASETWRITERTYPE;
2015
0
    dataSetWriterIdContext->elementClassiefier = UA_NS0ID_DATASETWRITERTYPE_DATASETWRITERID;
2016
0
    UA_CallbackValueSource valueCallback;
2017
0
    valueCallback.read = ReadCallback;
2018
0
    valueCallback.write = NULL;
2019
0
    retVal |= setVariableValueSource(server, valueCallback,
2020
0
                                     dataSetWriterIdNode, dataSetWriterIdContext);
2021
2022
0
    UA_NodeId stateNodeId = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "State"),
2023
0
                                               UA_NS0ID(HASCOMPONENT), statusIdNode);
2024
0
    if(!UA_NodeId_isNull(&stateNodeId)) {
2025
0
        UA_DataSource stateDataSource;
2026
0
        stateDataSource.read = pubSubStateVariableDataSourceRead;
2027
0
        stateDataSource.write = NULL;
2028
0
        retVal |= UA_Server_setVariableNode_dataSource(server, stateNodeId, stateDataSource);
2029
0
    }
2030
2031
0
    UA_Variant value;
2032
0
    UA_Variant_init(&value);
2033
0
    UA_Variant_setScalar(&value, &dataSetWriter->config.dataSetWriterId,
2034
0
                         &UA_TYPES[UA_TYPES_UINT16]);
2035
0
    writeValueAttribute(server, dataSetWriterIdNode, &value);
2036
2037
0
    UA_Variant_setScalar(&value, &dataSetWriter->config.keyFrameCount,
2038
0
                         &UA_TYPES[UA_TYPES_UINT32]);
2039
0
    writeValueAttribute(server, keyFrameNode, &value);
2040
2041
0
    UA_Variant_setScalar(&value, &dataSetWriter->config.dataSetFieldContentMask,
2042
0
                         &UA_TYPES[UA_TYPES_DATASETFIELDCONTENTMASK]);
2043
0
    writeValueAttribute(server, dataSetFieldContentMaskNode, &value);
2044
2045
0
    object_attr.displayName = UA_LOCALIZEDTEXT("", "MessageSettings");
2046
0
    retVal |= addNode(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(1, 0),
2047
0
                      dataSetWriter->head.identifier, UA_NS0ID(HASCOMPONENT),
2048
0
                      UA_QUALIFIEDNAME(0, "MessageSettings"),
2049
0
                      UA_NS0ID(UADPDATASETWRITERMESSAGETYPE), &object_attr,
2050
0
                      &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],
2051
0
                      NULL, NULL);
2052
0
    if(server->config.pubSubConfig.enableInformationModelMethods) {
2053
0
        retVal |= addRef(server, statusIdNode, UA_NS0ID(HASCOMPONENT), 
2054
0
                        UA_NS0ID(PUBSUBSTATUSTYPE_ENABLE), true);
2055
0
        retVal |= addRef(server, statusIdNode, UA_NS0ID(HASCOMPONENT), 
2056
0
                        UA_NS0ID(PUBSUBSTATUSTYPE_DISABLE), true);
2057
0
    }
2058
2059
0
    return retVal;
2060
0
}
2061
2062
static UA_StatusCode
2063
addDataSetWriterAction(UA_Server *server,
2064
                       const UA_NodeId *sessionId, void *sessionContext,
2065
                       const UA_NodeId *methodId, void *methodContext,
2066
                       const UA_NodeId *objectId, void *objectContext,
2067
                       size_t inputSize, const UA_Variant *input,
2068
0
                       size_t outputSize, UA_Variant *output) {
2069
0
    UA_LOCK_ASSERT(&server->serviceMutex);
2070
2071
0
    UA_NodeId dataSetWriterId;
2072
0
    UA_DataSetWriterDataType *dataSetWriterData = (UA_DataSetWriterDataType *)input->data;
2073
0
    UA_StatusCode retVal =
2074
0
        addDataSetWriterConfig(server, objectId, dataSetWriterData, &dataSetWriterId);
2075
0
    if(retVal != UA_STATUSCODE_GOOD) {
2076
0
        UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER,
2077
0
                     "addDataSetWriter failed");
2078
0
        return retVal;
2079
0
    }
2080
2081
0
    UA_Variant_setScalarCopy(output, &dataSetWriterId, &UA_TYPES[UA_TYPES_NODEID]);
2082
0
    return UA_STATUSCODE_GOOD;
2083
0
}
2084
2085
static UA_StatusCode
2086
removeDataSetWriterAction(UA_Server *server,
2087
                          const UA_NodeId *sessionId, void *sessionContext,
2088
                          const UA_NodeId *methodId, void *methodContext,
2089
                          const UA_NodeId *objectId, void *objectContext,
2090
                          size_t inputSize, const UA_Variant *input,
2091
0
                          size_t outputSize, UA_Variant *output){
2092
0
    UA_NodeId nodeToRemove = *((UA_NodeId *) input[0].data);
2093
0
    return UA_Server_removeDataSetWriter(server, nodeToRemove);
2094
0
}
2095
2096
/**********************************************/
2097
/*                Destructors                 */
2098
/**********************************************/
2099
2100
static void
2101
connectionTypeDestructor(UA_Server *server,
2102
                         const UA_NodeId *sessionId, void *sessionContext,
2103
                         const UA_NodeId *typeId, void *typeContext,
2104
0
                         const UA_NodeId *nodeId, void **nodeContext) {
2105
0
    UA_LOCK_ASSERT(&server->serviceMutex);
2106
0
    UA_LOG_INFO(server->config.logging, UA_LOGCATEGORY_PUBSUB,
2107
0
                "Connection destructor called!");
2108
0
    UA_NodeId publisherIdNode =
2109
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublisherId"),
2110
0
                            UA_NS0ID(HASPROPERTY), *nodeId);
2111
0
    UA_NodePropertyContext *ctx;
2112
0
    getNodeContext(server, publisherIdNode, (void **)&ctx);
2113
0
    if(!UA_NodeId_isNull(&publisherIdNode))
2114
0
        UA_free(ctx);
2115
0
}
2116
2117
static void
2118
writerGroupTypeDestructor(UA_Server *server,
2119
                          const UA_NodeId *sessionId, void *sessionContext,
2120
                          const UA_NodeId *typeId, void *typeContext,
2121
0
                          const UA_NodeId *nodeId, void **nodeContext) {
2122
0
    UA_LOCK_ASSERT(&server->serviceMutex);
2123
0
    UA_LOG_INFO(server->config.logging, UA_LOGCATEGORY_PUBSUB,
2124
0
                "WriterGroup destructor called!");
2125
0
    UA_NodeId intervalNode =
2126
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublishingInterval"),
2127
0
                            UA_NS0ID(HASPROPERTY), *nodeId);
2128
2129
0
    UA_NodePropertyContext *ctx;
2130
0
    getNodeContext(server, intervalNode, (void **)&ctx);
2131
0
    if(!UA_NodeId_isNull(&intervalNode))
2132
0
        UA_free(ctx);
2133
0
}
2134
2135
static void
2136
readerGroupTypeDestructor(UA_Server *server,
2137
                          const UA_NodeId *sessionId, void *sessionContext,
2138
                          const UA_NodeId *typeId, void *typeContext,
2139
0
                          const UA_NodeId *nodeId, void **nodeContext) {
2140
0
    UA_LOCK_ASSERT(&server->serviceMutex);
2141
0
}
2142
2143
static void
2144
dataSetWriterTypeDestructor(UA_Server *server,
2145
                            const UA_NodeId *sessionId, void *sessionContext,
2146
                            const UA_NodeId *typeId, void *typeContext,
2147
0
                            const UA_NodeId *nodeId, void **nodeContext) {
2148
0
    UA_LOCK_ASSERT(&server->serviceMutex);
2149
0
    UA_LOG_INFO(server->config.logging, UA_LOGCATEGORY_PUBSUB,
2150
0
                "DataSetWriter destructor called!");
2151
2152
0
    UA_NodeId dataSetWriterIdNode =
2153
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "DataSetWriterId"),
2154
0
                            UA_NS0ID(HASPROPERTY), *nodeId);
2155
2156
0
    UA_NodePropertyContext *ctx;
2157
0
    getNodeContext(server, dataSetWriterIdNode, (void **)&ctx);
2158
0
    if(!UA_NodeId_isNull(&dataSetWriterIdNode))
2159
0
        UA_free(ctx);
2160
0
}
2161
2162
static void
2163
dataSetReaderTypeDestructor(UA_Server *server,
2164
                            const UA_NodeId *sessionId, void *sessionContext,
2165
                            const UA_NodeId *typeId, void *typeContext,
2166
0
                            const UA_NodeId *nodeId, void **nodeContext) {
2167
0
    UA_LOCK_ASSERT(&server->serviceMutex);
2168
0
    UA_LOG_INFO(server->config.logging, UA_LOGCATEGORY_PUBSUB,
2169
0
                "DataSetReader destructor called!");
2170
0
    UA_NodeId publisherIdNode =
2171
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublisherId"),
2172
0
                            UA_NS0ID(HASPROPERTY), *nodeId);
2173
2174
0
    UA_NodePropertyContext *ctx;
2175
0
    getNodeContext(server, publisherIdNode, (void **)&ctx);
2176
0
    if(!UA_NodeId_isNull(&publisherIdNode))
2177
0
        UA_free(ctx);
2178
0
}
2179
2180
static void
2181
publishedDataItemsTypeDestructor(UA_Server *server,
2182
                                 const UA_NodeId *sessionId, void *sessionContext,
2183
                                 const UA_NodeId *typeId, void *typeContext,
2184
0
                                 const UA_NodeId *nodeId, void **nodeContext) {
2185
0
    UA_LOCK_ASSERT(&server->serviceMutex);
2186
0
    UA_LOG_INFO(server->config.logging, UA_LOGCATEGORY_PUBSUB,
2187
0
                "PublishedDataItems destructor called!");
2188
0
    void *childContext;
2189
0
    UA_NodeId node = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublishedData"),
2190
0
                                         UA_NS0ID(HASPROPERTY), *nodeId);
2191
0
    getNodeContext(server, node, (void**)&childContext);
2192
0
    if(!UA_NodeId_isNull(&node))
2193
0
        UA_free(childContext);
2194
2195
0
    node = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "ConfigurationVersion"),
2196
0
                               UA_NS0ID(HASPROPERTY), *nodeId);
2197
0
    getNodeContext(server, node, (void**)&childContext);
2198
0
    if(!UA_NodeId_isNull(&node))
2199
0
        UA_free(childContext);
2200
2201
0
    node = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "DataSetMetaData"),
2202
0
                               UA_NS0ID(HASPROPERTY), *nodeId);
2203
0
    getNodeContext(server, node, (void**)&childContext);
2204
0
    if(!UA_NodeId_isNull(&node))
2205
0
        UA_free(childContext);
2206
0
}
2207
2208
static void
2209
subscribedDataSetTypeDestructor(UA_Server *server,
2210
                                const UA_NodeId *sessionId, void *sessionContext,
2211
                                const UA_NodeId *typeId, void *typeContext,
2212
0
                                const UA_NodeId *nodeId, void **nodeContext) {
2213
0
    UA_LOCK_ASSERT(&server->serviceMutex);
2214
0
    UA_LOG_INFO(server->config.logging, UA_LOGCATEGORY_PUBSUB,
2215
0
                "Standalone SubscribedDataSet destructor called!");
2216
0
    void *childContext;
2217
0
    UA_NodeId node =
2218
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "DataSetMetaData"),
2219
0
                            UA_NS0ID(HASPROPERTY), *nodeId);
2220
0
    getNodeContext(server, node, (void**)&childContext);
2221
0
    if(!UA_NodeId_equal(&UA_NODEID_NULL , &node))
2222
0
        UA_free(childContext);
2223
0
    node = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "IsConnected"),
2224
0
                               UA_NS0ID(HASPROPERTY), *nodeId);
2225
0
    getNodeContext(server, node, (void**)&childContext);
2226
0
    if(!UA_NodeId_equal(&UA_NODEID_NULL , &node))
2227
0
        UA_free(childContext);
2228
0
}
2229
2230
/*************************************/
2231
/*         PubSub configurator       */
2232
/*************************************/
2233
2234
#ifdef UA_ENABLE_PUBSUB_FILE_CONFIG
2235
2236
/* Callback function that will be executed when the method "PubSub configurator
2237
 * (replace config)" is called. */
2238
static UA_StatusCode
2239
UA_loadPubSubConfigMethodCallback(UA_Server *server,
2240
                                  const UA_NodeId *sessionId, void *sessionContext,
2241
                                  const UA_NodeId *methodId, void *methodContext,
2242
                                  const UA_NodeId *objectId, void *objectContext,
2243
                                  size_t inputSize, const UA_Variant *input,
2244
                                  size_t outputSize, UA_Variant *output) {
2245
    UA_LOCK_ASSERT(&server->serviceMutex);
2246
    if(inputSize == 1) {
2247
        UA_ByteString *inputStr = (UA_ByteString*)input->data;
2248
        return UA_Server_loadPubSubConfigFromByteString(server, *inputStr);
2249
    } else if(inputSize > 1) {
2250
        return UA_STATUSCODE_BADTOOMANYARGUMENTS;
2251
    } else {
2252
        return UA_STATUSCODE_BADARGUMENTSMISSING;
2253
    }
2254
}
2255
2256
static void
2257
deletePubSubConfigMethodFinalize(void *application, void *context) {
2258
    UA_PubSubManager *manager = (UA_PubSubManager *) application;
2259
    UA_Server *server = manager->sc.server;
2260
    lockServer(manager->sc.server);
2261
    UA_PubSubManager_clear(manager);
2262
    unlockServer(server);
2263
    UA_free(context);
2264
}
2265
2266
/* Callback function that will be executed when the method "PubSub configurator
2267
 *  (delete config)" is called. */
2268
static UA_StatusCode
2269
UA_deletePubSubConfigMethodCallback(UA_Server *server,
2270
                                    const UA_NodeId *sessionId, void *sessionContext,
2271
                                    const UA_NodeId *methodId, void *methodContext,
2272
                                    const UA_NodeId *objectId, void *objectContext,
2273
                                    size_t inputSize, const UA_Variant *input,
2274
                                    size_t outputSize, UA_Variant *output) {
2275
    UA_LOCK_ASSERT(&server->serviceMutex);
2276
    UA_PubSubManager *psm = getPSM(server);
2277
    if(psm) {
2278
        psm->sc.stop(&psm->sc);
2279
        UA_DelayedCallback *dc = (UA_DelayedCallback*)UA_calloc(1, sizeof(UA_DelayedCallback));
2280
        if(!dc)
2281
            return UA_STATUSCODE_BADOUTOFMEMORY;
2282
        dc->callback = deletePubSubConfigMethodFinalize;
2283
        dc->application = psm;
2284
        dc->context = dc;
2285
        server->config.eventLoop->addDelayedCallback(psm->sc.server->config.eventLoop, dc);
2286
    }
2287
2288
    return UA_STATUSCODE_GOOD;
2289
}
2290
2291
#endif
2292
2293
UA_StatusCode
2294
553
initPubSubNS0(UA_Server *server) {
2295
553
    UA_LOCK_ASSERT(&server->serviceMutex);
2296
2297
553
    UA_StatusCode retVal = UA_STATUSCODE_GOOD;
2298
553
    UA_String profileArray[1];
2299
553
    profileArray[0] = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp");
2300
2301
553
    retVal |= writePubSubNs0VariableArray(server,
2302
553
                                          UA_NS0ID(PUBLISHSUBSCRIBE_SUPPORTEDTRANSPORTPROFILES),
2303
553
                                          profileArray, 1, &UA_TYPES[UA_TYPES_STRING]);
2304
2305
    /* Set read callback for PublishSubscribeType Status State (mandatory) */
2306
553
    UA_CallbackValueSource statusCallback;
2307
553
    statusCallback.read = pubSubStateVariableDataSourceRead;
2308
553
    statusCallback.write = NULL;
2309
553
    retVal |= setVariableValueSource(server, statusCallback, 
2310
553
                                    UA_NS0ID(PUBLISHSUBSCRIBE_STATUS_STATE), NULL);
2311
2312
553
    if(server->config.pubSubConfig.enableInformationModelMethods) {
2313
        /* Add missing references */
2314
553
        retVal |= addRef(server, UA_NS0ID(PUBLISHSUBSCRIBE_PUBLISHEDDATASETS),
2315
553
                         UA_NS0ID(HASCOMPONENT), UA_NS0ID(DATASETFOLDERTYPE_ADDDATASETFOLDER), true);
2316
553
        retVal |= addRef(server, UA_NS0ID(PUBLISHSUBSCRIBE_PUBLISHEDDATASETS),
2317
553
                         UA_NS0ID(HASCOMPONENT), UA_NS0ID(DATASETFOLDERTYPE_ADDPUBLISHEDDATAITEMS), true);
2318
553
        retVal |= addRef(server, UA_NS0ID(PUBLISHSUBSCRIBE_PUBLISHEDDATASETS),
2319
553
                         UA_NS0ID(HASCOMPONENT), UA_NS0ID(DATASETFOLDERTYPE_REMOVEPUBLISHEDDATASET), true);
2320
553
        retVal |= addRef(server, UA_NS0ID(PUBLISHSUBSCRIBE_PUBLISHEDDATASETS),
2321
553
                         UA_NS0ID(HASCOMPONENT), UA_NS0ID(DATASETFOLDERTYPE_REMOVEDATASETFOLDER), true);
2322
553
        retVal |= addRef(server, UA_NS0ID(PUBLISHSUBSCRIBE_STATUS),
2323
553
                         UA_NS0ID(HASCOMPONENT), UA_NS0ID(PUBSUBSTATUSTYPE_ENABLE), true);
2324
553
        retVal |= addRef(server, UA_NS0ID(PUBLISHSUBSCRIBE_STATUS),
2325
553
                         UA_NS0ID(HASCOMPONENT), UA_NS0ID(PUBSUBSTATUSTYPE_DISABLE), true);
2326
2327
        /* Set method callbacks */
2328
553
        retVal |= setMethodNode_callback(server, UA_NS0ID(PUBLISHSUBSCRIBE_ADDCONNECTION), addPubSubConnectionAction);
2329
553
        retVal |= setMethodNode_callback(server, UA_NS0ID(PUBLISHSUBSCRIBE_REMOVECONNECTION), removeConnectionAction);
2330
553
        retVal |= setMethodNode_callback(server, UA_NS0ID(DATASETFOLDERTYPE_ADDDATASETFOLDER), addDataSetFolderAction);
2331
553
        retVal |= setMethodNode_callback(server, UA_NS0ID(DATASETFOLDERTYPE_REMOVEDATASETFOLDER), removeDataSetFolderAction);
2332
553
        retVal |= setMethodNode_callback(server, UA_NS0ID(DATASETFOLDERTYPE_ADDPUBLISHEDDATAITEMS), addPublishedDataItemsAction);
2333
553
        retVal |= setMethodNode_callback(server, UA_NS0ID(DATASETFOLDERTYPE_REMOVEPUBLISHEDDATASET), removePublishedDataSetAction);
2334
553
        retVal |= setMethodNode_callback(server, UA_NS0ID(PUBLISHEDDATAITEMSTYPE_ADDVARIABLES), addVariablesAction);
2335
553
        retVal |= setMethodNode_callback(server, UA_NS0ID(PUBLISHEDDATAITEMSTYPE_REMOVEVARIABLES), removeVariablesAction);
2336
553
        retVal |= setMethodNode_callback(server, UA_NS0ID(PUBSUBCONNECTIONTYPE_ADDWRITERGROUP), addWriterGroupAction);
2337
553
        retVal |= setMethodNode_callback(server, UA_NS0ID(PUBSUBCONNECTIONTYPE_ADDREADERGROUP), addReaderGroupAction);
2338
553
        retVal |= setMethodNode_callback(server, UA_NS0ID(PUBSUBCONNECTIONTYPE_REMOVEGROUP), removeGroupAction);
2339
553
        retVal |= setMethodNode_callback(server, UA_NS0ID(WRITERGROUPTYPE_ADDDATASETWRITER), addDataSetWriterAction);
2340
553
        retVal |= setMethodNode_callback(server, UA_NS0ID(WRITERGROUPTYPE_REMOVEDATASETWRITER), removeDataSetWriterAction);
2341
553
        retVal |= setMethodNode_callback(server, UA_NS0ID(READERGROUPTYPE_ADDDATASETREADER), addDataSetReaderAction);
2342
553
        retVal |= setMethodNode_callback(server, UA_NS0ID(READERGROUPTYPE_REMOVEDATASETREADER), removeDataSetReaderAction);
2343
553
        retVal |= setMethodNode_callback(server, UA_NS0ID(PUBLISHSUBSCRIBE_PUBSUBCONFIGURATION_RESERVEIDS), addReserveIdsAction);
2344
553
        retVal |= setMethodNode_callback(server, UA_NS0ID(PUBSUBSTATUSTYPE_ENABLE), enablePubSubObjectAction);
2345
553
        retVal |= setMethodNode_callback(server, UA_NS0ID(PUBSUBSTATUSTYPE_DISABLE), disablePubSubObjectAction);
2346
2347
#ifdef UA_ENABLE_PUBSUB_FILE_CONFIG
2348
        /* Adds method node to server. This method is used to load binary files for
2349
         * PubSub configuration and delete / replace old PubSub configurations. */
2350
        UA_Argument inputArgument;
2351
        UA_Argument_init(&inputArgument);
2352
        inputArgument.description = UA_LOCALIZEDTEXT("", "PubSub config binfile");
2353
        inputArgument.name = UA_STRING("BinFile");
2354
        inputArgument.dataType = UA_TYPES[UA_TYPES_BYTESTRING].typeId;
2355
        inputArgument.valueRank = UA_VALUERANK_SCALAR;
2356
2357
        UA_MethodAttributes configAttr = UA_MethodAttributes_default;
2358
        configAttr.description = UA_LOCALIZEDTEXT("","Load binary configuration file");
2359
        configAttr.displayName = UA_LOCALIZEDTEXT("","LoadPubSubConfigurationFile");
2360
        configAttr.executable = true;
2361
        configAttr.userExecutable = true;
2362
        retVal |= addMethodNode(server, UA_NODEID_NULL,
2363
                                UA_NS0ID(PUBLISHSUBSCRIBE), UA_NS0ID(HASORDEREDCOMPONENT),
2364
                                UA_QUALIFIEDNAME(1, "PubSub configuration"),
2365
                                &configAttr, UA_loadPubSubConfigMethodCallback,
2366
                                1, &inputArgument, UA_NODEID_NULL, NULL,
2367
                                0, NULL, UA_NODEID_NULL, NULL,
2368
                                NULL, NULL);
2369
2370
        /* Adds method node to server. This method is used to delete the current
2371
         * PubSub configuration. */
2372
        configAttr.description = UA_LOCALIZEDTEXT("","Delete current PubSub configuration");
2373
        configAttr.displayName = UA_LOCALIZEDTEXT("","DeletePubSubConfiguration");
2374
        configAttr.executable = true;
2375
        configAttr.userExecutable = true;
2376
        retVal |= addMethodNode(server, UA_NODEID_NULL,
2377
                                UA_NS0ID(PUBLISHSUBSCRIBE), UA_NS0ID(HASORDEREDCOMPONENT),
2378
                                UA_QUALIFIEDNAME(1, "Delete PubSub config"),
2379
                                &configAttr, UA_deletePubSubConfigMethodCallback,
2380
                                0, NULL, UA_NODEID_NULL, NULL,
2381
                                0, NULL, UA_NODEID_NULL, NULL,
2382
                                NULL, NULL);
2383
#endif
2384
553
    } else {
2385
        /* Remove methods */
2386
0
        retVal |= deleteReference(server, UA_NS0ID(PUBLISHSUBSCRIBE),
2387
0
                                  UA_NS0ID(HASCOMPONENT), true,
2388
0
                                  UA_NS0EXID(PUBLISHSUBSCRIBE_ADDCONNECTION), false);
2389
0
        retVal |= deleteReference(server, UA_NS0ID(PUBLISHSUBSCRIBE),
2390
0
                                  UA_NS0ID(HASCOMPONENT), true,
2391
0
                                  UA_NS0EXID(PUBLISHSUBSCRIBE_REMOVECONNECTION), false);
2392
0
    }
2393
2394
    /* Set the object-type destructors */
2395
553
    UA_NodeTypeLifecycle lifeCycle;
2396
553
    lifeCycle.constructor = NULL;
2397
2398
553
    lifeCycle.destructor = connectionTypeDestructor;
2399
553
    retVal |= setNodeTypeLifecycle(server, UA_NS0ID(PUBSUBCONNECTIONTYPE), lifeCycle);
2400
2401
553
    lifeCycle.destructor = writerGroupTypeDestructor;
2402
553
    retVal |= setNodeTypeLifecycle(server, UA_NS0ID(WRITERGROUPTYPE), lifeCycle);
2403
2404
553
    lifeCycle.destructor = readerGroupTypeDestructor;
2405
553
    retVal |= setNodeTypeLifecycle(server, UA_NS0ID(READERGROUPTYPE), lifeCycle);
2406
2407
553
    lifeCycle.destructor = dataSetWriterTypeDestructor;
2408
553
    retVal |= setNodeTypeLifecycle(server, UA_NS0ID(DATASETWRITERTYPE), lifeCycle);
2409
2410
553
    lifeCycle.destructor = publishedDataItemsTypeDestructor;
2411
553
    retVal |= setNodeTypeLifecycle(server, UA_NS0ID(PUBLISHEDDATAITEMSTYPE), lifeCycle);
2412
2413
553
    lifeCycle.destructor = dataSetReaderTypeDestructor;
2414
553
    retVal |= setNodeTypeLifecycle(server, UA_NS0ID(DATASETREADERTYPE), lifeCycle);
2415
2416
553
    lifeCycle.destructor = subscribedDataSetTypeDestructor;
2417
553
    retVal |= setNodeTypeLifecycle(server, UA_NS0ID(STANDALONESUBSCRIBEDDATASETTYPE), lifeCycle);
2418
2419
#ifdef UA_ENABLE_PUBSUB_SKS
2420
    /* Initialize SKS-specific functionality */
2421
    retVal |= initPubSubNS0_SKS(server);
2422
#endif
2423
2424
553
    return retVal;
2425
553
}
2426
2427
/* Remove the Metadata nodes of the DSR and reference the Metadata nodes of the
2428
 * SDS instead */
2429
UA_StatusCode
2430
0
connectDataSetReaderToDataSet(UA_Server *server, UA_NodeId dsrId, UA_NodeId sdsId) {
2431
0
    UA_LOCK_ASSERT(&server->serviceMutex);
2432
2433
0
    UA_NodeId dataSetMetaDataOnSdsId =
2434
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "DataSetMetaData"),
2435
0
                            UA_NS0ID(HASPROPERTY), sdsId);
2436
0
    UA_NodeId subscribedDataSetOnSdsId =
2437
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "SubscribedDataSet"),
2438
0
                            UA_NS0ID(HASCOMPONENT), sdsId);
2439
2440
0
    if(UA_NodeId_isNull(&dataSetMetaDataOnSdsId) ||
2441
0
       UA_NodeId_isNull(&subscribedDataSetOnSdsId))
2442
0
        return UA_STATUSCODE_BADNOTFOUND;
2443
2444
0
    UA_NodeId dataSetMetaDataOnDsrId =
2445
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "DataSetMetaData"),
2446
0
                            UA_NS0ID(HASPROPERTY), dsrId);
2447
0
    UA_NodeId subscribedDataSetOnDsrId =
2448
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "SubscribedDataSet"),
2449
0
                            UA_NS0ID(HASCOMPONENT), dsrId);
2450
2451
0
    UA_NODESTORE_REMOVE(server, &dataSetMetaDataOnDsrId);
2452
0
    UA_NODESTORE_REMOVE(server, &subscribedDataSetOnDsrId);
2453
2454
0
    UA_StatusCode retVal = UA_STATUSCODE_GOOD;
2455
0
    retVal |= addRef(server, dsrId, UA_NS0ID(HASPROPERTY),
2456
0
                     dataSetMetaDataOnSdsId, true);
2457
0
    retVal |= addRef(server, dsrId, UA_NS0ID(HASPROPERTY),
2458
0
                     subscribedDataSetOnSdsId, true);
2459
0
    return retVal;
2460
0
}
2461
2462
/* Remove the references to the SDS */
2463
void
2464
0
disconnectDataSetReaderToDataSet(UA_Server *server, UA_NodeId dsrId) {
2465
0
    UA_LOCK_ASSERT(&server->serviceMutex);
2466
2467
0
    UA_NodeId dataSetMetaDataOnDsrId =
2468
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "DataSetMetaData"),
2469
0
                            UA_NS0ID(HASPROPERTY), dsrId);
2470
0
    UA_NodeId subscribedDataSetOnDsrId =
2471
0
        findSingleChildNode(server, UA_QUALIFIEDNAME(0, "SubscribedDataSet"),
2472
0
                            UA_NS0ID(HASCOMPONENT), dsrId);
2473
2474
0
    deleteReference(server, dsrId, UA_NS0ID(HASPROPERTY), true,
2475
0
                    UA_NODEID2EXPANDEDNODEID(dataSetMetaDataOnDsrId), true);
2476
2477
0
    deleteReference(server, dsrId, UA_NS0ID(HASCOMPONENT), true,
2478
0
                    UA_NODEID2EXPANDEDNODEID(subscribedDataSetOnDsrId), true);
2479
0
}
2480
2481
#endif /* UA_ENABLE_PUBSUB_INFORMATIONMODEL */