Browse Source

feat(pubsub): added pubsub configuration lock and pubsub state machine skeleton

Andreas Ebner 5 years ago
parent
commit
0f16425702

+ 1 - 0
examples/CMakeLists.txt

@@ -193,6 +193,7 @@ if(UA_ENABLE_PUBSUB)
     add_example(tutorial_pubsub_connection pubsub/tutorial_pubsub_connection.c)
     add_example(tutorial_pubsub_publish pubsub/tutorial_pubsub_publish.c)
 	add_example(server_pubsub_publisher_iop pubsub/server_pubsub_publisher_iop.c)
+	add_example(server_pubsub_publish_rt_level pubsub/server_pubsub_publisher_rt_level.c)
     if(UA_ENABLE_AMALGAMATION)
         message(WARNING "PubSub subscriber tutorial (preview) can not be used with AMALGAMATION. Skipping tutorial_pubsub_subscribe.")
     else(NOT UA_ENABLE_AMALGAMATION)

+ 1 - 0
examples/pubsub/server_pubsub_publisher_iop.c

@@ -796,6 +796,7 @@ static int run(UA_String *transportProfile,
     /* Add the new WriterGroup to an existing Connection. */
     UA_NodeId writerGroupIdent;
     UA_Server_addWriterGroup(server, connectionIdent, &writerGroupConfig, &writerGroupIdent);
+    UA_Server_setWriterGroupOperational(server, writerGroupIdent);
 
     /* Create a new Writer and connect it with an existing PublishedDataSet */
     // DataSetWriter ID 1 with Variant Encoding 

+ 127 - 0
examples/pubsub/server_pubsub_publisher_rt_level.c

@@ -0,0 +1,127 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+#include <open62541/plugin/log_stdout.h>
+#include <open62541/plugin/pubsub_udp.h>
+#include <open62541/server.h>
+#include <open62541/server_config_default.h>
+
+#include <signal.h>
+#include <stdlib.h>
+#include <open62541/server_pubsub.h>
+
+UA_NodeId publishedDataSetIdent, dataSetFieldIdent, writerGroupIdent, connectionIdentifier;
+
+UA_Boolean running = true;
+static void stopHandler(int sign) {
+    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
+    running = false;
+}
+
+/**
+ * The PubSub RT level example points out the configuration of different PubSub RT levels. These levels will be later
+ * used for deterministic message generation. The underlying base concept is the target to reduce the time spread and
+ * affort during the publish cycle. Most of the RT levels are based on a pregenerated and buffered DataSetMesseges and
+ * NetworkMessages. Since changes in the PubSub configuration will invalidate the buffered frames, the pubsub
+ * configuration can be frozen after the configuration phase
+ */
+
+/* The following PubSub configuration does not differ from the 'normal' configuration */
+static void
+addMinimalPubSubConfiguration(UA_Server * server){
+    /* Add one PubSubConnection */
+    UA_PubSubConnectionConfig connectionConfig;
+    memset(&connectionConfig, 0, sizeof(connectionConfig));
+    connectionConfig.name = UA_STRING("UDP-UADP Connection 1");
+    connectionConfig.transportProfileUri = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp");
+    connectionConfig.enabled = UA_TRUE;
+    UA_NetworkAddressUrlDataType networkAddressUrl = {UA_STRING_NULL , UA_STRING("opc.udp://224.0.0.22:4840/")};
+    UA_Variant_setScalar(&connectionConfig.address, &networkAddressUrl, &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]);
+    connectionConfig.publisherId.numeric = UA_UInt32_random();
+    UA_Server_addPubSubConnection(server, &connectionConfig, &connectionIdentifier);
+    /* Add one PublishedDataSet */
+    UA_PublishedDataSetConfig publishedDataSetConfig;
+    memset(&publishedDataSetConfig, 0, sizeof(UA_PublishedDataSetConfig));
+    publishedDataSetConfig.publishedDataSetType = UA_PUBSUB_DATASET_PUBLISHEDITEMS;
+    publishedDataSetConfig.name = UA_STRING("Demo PDS");
+    /* Add one DataSetField to the PDS */
+    UA_Server_addPublishedDataSet(server, &publishedDataSetConfig, &publishedDataSetIdent);
+}
+
+int main(void) {
+    signal(SIGINT, stopHandler);
+    signal(SIGTERM, stopHandler);
+
+    UA_Server *server = UA_Server_new();
+    UA_ServerConfig *config = UA_Server_getConfig(server);
+    UA_ServerConfig_setDefault(config);
+
+    config->pubsubTransportLayers = (UA_PubSubTransportLayer *) UA_malloc(sizeof(UA_PubSubTransportLayer));
+    if(!config->pubsubTransportLayers) {
+        UA_Server_delete(server);
+        return -1;
+    }
+    config->pubsubTransportLayers[0] = UA_PubSubTransportLayerUDPMP();
+    config->pubsubTransportLayersSize++;
+
+    /*Add standard PubSub configuration (no difference to the std. configuration)*/
+    addMinimalPubSubConfiguration(server);
+
+    /* Add one WriterGroup with PubSub RT Level 0. If any rtLevel != UA_PUBSUB_RT_NONE is set, the
+     * writerGroup does not start the publishing interval automatically.*/
+    UA_WriterGroupConfig writerGroupConfig;
+    memset(&writerGroupConfig, 0, sizeof(UA_WriterGroupConfig));
+    writerGroupConfig.name = UA_STRING("Demo WriterGroup");
+    writerGroupConfig.publishingInterval = 100;
+    writerGroupConfig.enabled = UA_FALSE;
+    writerGroupConfig.writerGroupId = 100;
+    writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP;
+    /* RT Level 0 setup */
+    writerGroupConfig.rtLevel = UA_PUBSUB_RT_FIXED_SIZE;
+    UA_Server_addWriterGroup(server, connectionIdentifier, &writerGroupConfig, &writerGroupIdent);
+    /* Add one DataSetWriter */
+    UA_NodeId dataSetWriterIdent;
+    UA_DataSetWriterConfig dataSetWriterConfig;
+    memset(&dataSetWriterConfig, 0, sizeof(UA_DataSetWriterConfig));
+    dataSetWriterConfig.name = UA_STRING("Demo DataSetWriter");
+    dataSetWriterConfig.dataSetWriterId = 62541;
+    dataSetWriterConfig.keyFrameCount = 10;
+    UA_Server_addDataSetWriter(server, writerGroupIdent, publishedDataSetIdent, &dataSetWriterConfig, &dataSetWriterIdent);
+    /* Add one DataSetField with static value source to PDS */
+    UA_DataSetFieldConfig dsfConfig;
+    UA_Server_getDataSetFieldConfig(server, dataSetFieldIdent, &dsfConfig);
+    /* Create Variant and configure as DataSetField source */
+    UA_UInt32 *intValue = UA_UInt32_new();
+    UA_Variant variant;
+    memset(&variant, 0, sizeof(UA_Variant));
+    UA_Variant_setScalar(&variant, intValue, &UA_TYPES[UA_TYPES_UINT32]);
+    UA_DataValue staticValueSource;
+    memset(&staticValueSource, 0, sizeof(staticValueSource));
+    staticValueSource.value = variant;
+    dsfConfig.field.variable.staticValueSourceEnabled = UA_TRUE;
+    dsfConfig.field.variable.staticValueSource.value = variant;
+    UA_Server_addDataSetField(server, publishedDataSetIdent, &dsfConfig, &dataSetFieldIdent);
+
+    /* The PubSub configuration is currently editable and the publish callback is not running */
+    writerGroupConfig.publishingInterval = 1000;
+    UA_Server_updateWriterGroupConfig(server, writerGroupIdent, &writerGroupConfig);
+
+    /* Freeze the PubSub configuration (and start implicitly the publish callback) */
+    UA_Server_freezeWriterGroupConfiguration(server, writerGroupIdent);
+    UA_Server_setWriterGroupOperational(server, writerGroupIdent);
+
+    /* Changes of the PubSub configuration is restricted after freeze */
+    UA_StatusCode retVal = UA_Server_updateWriterGroupConfig(server, writerGroupIdent, &writerGroupConfig);
+    if(retVal != UA_STATUSCODE_BADCONFIGURATIONERROR)
+        return EXIT_FAILURE;
+
+    /* Unfreeze the PubSub configuration (and stop implicitly the publish callback) */
+    UA_Server_setWriterGroupDisabled(server, writerGroupIdent);
+    UA_Server_unfreezeWriterGroupConfiguration(server, writerGroupIdent);
+
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    retval |= UA_Server_run(server, &running);
+
+    UA_Server_delete(server);
+    return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
+}

+ 1 - 0
examples/pubsub/tutorial_pubsub_publish.c

@@ -124,6 +124,7 @@ addWriterGroup(UA_Server *server) {
                                                               (UA_UadpNetworkMessageContentMask)UA_UADPNETWORKMESSAGECONTENTMASK_PAYLOADHEADER);
     writerGroupConfig.messageSettings.content.decoded.data = writerGroupMessage;
     UA_Server_addWriterGroup(server, connectionIdent, &writerGroupConfig, &writerGroupIdent);
+    UA_Server_setWriterGroupOperational(server, writerGroupIdent);
     UA_UadpWriterGroupMessageDataType_delete(writerGroupMessage);
 }
 

+ 65 - 1
include/open62541/server_pubsub.h

@@ -120,6 +120,9 @@ typedef struct {
     size_t connectionPropertiesSize;
     UA_KeyValuePair *connectionProperties;
     UA_Variant connectionTransportSettings;
+
+    /* This flag is 'read only' and is set internally based on the PubSub state. */
+    UA_Boolean configurationFrozen;
 } UA_PubSubConnectionConfig;
 
 UA_StatusCode UA_EXPORT
@@ -188,6 +191,8 @@ typedef struct {
         UA_PublishedEventConfig event;
         UA_PublishedEventTemplateConfig eventTemplate;
     } config;
+    /* This flag is 'read only' and is set internally based on the PubSub state. */
+    UA_Boolean configurationFrozen;
 } UA_PublishedDataSetConfig;
 
 void UA_EXPORT
@@ -229,6 +234,9 @@ typedef struct{
     UA_String fieldNameAlias;
     UA_Boolean promotedField;
     UA_PublishedVariableDataType publishParameters;
+    /* non std. field */
+    UA_Boolean staticValueSourceEnabled;
+    UA_DataValue staticValueSource;
 } UA_DataSetVariableConfig;
 
 typedef enum {
@@ -242,6 +250,8 @@ typedef struct {
         /* events need other config later */
         UA_DataSetVariableConfig variable;
     } field;
+    /* This flag is 'read only' and is set internally based on the PubSub state. */
+    UA_Boolean configurationFrozen;
 } UA_DataSetFieldConfig;
 
 void UA_EXPORT
