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