Coverage Report

Created: 2026-11-12 06:54

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