@@ -282,6 +292,41 @@ typedef enum {
     UA_PUBSUB_ENCODING_UADP
 } UA_PubSubEncodingType;
 
+/**
+ * WriterGroup
+ * -----------
+ * The message publishing can be configured for realtime requirements. The RT-levels
+ * go along with different requirements. The below listed levels can be configured:
+ *
+ * UA_PUBSUB_RT_NONE -
+ * ---> Description: Default "none-RT" Mode
+ * ---> Requirements: -
+ * ---> Restrictions: -
+ * UA_PUBSUB_RT_DIRECT_VALUE_ACCESS (Preview - not implemented)
+ * ---> Description: Normally, the latest value for each DataSetField is read out of the information model. Within this RT-mode, the
+ * value source of each field configured as static pointer to an DataValue. The publish cycle won't use call the server read function.
+ * ---> Requirements: All fields must be configured with a 'staticValueSource'.
+ * ---> Restrictions: -
+ * UA_PUBSUB_RT_FIXED_LENGTH (Preview - not implemented)
+ * ---> Description: All DataSetFields have a known, non-changing length. The server will pre-generate some
+ * buffers and use only memcopy operations to generate requested PubSub packages.
+ * ---> Requirements: DataSetFields with variable size can't be used within this mode.
+ * ---> Restrictions: The configuration must be frozen and changes are not allowed while the WriterGroup is 'Operational'.
+ * UA_PUBSUB_RT_DETERMINISTIC (Preview - not implemented)
+ * ---> Description: -
+ * ---> Requirements: -
+ * ---> Restrictions: -
+ *
+ * WARNING! For hard real time requirements the underlying system must be rt-capable.
+ *
+ */
+typedef enum {
+    UA_PUBSUB_RT_NONE = 0,
+    UA_PUBSUB_RT_DIRECT_VALUE_ACCESS = 1,
+    UA_PUBSUB_RT_FIXED_SIZE = 2,
+    UA_PUBSUB_RT_DETERMINISTIC = 4,
+} UA_PubSubRTLevel;
+
 typedef struct {
     UA_String name;
     UA_Boolean enabled;
@@ -299,6 +344,10 @@ typedef struct {
     /* non std. config parameter. maximum count of embedded DataSetMessage in
      * one NetworkMessage */
     UA_UInt16 maxEncapsulatedDataSetMessageCount;
+    /* This flag is 'read only' and is set internally based on the PubSub state. */
+    UA_Boolean configurationFrozen;
+    /* non std. field */
+    UA_PubSubRTLevel rtLevel;
 } UA_WriterGroupConfig;
 
 void UA_EXPORT
@@ -322,8 +371,21 @@ UA_Server_updateWriterGroupConfig(UA_Server *server, UA_NodeId writerGroupIdenti
 UA_StatusCode UA_EXPORT
 UA_Server_removeWriterGroup(UA_Server *server, const UA_NodeId writerGroup);
 
+UA_StatusCode UA_EXPORT
+UA_Server_freezeWriterGroupConfiguration(UA_Server *server, const UA_NodeId writerGroup);
+
+UA_StatusCode UA_EXPORT
+UA_Server_unfreezeWriterGroupConfiguration(UA_Server *server, const UA_NodeId writerGroup);
+
+UA_StatusCode UA_EXPORT
+UA_Server_setWriterGroupOperational(UA_Server *server, const UA_NodeId writerGroup);
+
+UA_StatusCode UA_EXPORT
+UA_Server_setWriterGroupDisabled(UA_Server *server, const UA_NodeId writerGroup);
+
 /**
- * .. _dsw:
+ * .. _dsw:    UA_Boolean configurationFrozen;
+
  *
  * DataSetWriter
  * -------------
@@ -342,6 +404,8 @@ typedef struct {
     UA_String dataSetName;
     size_t dataSetWriterPropertiesSize;
     UA_KeyValuePair *dataSetWriterProperties;
+    /* This flag is 'read only' and is set internally based on the PubSub state. */
+    UA_Boolean configurationFrozen;
 } UA_DataSetWriterConfig;
 
 void UA_EXPORT

+ 341 - 14
src/pubsub/ua_pubsub.c

@@ -7,6 +7,7 @@
  * Copyright (c) 2019 Kalycito Infotech Private Limited
  */
 
+#include <open62541/server_pubsub.h>
 #include "server/ua_server_internal.h"
 
 #ifdef UA_ENABLE_PUBSUB /* conditional compilation */
@@ -159,6 +160,12 @@ UA_Server_addWriterGroup(UA_Server *server, const UA_NodeId connection,
     if(!currentConnectionContext)
         return UA_STATUSCODE_BADNOTFOUND;
 
+    if(currentConnectionContext->config->configurationFrozen){
+        UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                       "Adding WriterGroup failed. PubSubConnection is frozen.");
+        return UA_STATUSCODE_BADCONFIGURATIONERROR;
+    }
+
     //allocate memory for new WriterGroup
     UA_WriterGroup *newWriterGroup = (UA_WriterGroup *) UA_calloc(1, sizeof(UA_WriterGroup));
     if(!newWriterGroup)
@@ -169,7 +176,6 @@ UA_Server_addWriterGroup(UA_Server *server, const UA_NodeId connection,
     if(writerGroupIdentifier){
         UA_NodeId_copy(&newWriterGroup->identifier, writerGroupIdentifier);
     }
-
     //deep copy of the config
     UA_WriterGroupConfig tmpWriterGroupConfig;
     retVal |= UA_WriterGroupConfig_copy(writerGroupConfig, &tmpWriterGroupConfig);
@@ -181,9 +187,7 @@ UA_Server_addWriterGroup(UA_Server *server, const UA_NodeId connection,
             &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE];
         tmpWriterGroupConfig.messageSettings.encoding = UA_EXTENSIONOBJECT_DECODED;
     }
-
     newWriterGroup->config = tmpWriterGroupConfig;
-    retVal |= UA_WriterGroup_addPublishCallback(server, newWriterGroup);
     LIST_INSERT_HEAD(&currentConnectionContext->writerGroups, newWriterGroup, listEntry);
 #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
     addWriterGroupRepresentation(server, newWriterGroup);
@@ -197,13 +201,26 @@ UA_Server_removeWriterGroup(UA_Server *server, const UA_NodeId writerGroup){
     if(!wg)
         return UA_STATUSCODE_BADNOTFOUND;
 
+    if(wg->config.configurationFrozen){
+        UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                       "Delete WriterGroup failed. WriterGroup is frozen.");
+        return UA_STATUSCODE_BADCONFIGURATIONERROR;
+    }
+
     UA_PubSubConnection *connection =
         UA_PubSubConnection_findConnectionbyId(server, wg->linkedConnection);
     if(!connection)
         return UA_STATUSCODE_BADNOTFOUND;
 
-    //unregister the publish callback
-    UA_PubSubManager_removeRepeatedPubSubCallback(server, wg->publishCallbackId);
+    if(connection->config->configurationFrozen){
+        UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                       "Delete WriterGroup failed. PubSubConnection is frozen.");
+        return UA_STATUSCODE_BADCONFIGURATIONERROR;
+    }
+    if(wg->state == UA_PUBSUBSTATE_OPERATIONAL){
+        //unregister the publish callback
+        UA_PubSubManager_removeRepeatedPubSubCallback(server, wg->publishCallbackId);
+    }
 #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
     removeGroupRepresentation(server, wg);
 #endif
@@ -214,6 +231,91 @@ UA_Server_removeWriterGroup(UA_Server *server, const UA_NodeId writerGroup){
     return UA_STATUSCODE_GOOD;
 }
 
+UA_StatusCode UA_EXPORT
+UA_Server_freezeWriterGroupConfiguration(UA_Server *server, const UA_NodeId writerGroup){
+    UA_WriterGroup *wg = UA_WriterGroup_findWGbyId(server, writerGroup);
+    if(!wg)
+        return UA_STATUSCODE_BADNOTFOUND;
+    //PubSubConnection freezeCounter++
+    UA_PubSubConnection *pubSubConnection = UA_PubSubConnection_findConnectionbyId(server, wg->linkedConnection);
+    pubSubConnection->configurationFreezeCounter++;
+    pubSubConnection->config->configurationFrozen = UA_TRUE;
+    //WriterGroup freeze
+    wg->config.configurationFrozen = UA_TRUE;
+    //DataSetWriter freeze
+    UA_DataSetWriter *dataSetWriter;
+    LIST_FOREACH(dataSetWriter, &wg->writers, listEntry){
+        dataSetWriter->config.configurationFrozen = UA_TRUE;
+        //PublishedDataSet freezeCounter++
+        UA_PublishedDataSet *publishedDataSet = UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet);
+        publishedDataSet->configurationFreezeCounter++;
+        publishedDataSet->config.configurationFrozen = UA_TRUE;
+        //DataSetFields freeze
+        UA_DataSetField *dataSetField;
+        LIST_FOREACH(dataSetField, &publishedDataSet->fields, listEntry){
+            dataSetField->config.configurationFrozen = UA_TRUE;
+        }
+    }
+    //if(wg->config.rtLevel == UA_PUBSUB_RT_FIXED_SIZE){
+        //UA_NetworkMessage_calculateBufferAndOffets(server, w)
+    //}
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_StatusCode UA_EXPORT
+UA_Server_unfreezeWriterGroupConfiguration(UA_Server *server, const UA_NodeId writerGroup){
+    UA_WriterGroup *wg = UA_WriterGroup_findWGbyId(server, writerGroup);
+    if(!wg)
+        return UA_STATUSCODE_BADNOTFOUND;
+    //if(wg->config.rtLevel == UA_PUBSUB_RT_NONE){
+    //    UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+    //                   "PubSub configuration freeze without RT configuration has no effect.");
+    //    return UA_STATUSCODE_BADCONFIGURATIONERROR;
+    //}
+    //PubSubConnection freezeCounter--
+    UA_PubSubConnection *pubSubConnection = UA_PubSubConnection_findConnectionbyId(server, wg->linkedConnection);
+    pubSubConnection->configurationFreezeCounter--;
+    if(pubSubConnection->configurationFreezeCounter == 0){
+        pubSubConnection->config->configurationFrozen = UA_FALSE;
+    }
+    //WriterGroup unfreeze
+    wg->config.configurationFrozen = UA_FALSE;
+    //DataSetWriter unfreeze
+    UA_DataSetWriter *dataSetWriter;
+    LIST_FOREACH(dataSetWriter, &wg->writers, listEntry){
+        UA_PublishedDataSet *publishedDataSet = UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet);
+        //PublishedDataSet freezeCounter--
+        publishedDataSet->configurationFreezeCounter--;
+        if(publishedDataSet->configurationFreezeCounter == 0){
+            publishedDataSet->config.configurationFrozen = UA_FALSE;
+            UA_DataSetField *dataSetField;
+            LIST_FOREACH(dataSetField, &publishedDataSet->fields, listEntry){
+                dataSetField->config.configurationFrozen = UA_FALSE;
+            }
+        }
+        dataSetWriter->config.configurationFrozen = UA_FALSE;
+    }
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_StatusCode UA_EXPORT
+UA_Server_setWriterGroupOperational(UA_Server *server, const UA_NodeId writerGroup){
+    UA_WriterGroup *wg = UA_WriterGroup_findWGbyId(server, writerGroup);
+
+    UA_WriterGroup_setPubSubState(server, UA_PUBSUBSTATE_OPERATIONAL, wg);
+
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_StatusCode UA_EXPORT
+UA_Server_setWriterGroupDisabled(UA_Server *server, const UA_NodeId writerGroup){
+    UA_WriterGroup *wg = UA_WriterGroup_findWGbyId(server, writerGroup);
+
+    UA_WriterGroup_setPubSubState(server, UA_PUBSUBSTATE_DISABLED, wg);
+
+    return UA_STATUSCODE_GOOD;
+}
+
 /**********************************************/
 /*               ReaderGroup                  */
 /**********************************************/
@@ -1061,6 +1163,13 @@ UA_Server_addDataSetField(UA_Server *server, const UA_NodeId publishedDataSet,
         return result;
     }
 
+    if(currentDataSet->config.configurationFrozen){
+        UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                       "Adding DataSetField failed. PublishedDataSet is frozen.");
+        result.result = UA_STATUSCODE_BADCONFIGURATIONERROR;
+        return result;
+    }
+
     if(currentDataSet->config.publishedDataSetType != UA_PUBSUB_DATASET_PUBLISHEDITEMS){
         result.result = UA_STATUSCODE_BADNOTIMPLEMENTED;
         return result;
@@ -1099,11 +1208,25 @@ UA_Server_removeDataSetField(UA_Server *server, const UA_NodeId dsf) {
     if(!currentField)
         return result;
 
+    if(currentField->config.configurationFrozen){
+        UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                       "Remove DataSetField failed. DataSetField is frozen.");
+        result.result = UA_STATUSCODE_BADCONFIGURATIONERROR;
+        return result;
+    }
+
     UA_PublishedDataSet *parentPublishedDataSet =
         UA_PublishedDataSet_findPDSbyId(server, currentField->publishedDataSet);
     if(!parentPublishedDataSet)
         return result;
 
+    if(parentPublishedDataSet->config.configurationFrozen){
+        UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                       "Remove DataSetField failed. PublishedDataSet is frozen.");
+        result.result = UA_STATUSCODE_BADCONFIGURATIONERROR;
+        return result;
+    }
+
     parentPublishedDataSet->fieldSize--;
     if(currentField->config.field.variable.promotedField)
         parentPublishedDataSet->promotedFieldsCount--;
@@ -1206,6 +1329,82 @@ UA_DataSetWriter_deleteMembers(UA_Server *server, UA_DataSetWriter *dataSetWrite
 #endif
 }
 
+//state machine methods not part of the open62541 state machine API
+UA_StatusCode
+UA_DataSetWriter_setPubSubState(UA_Server *server, UA_PubSubState state, UA_DataSetWriter *dataSetWriter){
+    switch(state){
+        case UA_PUBSUBSTATE_DISABLED:
+            switch (dataSetWriter->state){
+                case UA_PUBSUBSTATE_DISABLED:
+                    return UA_STATUSCODE_GOOD;
+                case UA_PUBSUBSTATE_PAUSED:
+                    dataSetWriter->state = UA_PUBSUBSTATE_DISABLED;
+                    //no further action is required
+                    break;
+                case UA_PUBSUBSTATE_OPERATIONAL:
+                    dataSetWriter->state = UA_PUBSUBSTATE_DISABLED;
+
+                    break;
+                case UA_PUBSUBSTATE_ERROR:
+                    break;
+                default:
+                    UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                                   "Received unknown PubSub state!");
+            }
+            break;
+        case UA_PUBSUBSTATE_PAUSED:
+            switch (dataSetWriter->state){
+                case UA_PUBSUBSTATE_DISABLED:
+                    break;
+                case UA_PUBSUBSTATE_PAUSED:
+                    return UA_STATUSCODE_GOOD;
+                case UA_PUBSUBSTATE_OPERATIONAL:
+                    break;
+                case UA_PUBSUBSTATE_ERROR:
+                    break;
+                default:
+                    UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                                   "Received unknown PubSub state!");
+            }
+            break;
+        case UA_PUBSUBSTATE_OPERATIONAL:
+            switch (dataSetWriter->state){
+                case UA_PUBSUBSTATE_DISABLED:
+                    dataSetWriter->state = UA_PUBSUBSTATE_OPERATIONAL;
+                    break;
+                case UA_PUBSUBSTATE_PAUSED:
+                    break;
+                case UA_PUBSUBSTATE_OPERATIONAL:
+                    return UA_STATUSCODE_GOOD;
+                case UA_PUBSUBSTATE_ERROR:
+                    break;
+                default:
+                    UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                                   "Received unknown PubSub state!");
+            }
+            break;
+        case UA_PUBSUBSTATE_ERROR:
+            switch (dataSetWriter->state){
+                case UA_PUBSUBSTATE_DISABLED:
+                    break;
+                case UA_PUBSUBSTATE_PAUSED:
+                    break;
+                case UA_PUBSUBSTATE_OPERATIONAL:
+                    break;
+                case UA_PUBSUBSTATE_ERROR:
+                    return UA_STATUSCODE_GOOD;
+                default:
+                    UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                                   "Received unknown PubSub state!");
+            }
+            break;
+        default:
+            UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                                           "Received unknown PubSub state!");
+    }
+    return UA_STATUSCODE_GOOD;
+}
+
 /**********************************************/
 /*               WriterGroup                  */
 /**********************************************/
@@ -1254,6 +1453,13 @@ UA_Server_updateWriterGroupConfig(UA_Server *server, UA_NodeId writerGroupIdenti
     UA_WriterGroup *currentWriterGroup = UA_WriterGroup_findWGbyId(server, writerGroupIdentifier);
     if(!currentWriterGroup)
         return UA_STATUSCODE_BADNOTFOUND;
+
+    if(currentWriterGroup->config.configurationFrozen){
+        UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                       "Modify WriterGroup failed. WriterGroup is frozen.");
+        return UA_STATUSCODE_BADCONFIGURATIONERROR;
+    }
+
     //The update functionality will be extended during the next PubSub batches.
     //Currently is only a change of the publishing interval possible.
     if(currentWriterGroup->config.maxEncapsulatedDataSetMessageCount != config->maxEncapsulatedDataSetMessageCount){
@@ -1264,9 +1470,13 @@ UA_Server_updateWriterGroupConfig(UA_Server *server, UA_NodeId writerGroupIdenti
         }
     }
     if(currentWriterGroup->config.publishingInterval != config->publishingInterval) {
-        UA_PubSubManager_removeRepeatedPubSubCallback(server, currentWriterGroup->publishCallbackId);
-        currentWriterGroup->config.publishingInterval = config->publishingInterval;
-        UA_WriterGroup_addPublishCallback(server, currentWriterGroup);
+        if(currentWriterGroup->config.rtLevel == UA_PUBSUB_RT_NONE && currentWriterGroup->state == UA_PUBSUBSTATE_OPERATIONAL){
+            UA_PubSubManager_removeRepeatedPubSubCallback(server, currentWriterGroup->publishCallbackId);
+            currentWriterGroup->config.publishingInterval = config->publishingInterval;
+            UA_WriterGroup_addPublishCallback(server, currentWriterGroup);
+        } else {
+            currentWriterGroup->config.publishingInterval = config->publishingInterval;
+        }
     }
     if(currentWriterGroup->config.priority != config->priority) {
         UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
@@ -1313,6 +1523,89 @@ UA_WriterGroup_deleteMembers(UA_Server *server, UA_WriterGroup *writerGroup) {
     UA_NodeId_deleteMembers(&writerGroup->identifier);
 }
 
+UA_StatusCode
+UA_WriterGroup_setPubSubState(UA_Server *server, UA_PubSubState state, UA_WriterGroup *writerGroup){
+    UA_DataSetWriter *dataSetWriter;
+    switch(state){
+        case UA_PUBSUBSTATE_DISABLED:
+            switch (writerGroup->state){
+                case UA_PUBSUBSTATE_DISABLED:
+                    return UA_STATUSCODE_GOOD;
+                case UA_PUBSUBSTATE_PAUSED:
+                    break;
+                case UA_PUBSUBSTATE_OPERATIONAL:
+                    UA_PubSubManager_removeRepeatedPubSubCallback(server, writerGroup->publishCallbackId);
+                    LIST_FOREACH(dataSetWriter, &writerGroup->writers, listEntry){
+                        UA_DataSetWriter_setPubSubState(server, UA_PUBSUBSTATE_DISABLED, dataSetWriter);
+                    }
+                    writerGroup->state = UA_PUBSUBSTATE_DISABLED;
+                    break;
+                case UA_PUBSUBSTATE_ERROR:
+                    break;
+                default:
+                    UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                                   "Received unknown PubSub state!");
+            }
+            break;
+        case UA_PUBSUBSTATE_PAUSED:
+            switch (writerGroup->state){
+                case UA_PUBSUBSTATE_DISABLED:
+                    break;
+                case UA_PUBSUBSTATE_PAUSED:
+                    return UA_STATUSCODE_GOOD;
+                case UA_PUBSUBSTATE_OPERATIONAL:
+                    break;
+                case UA_PUBSUBSTATE_ERROR:
+                    break;
+                default:
+                    UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                                   "Received unknown PubSub state!");
+            }
+            break;
+        case UA_PUBSUBSTATE_OPERATIONAL:
+            switch (writerGroup->state){
+                case UA_PUBSUBSTATE_DISABLED:
+                    writerGroup->state = UA_PUBSUBSTATE_OPERATIONAL;
+                    UA_PubSubManager_removeRepeatedPubSubCallback(server, writerGroup->publishCallbackId);
+                    LIST_FOREACH(dataSetWriter, &writerGroup->writers, listEntry){
+                        UA_DataSetWriter_setPubSubState(server, UA_PUBSUBSTATE_OPERATIONAL, dataSetWriter);
+                    }
+                    UA_WriterGroup_addPublishCallback(server, writerGroup);
+                    break;
+                case UA_PUBSUBSTATE_PAUSED:
+                    break;
+                case UA_PUBSUBSTATE_OPERATIONAL:
+                    return UA_STATUSCODE_GOOD;
+                case UA_PUBSUBSTATE_ERROR:
+                    break;
+                default:
+                    UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                                   "Received unknown PubSub state!");
+            }
+            break;
+        case UA_PUBSUBSTATE_ERROR:
+            switch (writerGroup->state){
+                case UA_PUBSUBSTATE_DISABLED:
+                    break;
+                case UA_PUBSUBSTATE_PAUSED:
+                    break;
+                case UA_PUBSUBSTATE_OPERATIONAL:
+                    break;
+                case UA_PUBSUBSTATE_ERROR:
+                    return UA_STATUSCODE_GOOD;
+                default:
+                    UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                                   "Received unknown PubSub state!");
+            }
+            break;
+        default:
+            UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                           "Received unknown PubSub state!");
+    }
+    return UA_STATUSCODE_GOOD;
+}
+
+
 UA_StatusCode
 UA_Server_addDataSetWriter(UA_Server *server,
                            const UA_NodeId writerGroup, const UA_NodeId dataSet,
@@ -1326,10 +1619,22 @@ UA_Server_addDataSetWriter(UA_Server *server,
     if(!currentDataSetContext)
         return UA_STATUSCODE_BADNOTFOUND;
 
+    if(currentDataSetContext->config.configurationFrozen){
+        UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                       "Adding DataSetWriter failed. PublishedDataSet is frozen.");
+        return UA_STATUSCODE_BADCONFIGURATIONERROR;
+    }
+
     UA_WriterGroup *wg = UA_WriterGroup_findWGbyId(server, writerGroup);
     if(!wg)
         return UA_STATUSCODE_BADNOTFOUND;
 
+    if(wg->config.configurationFrozen){
+        UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                       "Adding DataSetWriter failed. WriterGroup is frozen.");
+        return UA_STATUSCODE_BADCONFIGURATIONERROR;
+    }
+
     UA_DataSetWriter *newDataSetWriter = (UA_DataSetWriter *) UA_calloc(1, sizeof(UA_DataSetWriter));
     if(!newDataSetWriter)
         return UA_STATUSCODE_BADOUTOFMEMORY;
@@ -1374,10 +1679,26 @@ UA_Server_removeDataSetWriter(UA_Server *server, const UA_NodeId dsw){
     if(!dataSetWriter)
         return UA_STATUSCODE_BADNOTFOUND;
 
+    if(dataSetWriter->config.configurationFrozen){
+        UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                       "Remove DataSetWriter failed. DataSetWriter is frozen.");
+        return UA_STATUSCODE_BADCONFIGURATIONERROR;
+    }
+
     UA_WriterGroup *linkedWriterGroup = UA_WriterGroup_findWGbyId(server, dataSetWriter->linkedWriterGroup);
     if(!linkedWriterGroup)
         return UA_STATUSCODE_BADNOTFOUND;
 
+    if(linkedWriterGroup->config.configurationFrozen){
+        UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                       "Remove DataSetWriter failed. WriterGroup is frozen.");
+        return UA_STATUSCODE_BADCONFIGURATIONERROR;
+    }
+
+    UA_PublishedDataSet *publishedDataSet = UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet);
+    if(!publishedDataSet)
+        return UA_STATUSCODE_BADNOTFOUND;
+
     linkedWriterGroup->writersCount--;
 #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
     removeDataSetWriterRepresentation(server, dataSetWriter);
@@ -1514,12 +1835,18 @@ static void
 UA_PubSubDataSetField_sampleValue(UA_Server *server, UA_DataSetField *field,
                                   UA_DataValue *value) {
     /* Read the value */
-    UA_ReadValueId rvid;
-    UA_ReadValueId_init(&rvid);
-    rvid.nodeId = field->config.field.variable.publishParameters.publishedVariable;
-    rvid.attributeId = field->config.field.variable.publishParameters.attributeId;
-    rvid.indexRange = field->config.field.variable.publishParameters.indexRange;
-    *value = UA_Server_read(server, &rvid, UA_TIMESTAMPSTORETURN_BOTH);
+    if(field->config.field.variable.staticValueSourceEnabled == UA_FALSE){
+        UA_ReadValueId rvid;
+        UA_ReadValueId_init(&rvid);
+        rvid.nodeId = field->config.field.variable.publishParameters.publishedVariable;
+        rvid.attributeId = field->config.field.variable.publishParameters.attributeId;
+        rvid.indexRange = field->config.field.variable.publishParameters.indexRange;
+        *value = UA_Server_read(server, &rvid, UA_TIMESTAMPSTORETURN_BOTH);
+    } else {
+        value->value.storageType = UA_VARIANT_DATA_NODELETE;
+        *value = field->config.field.variable.staticValueSource;
+    }
+
 }
 
 static UA_StatusCode

+ 42 - 0
src/pubsub/ua_pubsub.h

@@ -40,6 +40,7 @@ typedef struct{
     UA_NodeId identifier;
     UA_UInt16 fieldSize;
     UA_UInt16 promotedFieldsCount;
+    UA_UInt16 configurationFreezeCounter;
 } UA_PublishedDataSet;
 
 UA_StatusCode
@@ -61,6 +62,8 @@ typedef struct{
     LIST_HEAD(UA_ListOfWriterGroup, UA_WriterGroup) writerGroups;
     LIST_HEAD(UA_ListOfPubSubReaderGroup, UA_ReaderGroup) readerGroups;
     size_t readerGroupsSize;
+
+    UA_UInt16 configurationFreezeCounter;
 } UA_PubSubConnection;
 
 UA_StatusCode
@@ -94,6 +97,7 @@ typedef struct UA_DataSetWriter{
     UA_NodeId linkedWriterGroup;
     UA_NodeId connectedDataSet;
     UA_ConfigurationVersionDataType connectedDataSetVersion;
+    UA_PubSubState state;
 #ifdef UA_ENABLE_PUBSUB_DELTAFRAMES
     UA_UInt16 deltaFrameCounter;            //actual count of sent deltaFrames
     size_t lastSamplesCount;
@@ -106,6 +110,39 @@ UA_StatusCode
 UA_DataSetWriterConfig_copy(const UA_DataSetWriterConfig *src, UA_DataSetWriterConfig *dst);
 UA_DataSetWriter *
 UA_DataSetWriter_findDSWbyId(UA_Server *server, UA_NodeId identifier);
+UA_StatusCode
+UA_DataSetWriter_setPubSubState(UA_Server *server, UA_PubSubState state, UA_DataSetWriter *dataSetWriter);
+
+/**********************************************/
+/*          Network Message Offsets           */
+/**********************************************/
+
+/* Offsets for buffered messages in the PubSub fast path.
+ * TODO: Implement the generation of the offsets */
+typedef enum {
+	UA_PUBSUB_OFFSETTYPE_SEQUENCENUMBER,
+    UA_PUBSUB_OFFSETTYPE_TIMESTAMP,     /* source pointer */
+	UA_PUBSUB_OFFSETTYPE_TIMESTAMP_NOW, /* no source */
+    UA_PUBSUB_OFFSETTYPE_PAYLOAD_DATAVALUE,
+	UA_PUBSUB_OFFSETTYPE_PAYLOAD_VARIANT,
+	UA_PUBSUB_OFFSETTYPE_PAYLOAD_RAW
+    /* Add more offset types as needed */
+} UA_NetworkMessageOffsetType;
+
+typedef struct {
+    UA_NetworkMessageOffsetType contentType;
+	union {
+		UA_DateTime *timestamp;
+		UA_DataValue *value;
+	} offsetData;
+	size_t offset;
+} UA_NetworkMessageOffset;
+
+typedef struct {
+    UA_ByteString buffer; /* The precomputed message buffer */
+    UA_NetworkMessageOffset *offsets; /* Offsets for changes in the message buffer */
+    size_t offsetsSize;
+} UA_NetworkMessageOffsetBuffer;
 
 /**********************************************/
 /*               WriterGroup                  */
@@ -121,12 +158,16 @@ struct UA_WriterGroup{
     UA_UInt32 writersCount;
     UA_UInt64 publishCallbackId;
     UA_Boolean publishCallbackIsRegistered;
+    UA_PubSubState state;
+    UA_NetworkMessageOffsetBuffer bufferedMessage;
 };
 
 UA_StatusCode
 UA_WriterGroupConfig_copy(const UA_WriterGroupConfig *src, UA_WriterGroupConfig *dst);
 UA_WriterGroup *
 UA_WriterGroup_findWGbyId(UA_Server *server, UA_NodeId identifier);
+UA_StatusCode
+UA_WriterGroup_setPubSubState(UA_Server *server, UA_PubSubState state, UA_WriterGroup *writerGroup);
 
 /**********************************************/
 /*               DataSetField                 */
@@ -141,6 +182,7 @@ typedef struct UA_DataSetField{
     UA_FieldMetaData fieldMetaData;
     UA_UInt64 sampleCallbackId;
     UA_Boolean sampleCallbackIsRegistered;
+
 } UA_DataSetField;
 
 UA_StatusCode

+ 6 - 0
src/pubsub/ua_pubsub_manager.c

@@ -227,6 +227,12 @@ UA_Server_removePublishedDataSet(UA_Server *server, const UA_NodeId pds) {
     if(!publishedDataSet){
         return UA_STATUSCODE_BADNOTFOUND;
     }
+    if(publishedDataSet->config.configurationFrozen){
+        UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                       "Remove PublishedDataSet failed. PublishedDataSet is frozen.");
+        return UA_STATUSCODE_BADCONFIGURATIONERROR;
+    }
+
     //search for referenced writers -> delete this writers. (Standard: writer must be connected with PDS)
     for(size_t i = 0; i < server->pubSubManager.connectionsSize; i++){
         UA_WriterGroup *writerGroup;

+ 3 - 0
tests/CMakeLists.txt

@@ -307,6 +307,9 @@ if(UA_ENABLE_PUBSUB)
     add_executable(check_pubsub_publishspeed pubsub/check_pubsub_publishspeed.c $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-plugins>)
     target_link_libraries(check_pubsub_publishspeed ${LIBS})
     add_test_valgrind(pubsub_publishspeed ${TESTS_BINARY_DIR}/check_pubsub_publish)
+    add_executable(check_pubsub_config_freeze pubsub/check_pubsub_config_freeze.c $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-plugins>)
+    target_link_libraries(check_pubsub_config_freeze ${LIBS})
+    add_test_valgrind(check_pubsub_config_freeze ${TESTS_BINARY_DIR}/check_pubsub_config_freeze)
 
     add_executable(check_pubsub_multiple_layer pubsub/check_pubsub_multiple_layer.c $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-plugins>)
     target_link_libraries(check_pubsub_multiple_layer ${LIBS})

+ 361 - 0
tests/pubsub/check_pubsub_config_freeze.c

@@ -0,0 +1,361 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * Copyright (c) 2019 Fraunhofer IOSB (Author: Andreas Ebner)
+ */
+
+#include <open62541/plugin/pubsub_udp.h>
+#include <open62541/server_config_default.h>
+#include <open62541/server_pubsub.h>
+#include <open62541/types_generated_encoding_binary.h>
+
+#include "ua_server_internal.h"
+
+#include <check.h>
+#include <stdio.h>
+#include <time.h>
+
+UA_Server *server = NULL;
+
+static void setup(void) {
+    server = UA_Server_new();
+    UA_ServerConfig *config = UA_Server_getConfig(server);
+    UA_ServerConfig_setDefault(config);
+
+    config->pubsubTransportLayers = (UA_PubSubTransportLayer*)
+            UA_malloc(sizeof(UA_PubSubTransportLayer));
+    config->pubsubTransportLayers[0] = UA_PubSubTransportLayerUDPMP();
+    config->pubsubTransportLayersSize++;
+
+    UA_Server_run_startup(server);
+}
+
+static void teardown(void) {
+    UA_Server_run_shutdown(server);
+    UA_Server_delete(server);
+}
+
+START_TEST(CreateAndLockConfiguration) {
+    //create config
+    UA_NodeId connection1, writerGroup1, publishedDataSet1, dataSetWriter1, dataSetField1;
+    UA_PubSubConnectionConfig connectionConfig;
+    memset(&connectionConfig, 0, sizeof(UA_PubSubConnectionConfig));
+    connectionConfig.name = UA_STRING("UADP Connection");
+    UA_NetworkAddressUrlDataType networkAddressUrl = {UA_STRING_NULL, UA_STRING("opc.udp://224.0.0.22:4840/")};
+    UA_Variant_setScalar(&connectionConfig.address, &networkAddressUrl,
+    &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]);
+    connectionConfig.transportProfileUri = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp");
+    UA_Server_addPubSubConnection(server, &connectionConfig, &connection1);
+
+    UA_WriterGroupConfig writerGroupConfig;
+    memset(&writerGroupConfig, 0, sizeof(writerGroupConfig));
+    writerGroupConfig.name = UA_STRING("WriterGroup 1");
+    writerGroupConfig.publishingInterval = 10;
+    writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP;
+    writerGroupConfig.rtLevel = UA_PUBSUB_RT_FIXED_SIZE;
+    UA_Server_addWriterGroup(server, connection1, &writerGroupConfig, &writerGroup1);
+
+    UA_PublishedDataSetConfig pdsConfig;
+    memset(&pdsConfig, 0, sizeof(UA_PublishedDataSetConfig));
+    pdsConfig.publishedDataSetType = UA_PUBSUB_DATASET_PUBLISHEDITEMS;
+    pdsConfig.name = UA_STRING("PublishedDataSet 1");
+    UA_Server_addPublishedDataSet(server, &pdsConfig, &publishedDataSet1);
+
+    UA_DataSetFieldConfig fieldConfig;
+    memset(&fieldConfig, 0, sizeof(UA_DataSetFieldConfig));
+    fieldConfig.dataSetFieldType = UA_PUBSUB_DATASETFIELD_VARIABLE;
+    fieldConfig.field.variable.fieldNameAlias = UA_STRING("field 1");
+    UA_Server_addDataSetField(server, publishedDataSet1, &fieldConfig, &dataSetField1);
+
+    UA_DataSetField *dataSetField = UA_DataSetField_findDSFbyId(server, dataSetField1);
+    ck_assert(dataSetField->config.configurationFrozen == UA_FALSE);
+
+    //get internal WG Pointer
+    UA_WriterGroup *writerGroup = UA_WriterGroup_findWGbyId(server, writerGroup1);
+    ck_assert(writerGroup->state == UA_PUBSUBSTATE_DISABLED);
+
+    UA_DataSetWriterConfig dataSetWriterConfig;
+    memset(&dataSetWriterConfig, 0, sizeof(dataSetWriterConfig));
+    dataSetWriterConfig.name = UA_STRING("DataSetWriter 1");
+    UA_Server_addDataSetWriter(server, writerGroup1, publishedDataSet1, &dataSetWriterConfig, &dataSetWriter1);
+    UA_DataSetWriter *dataSetWriter = UA_DataSetWriter_findDSWbyId(server, dataSetWriter1);
+
+    //get internal PubSubConnection Pointer
+    UA_PubSubConnection *pubSubConnection = UA_PubSubConnection_findConnectionbyId(server, connection1);
+
+    ck_assert(dataSetWriter->config.configurationFrozen == UA_FALSE);
+    //Lock the writer group and the child pubsub entities
+        UA_Server_freezeWriterGroupConfiguration(server, writerGroup1);
+    ck_assert(dataSetWriter->config.configurationFrozen == UA_TRUE);
+    ck_assert(dataSetField->config.configurationFrozen == UA_TRUE);
+    ck_assert(pubSubConnection->config->configurationFrozen == UA_TRUE);
+    UA_PublishedDataSet *publishedDataSet = UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet);
+    ck_assert(publishedDataSet->config.configurationFrozen == UA_TRUE);
+    UA_DataSetField *dsf;
+    LIST_FOREACH(dsf ,&publishedDataSet->fields , listEntry){
+        ck_assert(dsf->config.configurationFrozen == UA_TRUE);
+    }
+    //set state to disabled and implicit unlock the configuration
+        UA_Server_unfreezeWriterGroupConfiguration(server, writerGroup1);
+} END_TEST
+
+START_TEST(CreateAndLockConfigurationWithExternalAPI) {
+        //create config
+        UA_NodeId connection1, writerGroup1, publishedDataSet1, dataSetWriter1, dataSetField1;
+        UA_PubSubConnectionConfig connectionConfig;
+        memset(&connectionConfig, 0, sizeof(UA_PubSubConnectionConfig));
+        connectionConfig.name = UA_STRING("UADP Connection");
+        UA_NetworkAddressUrlDataType networkAddressUrl = {UA_STRING_NULL, UA_STRING("opc.udp://224.0.0.22:4840/")};
+        UA_Variant_setScalar(&connectionConfig.address, &networkAddressUrl,
+                             &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]);
+        connectionConfig.transportProfileUri = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp");
+        UA_Server_addPubSubConnection(server, &connectionConfig, &connection1);
+
+        UA_WriterGroupConfig writerGroupConfig;
+        memset(&writerGroupConfig, 0, sizeof(writerGroupConfig));
+        writerGroupConfig.name = UA_STRING("WriterGroup 1");
+        writerGroupConfig.publishingInterval = 10;
+        writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP;
+        writerGroupConfig.rtLevel = UA_PUBSUB_RT_FIXED_SIZE;
+        UA_Server_addWriterGroup(server, connection1, &writerGroupConfig, &writerGroup1);
+
+        UA_PublishedDataSetConfig pdsConfig;
+        memset(&pdsConfig, 0, sizeof(UA_PublishedDataSetConfig));
+        pdsConfig.publishedDataSetType = UA_PUBSUB_DATASET_PUBLISHEDITEMS;
+        pdsConfig.name = UA_STRING("PublishedDataSet 1");
+        UA_Server_addPublishedDataSet(server, &pdsConfig, &publishedDataSet1);
+
+        UA_DataSetFieldConfig fieldConfig;
+        memset(&fieldConfig, 0, sizeof(UA_DataSetFieldConfig));
+        fieldConfig.dataSetFieldType = UA_PUBSUB_DATASETFIELD_VARIABLE;
+        fieldConfig.field.variable.fieldNameAlias = UA_STRING("field 1");
+        UA_Server_addDataSetField(server, publishedDataSet1, &fieldConfig, &dataSetField1);
+
+        UA_DataSetField *dataSetField = UA_DataSetField_findDSFbyId(server, dataSetField1);
+        ck_assert(dataSetField->config.configurationFrozen == UA_FALSE);
+
+        //get internal WG Pointer
+        UA_WriterGroup *writerGroup = UA_WriterGroup_findWGbyId(server, writerGroup1);
+        ck_assert(writerGroup->state == UA_PUBSUBSTATE_DISABLED);
+
+        UA_DataSetWriterConfig dataSetWriterConfig;
+        memset(&dataSetWriterConfig, 0, sizeof(dataSetWriterConfig));
+        dataSetWriterConfig.name = UA_STRING("DataSetWriter 1");
+        UA_Server_addDataSetWriter(server, writerGroup1, publishedDataSet1, &dataSetWriterConfig, &dataSetWriter1);
+        UA_DataSetWriter *dataSetWriter = UA_DataSetWriter_findDSWbyId(server, dataSetWriter1);
+
+        //get internal PubSubConnection Pointer
+        UA_PubSubConnection *pubSubConnection = UA_PubSubConnection_findConnectionbyId(server, connection1);
+
+        ck_assert(dataSetWriter->config.configurationFrozen == UA_FALSE);
+        //Lock the with the freeze function
+        UA_Server_freezeWriterGroupConfiguration(server, writerGroup1);
+        ck_assert(dataSetWriter->config.configurationFrozen == UA_TRUE);
+        ck_assert(dataSetField->config.configurationFrozen == UA_TRUE);
+        ck_assert(pubSubConnection->config->configurationFrozen == UA_TRUE);
+        UA_PublishedDataSet *publishedDataSet = UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet);
+        ck_assert(publishedDataSet->config.configurationFrozen == UA_TRUE);
+        UA_DataSetField *dsf;
+        LIST_FOREACH(dsf ,&publishedDataSet->fields , listEntry){
+            ck_assert(dsf->config.configurationFrozen == UA_TRUE);
+        }
+        //set state to disabled and implicit unlock the configuration
+        UA_Server_unfreezeWriterGroupConfiguration(server, writerGroup1);
+    } END_TEST
+
+START_TEST(CreateAndReleaseMultiplePDSLocks) {
+    //create config
+    UA_NodeId connection1, writerGroup1, writerGroup2, publishedDataSet1, dataSetWriter1, dataSetWriter2, dataSetWriter3, dataSetField1;
+    UA_PubSubConnectionConfig connectionConfig;
+    memset(&connectionConfig, 0, sizeof(UA_PubSubConnectionConfig));
+    connectionConfig.name = UA_STRING("UADP Connection");
+    UA_NetworkAddressUrlDataType networkAddressUrl = {UA_STRING_NULL, UA_STRING("opc.udp://224.0.0.22:4840/")};
+    UA_Variant_setScalar(&connectionConfig.address, &networkAddressUrl,
+                         &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]);
+    connectionConfig.transportProfileUri = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp");
+    UA_Server_addPubSubConnection(server, &connectionConfig, &connection1);
+
+    //Add two writer groups
+    UA_WriterGroupConfig writerGroupConfig;
+    memset(&writerGroupConfig, 0, sizeof(writerGroupConfig));
+    writerGroupConfig.name = UA_STRING("WriterGroup 1");
+    writerGroupConfig.publishingInterval = 10;
+    writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP;
+    writerGroupConfig.rtLevel = UA_PUBSUB_RT_FIXED_SIZE;
+    UA_Server_addWriterGroup(server, connection1, &writerGroupConfig, &writerGroup1);
+    writerGroupConfig.name = UA_STRING("WriterGroup 2");
+    UA_Server_addWriterGroup(server, connection1, &writerGroupConfig, &writerGroup2);
+
+    UA_PublishedDataSetConfig pdsConfig;
+    memset(&pdsConfig, 0, sizeof(UA_PublishedDataSetConfig));
+    pdsConfig.publishedDataSetType = UA_PUBSUB_DATASET_PUBLISHEDITEMS;
+    pdsConfig.name = UA_STRING("PublishedDataSet 1");
+    UA_Server_addPublishedDataSet(server, &pdsConfig, &publishedDataSet1);
+
+    UA_DataSetFieldConfig fieldConfig;
+    memset(&fieldConfig, 0, sizeof(UA_DataSetFieldConfig));
+    fieldConfig.dataSetFieldType = UA_PUBSUB_DATASETFIELD_VARIABLE;
+    fieldConfig.field.variable.fieldNameAlias = UA_STRING("field 1");
+    UA_Server_addDataSetField(server, publishedDataSet1, &fieldConfig, &dataSetField1);
+
+    UA_DataSetField *dataSetField = UA_DataSetField_findDSFbyId(server, dataSetField1);
+
+    UA_DataSetWriterConfig dataSetWriterConfig;
+    memset(&dataSetWriterConfig, 0, sizeof(dataSetWriterConfig));
+    dataSetWriterConfig.name = UA_STRING("DataSetWriter 1");
+    UA_Server_addDataSetWriter(server, writerGroup1, publishedDataSet1, &dataSetWriterConfig, &dataSetWriter1);
+    dataSetWriterConfig.name = UA_STRING("DataSetWriter 2");
+    UA_Server_addDataSetWriter(server, writerGroup1, publishedDataSet1, &dataSetWriterConfig, &dataSetWriter2);
+    dataSetWriterConfig.name = UA_STRING("DataSetWriter 3");
+    UA_Server_addDataSetWriter(server, writerGroup2, publishedDataSet1, &dataSetWriterConfig, &dataSetWriter3);
+
+    UA_WriterGroup *writerGroup_1 = UA_WriterGroup_findWGbyId(server, writerGroup1);
+    UA_WriterGroup *writerGroup_2 = UA_WriterGroup_findWGbyId(server, writerGroup2);
+    UA_PublishedDataSet *publishedDataSet = UA_PublishedDataSet_findPDSbyId(server, publishedDataSet1);
+    UA_PubSubConnection *pubSubConnection = UA_PubSubConnection_findConnectionbyId(server, connection1);
+    //freeze configuratoin of both WG
+    ck_assert(writerGroup_1->config.configurationFrozen == UA_FALSE);
+    ck_assert(writerGroup_2->config.configurationFrozen == UA_FALSE);
+    ck_assert(publishedDataSet->config.configurationFrozen == UA_FALSE);
+    ck_assert(pubSubConnection->config->configurationFrozen == UA_FALSE);
+        UA_Server_freezeWriterGroupConfiguration(server, writerGroup1);
+        UA_Server_freezeWriterGroupConfiguration(server, writerGroup2);
+    ck_assert(writerGroup_1->config.configurationFrozen == UA_TRUE);
+    ck_assert(writerGroup_2->config.configurationFrozen == UA_TRUE);
+    ck_assert(publishedDataSet->config.configurationFrozen == UA_TRUE);
+    ck_assert(pubSubConnection->config->configurationFrozen == UA_TRUE);
+    //unlock one tree, get sure pds still locked
+        UA_Server_unfreezeWriterGroupConfiguration(server, writerGroup1);
+    ck_assert(writerGroup_1->config.configurationFrozen == UA_FALSE);
+    ck_assert(publishedDataSet->config.configurationFrozen == UA_TRUE);
+    ck_assert(dataSetField->config.configurationFrozen == UA_TRUE);
+        UA_Server_unfreezeWriterGroupConfiguration(server, writerGroup2);
+    ck_assert(publishedDataSet->config.configurationFrozen == UA_FALSE);
+    ck_assert(dataSetField->config.configurationFrozen == UA_FALSE);
+    ck_assert(pubSubConnection->config->configurationFrozen == UA_FALSE);
+
+    } END_TEST
+
+START_TEST(CreateLockAndEditConfiguration) {
+    UA_NodeId connection1, writerGroup1, publishedDataSet1, dataSetWriter1;
+    UA_PubSubConnectionConfig connectionConfig;
+    memset(&connectionConfig, 0, sizeof(UA_PubSubConnectionConfig));
+    connectionConfig.name = UA_STRING("UADP Connection");
+    UA_NetworkAddressUrlDataType networkAddressUrl = {UA_STRING_NULL, UA_STRING("opc.udp://224.0.0.22:4840/")};
+    UA_Variant_setScalar(&connectionConfig.address, &networkAddressUrl,
+                         &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]);
+    connectionConfig.transportProfileUri = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp");
+    UA_Server_addPubSubConnection(server, &connectionConfig, &connection1);
+
+    UA_WriterGroupConfig writerGroupConfig;
+    memset(&writerGroupConfig, 0, sizeof(writerGroupConfig));
+    writerGroupConfig.name = UA_STRING("WriterGroup 1");
+    writerGroupConfig.publishingInterval = 10;
+    writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP;
+    writerGroupConfig.rtLevel = UA_PUBSUB_RT_FIXED_SIZE;
+    UA_Server_addWriterGroup(server, connection1, &writerGroupConfig, &writerGroup1);
+
+    UA_PublishedDataSetConfig pdsConfig;
+    memset(&pdsConfig, 0, sizeof(UA_PublishedDataSetConfig));
+    pdsConfig.publishedDataSetType = UA_PUBSUB_DATASET_PUBLISHEDITEMS;
+    pdsConfig.name = UA_STRING("PublishedDataSet 1");
+    UA_Server_addPublishedDataSet(server, &pdsConfig, &publishedDataSet1);
+
+    UA_DataSetFieldConfig fieldConfig;
+    memset(&fieldConfig, 0, sizeof(UA_DataSetFieldConfig));
+    fieldConfig.dataSetFieldType = UA_PUBSUB_DATASETFIELD_VARIABLE;
+    fieldConfig.field.variable.fieldNameAlias = UA_STRING("field 1");
+    UA_NodeId localDataSetField;
+    UA_Server_addDataSetField(server, publishedDataSet1, &fieldConfig, &localDataSetField);
+
+    //get internal WG Pointer
+    UA_WriterGroup *writerGroup = UA_WriterGroup_findWGbyId(server, writerGroup1);
+    ck_assert(writerGroup->state == UA_PUBSUBSTATE_DISABLED);
+
+    UA_DataSetWriterConfig dataSetWriterConfig;
+    memset(&dataSetWriterConfig, 0, sizeof(dataSetWriterConfig));
+    dataSetWriterConfig.name = UA_STRING("DataSetWriter 1");
+    UA_Server_addDataSetWriter(server, writerGroup1, publishedDataSet1, &dataSetWriterConfig, &dataSetWriter1);
+    UA_DataSetWriter *dataSetWriter = UA_DataSetWriter_findDSWbyId(server, dataSetWriter1);
+
+    ck_assert(dataSetWriter->config.configurationFrozen == UA_FALSE);
+    //Lock the writer group and the child pubsub entities
+        UA_Server_freezeWriterGroupConfiguration(server, writerGroup1);
+    //call not allowed configuration methods
+    UA_DataSetFieldResult fieldRemoveResult = UA_Server_removeDataSetField(server, localDataSetField);
+    ck_assert(fieldRemoveResult.result == UA_STATUSCODE_BADCONFIGURATIONERROR);
+    ck_assert(UA_Server_removePublishedDataSet(server, publishedDataSet1) == UA_STATUSCODE_BADCONFIGURATIONERROR);
+        UA_Server_unfreezeWriterGroupConfiguration(server, writerGroup1);
+    fieldRemoveResult = UA_Server_removeDataSetField(server, localDataSetField);
+    ck_assert(fieldRemoveResult.result == UA_STATUSCODE_GOOD);
+    } END_TEST
+
+START_TEST(CreateConfigWithStaticFieldSource) {
+    UA_NodeId connection1, writerGroup1, publishedDataSet1, dataSetWriter1;
+    UA_PubSubConnectionConfig connectionConfig;
+    memset(&connectionConfig, 0, sizeof(UA_PubSubConnectionConfig));
+    connectionConfig.name = UA_STRING("UADP Connection");
+    UA_NetworkAddressUrlDataType networkAddressUrl = {UA_STRING_NULL, UA_STRING("opc.udp://224.0.0.22:4840/")};
+    UA_Variant_setScalar(&connectionConfig.address, &networkAddressUrl,
+                         &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]);
+    connectionConfig.transportProfileUri = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp");
+    UA_Server_addPubSubConnection(server, &connectionConfig, &connection1);
+
+    UA_WriterGroupConfig writerGroupConfig;
+    memset(&writerGroupConfig, 0, sizeof(writerGroupConfig));
+    writerGroupConfig.name = UA_STRING("WriterGroup 1");
+    writerGroupConfig.publishingInterval = 10;
+    writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP;
+    writerGroupConfig.rtLevel = UA_PUBSUB_RT_FIXED_SIZE;
+    UA_Server_addWriterGroup(server, connection1, &writerGroupConfig, &writerGroup1);
+
+    UA_PublishedDataSetConfig pdsConfig;
+    memset(&pdsConfig, 0, sizeof(UA_PublishedDataSetConfig));
+    pdsConfig.publishedDataSetType = UA_PUBSUB_DATASET_PUBLISHEDITEMS;
+    pdsConfig.name = UA_STRING("PublishedDataSet 1");
+    UA_Server_addPublishedDataSet(server, &pdsConfig, &publishedDataSet1);
+
+    UA_UInt32 *intValue = UA_UInt32_new();
+    UA_Variant variant;
+    memset(&variant, 0, sizeof(UA_Variant));
+    UA_Variant_setScalar(&variant, intValue, &UA_TYPES[UA_TYPES_UINT32]);
+    UA_DataValue staticValueSource;
+    memset(&staticValueSource, 0, sizeof(staticValueSource));
+    staticValueSource.value = variant;
+
+    UA_DataSetFieldConfig fieldConfig;
+    memset(&fieldConfig, 0, sizeof(UA_DataSetFieldConfig));
+    fieldConfig.dataSetFieldType = UA_PUBSUB_DATASETFIELD_VARIABLE;
+    fieldConfig.field.variable.fieldNameAlias = UA_STRING("field 1");
+    fieldConfig.field.variable.staticValueSourceEnabled = UA_TRUE;
+    fieldConfig.field.variable.staticValueSource.value = variant;
+    UA_NodeId localDataSetField;
+    UA_Server_addDataSetField(server, publishedDataSet1, &fieldConfig, &localDataSetField);
+
+    UA_DataSetWriterConfig dataSetWriterConfig;
+    memset(&dataSetWriterConfig, 0, sizeof(dataSetWriterConfig));
+    dataSetWriterConfig.name = UA_STRING("DataSetWriter 1");
+    UA_Server_addDataSetWriter(server, writerGroup1, publishedDataSet1, &dataSetWriterConfig, &dataSetWriter1);
+    UA_DataValue_deleteMembers(&staticValueSource);
+    } END_TEST
+
+int main(void) {
+    TCase *tc_lock_configuration = tcase_create("Create and Lock");
+    tcase_add_checked_fixture(tc_lock_configuration, setup, teardown);
+    tcase_add_test(tc_lock_configuration, CreateAndLockConfiguration);
+    tcase_add_test(tc_lock_configuration, CreateAndLockConfigurationWithExternalAPI);
+    tcase_add_test(tc_lock_configuration, CreateAndReleaseMultiplePDSLocks);
+    tcase_add_test(tc_lock_configuration, CreateLockAndEditConfiguration);
+    tcase_add_test(tc_lock_configuration, CreateConfigWithStaticFieldSource);
+
+    Suite *s = suite_create("PubSub RT configuration lock mechanism");
+    suite_add_tcase(s, tc_lock_configuration);
+
+    SRunner *sr = srunner_create(s);
+    srunner_set_fork_status(sr, CK_NOFORK);
+    srunner_run_all(sr,CK_NORMAL);
+    int number_failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+    return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}

+ 9 - 0
tests/pubsub/check_pubsub_informationmodel.c

@@ -68,6 +68,7 @@ static void addWriterGroup(UA_NodeId parentConnection, UA_String name, UA_Durati
     writerGroupConfig.publishingInterval = interval;
     writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP;
     UA_Server_addWriterGroup(server, parentConnection, &writerGroupConfig, assignedId);
+    UA_Server_setWriterGroupOperational(server, *assignedId);
 }
 
 static void addDataSetWriter(UA_NodeId parentWriterGroup, UA_NodeId connectedPDS, UA_String name, UA_NodeId *assignedId){
@@ -111,8 +112,11 @@ static void setupBasicPubSubConfiguration(void){
     addPublishedDataSet(UA_STRING("PublishedDataSet 1"), &publishedDataSet1);
     addPublishedDataSet(UA_STRING("PublishedDataSet 2"), &publishedDataSet2);
     addWriterGroup(connection1, UA_STRING("WriterGroup 1"), 10, &writerGroup1);
+    UA_Server_setWriterGroupOperational(server, writerGroup1);
     addWriterGroup(connection1, UA_STRING("WriterGroup 2"), 100, &writerGroup2);
+    UA_Server_setWriterGroupOperational(server, writerGroup2);
     addWriterGroup(connection2, UA_STRING("WriterGroup 3"), 1000, &writerGroup3);
+    UA_Server_setWriterGroupOperational(server, writerGroup3);
     addDataSetWriter(writerGroup1, publishedDataSet1, UA_STRING("DataSetWriter 1"), &dataSetWriter1);
     addDataSetWriter(writerGroup1, publishedDataSet2, UA_STRING("DataSetWriter 2"), &dataSetWriter2);
     addDataSetWriter(writerGroup2, publishedDataSet2, UA_STRING("DataSetWriter 3"), &dataSetWriter3);
@@ -175,6 +179,7 @@ START_TEST(AddSingleWriterGroupAndCheckInformationModelRepresentation){
     addPublishedDataSet(pdsName, &publishedDataSet1);
     UA_String wgName = UA_STRING("WriterGroup 1");
     addWriterGroup(connection1, wgName, 10, &writerGroup1);
+    UA_Server_setWriterGroupOperational(server, writerGroup1);
     UA_QualifiedName browseName;
     ck_assert_int_eq(UA_Server_readBrowseName(server, writerGroup1, &browseName), UA_STATUSCODE_GOOD);
     ck_assert_int_eq(UA_String_equal(&browseName.name, &wgName), UA_TRUE);
@@ -188,12 +193,14 @@ START_TEST(AddRemoveAddSingleWriterGroupAndCheckInformationModelRepresentation){
     addPublishedDataSet(pdsName, &publishedDataSet1);
     UA_String wgName = UA_STRING("WriterGroup 1");
     addWriterGroup(connection1, wgName, 10, &writerGroup1);
+    UA_Server_setWriterGroupOperational(server, writerGroup1);
     UA_QualifiedName browseName;
     UA_StatusCode retVal;
     ck_assert_int_eq(UA_Server_removeWriterGroup(server, writerGroup1), UA_STATUSCODE_GOOD);
     retVal = UA_Server_readBrowseName(server, writerGroup1, &browseName);
     ck_assert_int_eq(retVal, UA_STATUSCODE_BADNODEIDUNKNOWN);
     addWriterGroup(connection1, wgName, 10, &writerGroup1);
+    UA_Server_setWriterGroupOperational(server, writerGroup1);
     retVal = UA_Server_readBrowseName(server, writerGroup1, &browseName);
     ck_assert_int_eq(retVal, UA_STATUSCODE_GOOD);
     ck_assert_int_eq(UA_String_equal(&browseName.name, &wgName), UA_TRUE);
@@ -207,6 +214,7 @@ START_TEST(AddSingleDataSetWriterAndCheckInformationModelRepresentation){
     addPublishedDataSet(pdsName, &publishedDataSet1);
     UA_String wgName = UA_STRING("WriterGroup 1");
     addWriterGroup(connection1, wgName, 10, &writerGroup1);
+    UA_Server_setWriterGroupOperational(server, writerGroup1);
     UA_String dswName = UA_STRING("DataSetWriter 1");
     addDataSetWriter(writerGroup1, publishedDataSet1, dswName, &dataSetWriter1);
     UA_QualifiedName browseName;
@@ -222,6 +230,7 @@ START_TEST(AddRemoveAddSingleDataSetWriterAndCheckInformationModelRepresentation
     addPublishedDataSet(pdsName, &publishedDataSet1);
     UA_String wgName = UA_STRING("WriterGroup 1");
     addWriterGroup(connection1, wgName, 10, &writerGroup1);
+    UA_Server_setWriterGroupOperational(server, writerGroup1);
     UA_String dswName = UA_STRING("DataSetWriter 1");
     addDataSetWriter(writerGroup1, publishedDataSet1, dswName, &dataSetWriter1);
     UA_QualifiedName browseName;

+ 12 - 0
tests/pubsub/check_pubsub_publish.c

@@ -56,6 +56,7 @@ START_TEST(AddWriterGroupWithValidConfiguration){
         writerGroupConfig.publishingInterval = 10;
         UA_NodeId localWriterGroup;
         retVal = UA_Server_addWriterGroup(server, connection1, &writerGroupConfig, &localWriterGroup);
+        UA_Server_setWriterGroupOperational(server, localWriterGroup);
         ck_assert_int_eq(retVal, UA_STATUSCODE_GOOD);
         size_t writerGroupCount = 0;
         UA_WriterGroup *writerGroup;
@@ -63,6 +64,7 @@ START_TEST(AddWriterGroupWithValidConfiguration){
             writerGroupCount++;
         }
         ck_assert_int_eq(writerGroupCount, 1);
+        UA_Server_setWriterGroupDisabled(server, localWriterGroup);
     } END_TEST
 
 START_TEST(AddRemoveAddWriterGroupWithMinimalValidConfiguration){
@@ -73,6 +75,7 @@ START_TEST(AddRemoveAddWriterGroupWithMinimalValidConfiguration){
         writerGroupConfig.publishingInterval = 10;
         UA_NodeId localWriterGroup;
         retVal |= UA_Server_addWriterGroup(server, connection1, &writerGroupConfig, &localWriterGroup);
+        UA_Server_setWriterGroupOperational(server, localWriterGroup);
         ck_assert_int_eq(retVal, UA_STATUSCODE_GOOD);
         retVal |= UA_Server_removeWriterGroup(server, localWriterGroup);
         ck_assert_int_eq(retVal, UA_STATUSCODE_GOOD);
@@ -83,6 +86,7 @@ START_TEST(AddRemoveAddWriterGroupWithMinimalValidConfiguration){
         }
         ck_assert_int_eq(writerGroupCount, 0);
         retVal |= UA_Server_addWriterGroup(server, connection1, &writerGroupConfig, &localWriterGroup);
+        UA_Server_setWriterGroupOperational(server, localWriterGroup);
         ck_assert_int_eq(retVal, UA_STATUSCODE_GOOD);
         writerGroupCount = 0;
         LIST_FOREACH(writerGroup, &UA_PubSubConnection_findConnectionbyId(server, connection1)->writerGroups, listEntry){
@@ -90,6 +94,7 @@ START_TEST(AddRemoveAddWriterGroupWithMinimalValidConfiguration){
         }
         ck_assert_int_eq(writerGroupCount, 1);
         retVal |= UA_Server_addWriterGroup(server, connection1, &writerGroupConfig, &localWriterGroup);
+        UA_Server_setWriterGroupOperational(server, localWriterGroup);
         writerGroupCount = 0;
         LIST_FOREACH(writerGroup, &UA_PubSubConnection_findConnectionbyId(server, connection1)->writerGroups, listEntry){
             writerGroupCount++;
@@ -133,6 +138,7 @@ START_TEST(GetWriterGroupConfigurationAndCompareValues){
         writerGroupConfig.publishingInterval = 10;
         UA_NodeId localWriterGroup;
         retVal |= UA_Server_addWriterGroup(server, connection1, &writerGroupConfig, &localWriterGroup);
+        UA_Server_setWriterGroupOperational(server, localWriterGroup);
         UA_WriterGroupConfig writerGroupConfigCopy;
         retVal |= UA_Server_getWriterGroupConfig(server, localWriterGroup, &writerGroupConfigCopy);
         ck_assert_int_eq(retVal, UA_STATUSCODE_GOOD);
@@ -149,14 +155,17 @@ static void setupDataSetWriterTestEnvironment(void){
     writerGroupConfig.publishingInterval = 10;
     writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP;
     UA_Server_addWriterGroup(server, connection1, &writerGroupConfig, &writerGroup1);
+    UA_Server_setWriterGroupOperational(server, writerGroup1);
     writerGroupConfig.name = UA_STRING("WriterGroup 2");
     writerGroupConfig.publishingInterval = 50;
     writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP;
     UA_Server_addWriterGroup(server, connection2, &writerGroupConfig, &writerGroup2);
+    UA_Server_setWriterGroupOperational(server, writerGroup2);
     writerGroupConfig.name = UA_STRING("WriterGroup 3");
     writerGroupConfig.publishingInterval = 100;
     writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP;
     UA_Server_addWriterGroup(server, connection2, &writerGroupConfig, &writerGroup3);
+    UA_Server_setWriterGroupOperational(server, writerGroup3);
     UA_PublishedDataSetConfig pdsConfig;
     memset(&pdsConfig, 0, sizeof(UA_PublishedDataSetConfig));
     pdsConfig.publishedDataSetType = UA_PUBSUB_DATASET_PUBLISHEDITEMS;
@@ -358,14 +367,17 @@ START_TEST(SinglePublishDataSetField){
         writerGroupConfig.publishingInterval = 10;
         writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP;
         UA_Server_addWriterGroup(server, connection1, &writerGroupConfig, &writerGroup1);
+        UA_Server_setWriterGroupOperational(server, writerGroup1);
         writerGroupConfig.name = UA_STRING("WriterGroup 2");
         writerGroupConfig.publishingInterval = 50;
         writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP;
         UA_Server_addWriterGroup(server, connection2, &writerGroupConfig, &writerGroup2);
+        UA_Server_setWriterGroupOperational(server, writerGroup2);
         writerGroupConfig.name = UA_STRING("WriterGroup 3");
         writerGroupConfig.publishingInterval = 100;
         writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP;
         UA_Server_addWriterGroup(server, connection2, &writerGroupConfig, &writerGroup3);
+        UA_Server_setWriterGroupOperational(server, writerGroup3);
         UA_PublishedDataSetConfig pdsConfig;
         memset(&pdsConfig, 0, sizeof(UA_PublishedDataSetConfig));
         pdsConfig.publishedDataSetType = UA_PUBSUB_DATASET_PUBLISHEDITEMS;

+ 1 - 0
tests/pubsub/check_pubsub_publish_json.c

@@ -58,6 +58,7 @@ START_TEST(SinglePublishDataSetField){
         writerGroupConfig.publishingInterval = 10;
         writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_JSON;
         UA_Server_addWriterGroup(server, connection1, &writerGroupConfig, &writerGroup1);
+        UA_Server_setWriterGroupOperational(server, writerGroup1);
 
         UA_PublishedDataSetConfig pdsConfig;
         memset(&pdsConfig, 0, sizeof(UA_PublishedDataSetConfig));

+ 43 - 0
tests/pubsub/check_pubsub_publish_uadp.c

@@ -97,6 +97,7 @@ START_TEST(CheckNMandDSMcalculation){
     //maximum DSM in one NM = 10
     writerGroupConfig.maxEncapsulatedDataSetMessageCount = 10;
     UA_Server_addWriterGroup(server, connection1, &writerGroupConfig, &writerGroupIdent);
+    UA_Server_setWriterGroupOperational(server, writerGroupIdent);
     UA_UadpWriterGroupMessageDataType_delete(wgm);
 
     UA_DataSetWriterConfig dataSetWriterConfig;
@@ -180,10 +181,52 @@ START_TEST(CheckNMandDSMcalculation){
 
     } END_TEST
 
+START_TEST(CheckNMandDSMBufferCalculation){
+        UA_WriterGroupConfig writerGroupConfig;
+        memset(&writerGroupConfig, 0, sizeof(UA_WriterGroupConfig));
+        writerGroupConfig.name = UA_STRING("Demo WriterGroup");
+        writerGroupConfig.publishingInterval = 10;
+        writerGroupConfig.enabled = UA_FALSE;
+        writerGroupConfig.writerGroupId = 100;
+        writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP;
+        writerGroupConfig.rtLevel = UA_PUBSUB_RT_FIXED_SIZE;
+
+        UA_UadpWriterGroupMessageDataType *wgm = UA_UadpWriterGroupMessageDataType_new();
+        wgm->networkMessageContentMask = UA_UADPNETWORKMESSAGECONTENTMASK_PAYLOADHEADER;
+        writerGroupConfig.messageSettings.content.decoded.data = wgm;
+        writerGroupConfig.messageSettings.content.decoded.type =
+                &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE];
+        writerGroupConfig.messageSettings.encoding = UA_EXTENSIONOBJECT_DECODED;
+
+        //maximum DSM in one NM = 10
+        writerGroupConfig.maxEncapsulatedDataSetMessageCount = 10;
+        UA_Server_addWriterGroup(server, connection1, &writerGroupConfig, &writerGroupIdent);
+        UA_Server_setWriterGroupOperational(server, writerGroupIdent);
+        UA_UadpWriterGroupMessageDataType_delete(wgm);
+
+        UA_DataSetWriterConfig dataSetWriterConfig;
+        memset(&dataSetWriterConfig, 0, sizeof(UA_DataSetWriterConfig));
+        dataSetWriterConfig.name = UA_STRING("Test DataSetWriter");
+        dataSetWriterConfig.dataSetWriterId = 10;
+        dataSetWriterConfig.keyFrameCount = 1;
+        //add 10 dataSetWriter
+        for(UA_UInt16 i = 0; i < 10; i++){
+            dataSetWriterConfig.dataSetWriterId = (UA_UInt16) (dataSetWriterConfig.dataSetWriterId + 1);
+            UA_Server_addDataSetWriter(server, writerGroupIdent, publishedDataSetIdent,
+                                       &dataSetWriterConfig, &dataSetWriterIdent);
+        }
+
+        //TODO extend this test with the buffer and offset calculation of the uadp network layer
+        UA_Server_freezeWriterGroupConfiguration(server, writerGroupIdent);
+        UA_Server_unfreezeWriterGroupConfiguration(server, writerGroupIdent);
+
+    } END_TEST
+
 int main(void) {
     TCase *tc_add_pubsub_DSMandNMcalculation = tcase_create("PubSub NM and DSM");
     tcase_add_checked_fixture(tc_add_pubsub_DSMandNMcalculation, setup, teardown);
     tcase_add_test(tc_add_pubsub_DSMandNMcalculation, CheckNMandDSMcalculation);
+    tcase_add_test(tc_add_pubsub_DSMandNMcalculation, CheckNMandDSMBufferCalculation);
 
     Suite *s = suite_create("PubSub NM and DSM calculation");
     suite_add_tcase(s, tc_add_pubsub_DSMandNMcalculation);

+ 1 - 0
tests/pubsub/check_pubsub_publishspeed.c

@@ -45,6 +45,7 @@ static void setup(void) {
     writerGroupConfig.publishingInterval = 10;
     writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP;
     UA_Server_addWriterGroup(server, connection1, &writerGroupConfig, &writerGroup1);
+    UA_Server_setWriterGroupOperational(server, writerGroup1);
 
     UA_PublishedDataSetConfig pdsConfig;
     memset(&pdsConfig, 0, sizeof(UA_PublishedDataSetConfig));

+ 5 - 1
tests/pubsub/check_pubsub_subscribe.c

@@ -537,7 +537,7 @@ START_TEST(SinglePublishSubscribeDateTime) {
         writerGroupConfig.writerGroupId = WRITER_GROUP_ID;
         writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP;
         retVal |= UA_Server_addWriterGroup(server, connection_test, &writerGroupConfig, &writerGroup);
-        ck_assert_int_eq(retVal, UA_STATUSCODE_GOOD);
+        UA_Server_setWriterGroupOperational(server, writerGroup);
         /* DataSetWriter */
         UA_DataSetWriterConfig dataSetWriterConfig;
         memset(&dataSetWriterConfig, 0, sizeof(dataSetWriterConfig));
@@ -668,6 +668,7 @@ START_TEST(SinglePublishSubscribeInt32) {
                                                                   (UA_UadpNetworkMessageContentMask)UA_UADPNETWORKMESSAGECONTENTMASK_PAYLOADHEADER);
         writerGroupConfig.messageSettings.content.decoded.data = writerGroupMessage;
         retVal |= UA_Server_addWriterGroup(server, connection_test, &writerGroupConfig, &writerGroup);
+        UA_Server_setWriterGroupOperational(server, writerGroup);
         UA_UadpWriterGroupMessageDataType_delete(writerGroupMessage);
         ck_assert_int_eq(retVal, UA_STATUSCODE_GOOD);
 
@@ -842,6 +843,7 @@ START_TEST(SinglePublishSubscribeInt64) {
                                                                   (UA_UadpNetworkMessageContentMask)UA_UADPNETWORKMESSAGECONTENTMASK_PAYLOADHEADER);
         writerGroupConfig.messageSettings.content.decoded.data = writerGroupMessage;
         retVal |= UA_Server_addWriterGroup(server, connection_test, &writerGroupConfig, &writerGroup);
+        UA_Server_setWriterGroupOperational(server, writerGroup);
         UA_UadpWriterGroupMessageDataType_delete(writerGroupMessage);
         ck_assert_int_eq(retVal, UA_STATUSCODE_GOOD);
 
@@ -1017,6 +1019,7 @@ START_TEST(SinglePublishSubscribeBool) {
         writerGroupConfig.messageSettings.content.decoded.data = writerGroupMessage;
         retVal |= UA_Server_addWriterGroup(server, connection_test, &writerGroupConfig, &writerGroup);
         UA_UadpWriterGroupMessageDataType_delete(writerGroupMessage);
+        UA_Server_setWriterGroupOperational(server, writerGroup);
         ck_assert_int_eq(retVal, UA_STATUSCODE_GOOD);
 
         /* DataSetWriter */
@@ -1191,6 +1194,7 @@ START_TEST(SinglePublishSubscribewithValidIdentifiers) {
                                                                   (UA_UadpNetworkMessageContentMask)UA_UADPNETWORKMESSAGECONTENTMASK_PAYLOADHEADER);
         writerGroupConfig.messageSettings.content.decoded.data = writerGroupMessage;
         retVal |= UA_Server_addWriterGroup(server, connection_test, &writerGroupConfig, &writerGroup);
+        UA_Server_setWriterGroupOperational(server, writerGroup);
         UA_UadpWriterGroupMessageDataType_delete(writerGroupMessage);
         ck_assert_int_eq(retVal, UA_STATUSCODE_GOOD);