浏览代码

Merge pull request #757 from open62541/static_single_services

move locally usable services into the service sets; some cleanup
Julius Pfrommer 8 年之前
父节点
当前提交
bb47044a7f

+ 0 - 208
src/server/ua_server.c

@@ -220,36 +220,6 @@ addNodeInternalWithType(UA_Server *server, UA_Node *node, const UA_NodeId parent
     return res;
 }
 
-UA_StatusCode
-__UA_Server_addNode(UA_Server *server, const UA_NodeClass nodeClass,
-                    const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId,
-                    const UA_NodeId referenceTypeId, const UA_QualifiedName browseName,
-                    const UA_NodeId typeDefinition, const UA_NodeAttributes *attr,
-                    const UA_DataType *attributeType,
-                    UA_InstantiationCallback *instantiationCallback, UA_NodeId *outNewNodeId) {
-    UA_AddNodesItem item;
-    UA_AddNodesItem_init(&item);
-    item.parentNodeId.nodeId = parentNodeId;
-    item.referenceTypeId = referenceTypeId;
-    item.requestedNewNodeId.nodeId = requestedNewNodeId;
-    item.browseName = browseName;
-    item.nodeClass = nodeClass;
-    item.typeDefinition.nodeId = typeDefinition;
-    item.nodeAttributes = (UA_ExtensionObject){.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE,
-                                               .content.decoded = {attributeType, (void*)(uintptr_t)attr}};
-    UA_AddNodesResult result;
-    UA_AddNodesResult_init(&result);
-    UA_RCU_LOCK();
-    Service_AddNodes_single(server, &adminSession, &item, &result, instantiationCallback);
-    UA_RCU_UNLOCK();
-
-    if(outNewNodeId && result.statusCode == UA_STATUSCODE_GOOD)
-        *outNewNodeId = result.addedNodeId;
-    else
-        UA_NodeId_deleteMembers(&result.addedNodeId);
-    return result.statusCode;
-}
-
 /**********/
 /* Server */
 /**********/
@@ -1320,181 +1290,3 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
 
     return server;
 }
-
-UA_StatusCode
-__UA_Server_write(UA_Server *server, const UA_NodeId *nodeId,
-                  const UA_AttributeId attributeId, const UA_DataType *attr_type,
-                  const void *value) {
-    UA_WriteValue wvalue;
-    UA_WriteValue_init(&wvalue);
-    wvalue.nodeId = *nodeId;
-    wvalue.attributeId = attributeId;
-    if(attributeId != UA_ATTRIBUTEID_VALUE)
-        /* hacked cast. the target WriteValue is used as const anyway */
-        UA_Variant_setScalar(&wvalue.value.value, (void*)(uintptr_t)value, attr_type);
-    else {
-        if(attr_type != &UA_TYPES[UA_TYPES_VARIANT])
-            return UA_STATUSCODE_BADTYPEMISMATCH;
-        wvalue.value.value = *(const UA_Variant*)value;
-    }
-    wvalue.value.hasValue = true;
-    UA_RCU_LOCK();
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wvalue);
-    UA_RCU_UNLOCK();
-    return retval;
-}
-
-static UA_StatusCode
-setValueCallback(UA_Server *server, UA_Session *session, UA_VariableNode *node, UA_ValueCallback *callback) {
-    if(node->nodeClass != UA_NODECLASS_VARIABLE)
-        return UA_STATUSCODE_BADNODECLASSINVALID;
-    node->value.variant.callback = *callback;
-    return UA_STATUSCODE_GOOD;
-}
-
-UA_StatusCode UA_EXPORT
-UA_Server_setVariableNode_valueCallback(UA_Server *server, const UA_NodeId nodeId,
-                                        const UA_ValueCallback callback) {
-    UA_RCU_LOCK();
-    UA_StatusCode retval = UA_Server_editNode(server, &adminSession, &nodeId,
-                                              (UA_EditNodeCallback)setValueCallback, &callback);
-    UA_RCU_UNLOCK();
-    return retval;
-}
-
-static UA_StatusCode
-setDataSource(UA_Server *server, UA_Session *session,
-              UA_VariableNode* node, UA_DataSource *dataSource) {
-    if(node->nodeClass != UA_NODECLASS_VARIABLE)
-        return UA_STATUSCODE_BADNODECLASSINVALID;
-    if(node->valueSource == UA_VALUESOURCE_VARIANT)
-        UA_Variant_deleteMembers(&node->value.variant.value);
-    node->value.dataSource = *dataSource;
-    node->valueSource = UA_VALUESOURCE_DATASOURCE;
-    return UA_STATUSCODE_GOOD;
-}
-
-UA_StatusCode
-UA_Server_setVariableNode_dataSource(UA_Server *server, const UA_NodeId nodeId,
-                                     const UA_DataSource dataSource) {
-    UA_RCU_LOCK();
-    UA_StatusCode retval = UA_Server_editNode(server, &adminSession, &nodeId,
-                                              (UA_EditNodeCallback)setDataSource, &dataSource);
-    UA_RCU_UNLOCK();
-    return retval;
-}
-
-static UA_StatusCode
-setObjectTypeLifecycleManagement(UA_Server *server, UA_Session *session, UA_ObjectTypeNode* node,
-                                 UA_ObjectLifecycleManagement *olm) {
-    if(node->nodeClass != UA_NODECLASS_OBJECTTYPE)
-        return UA_STATUSCODE_BADNODECLASSINVALID;
-    node->lifecycleManagement = *olm;
-    return UA_STATUSCODE_GOOD;
-}
-
-UA_StatusCode UA_EXPORT
-UA_Server_setObjectTypeNode_lifecycleManagement(UA_Server *server, UA_NodeId nodeId,
-                                                UA_ObjectLifecycleManagement olm) {
-    UA_RCU_LOCK();
-    UA_StatusCode retval = UA_Server_editNode(server, &adminSession, &nodeId,
-                                              (UA_EditNodeCallback)setObjectTypeLifecycleManagement, &olm);
-    UA_RCU_UNLOCK();
-    return retval;
-}
-
-#ifdef UA_ENABLE_METHODCALLS
-
-struct addMethodCallback {
-    UA_MethodCallback callback;
-    void *handle;
-};
-
-static UA_StatusCode
-editMethodCallback(UA_Server *server, UA_Session* session, UA_Node* node, const void* handle) {
-    if(node->nodeClass != UA_NODECLASS_METHOD)
-        return UA_STATUSCODE_BADNODECLASSINVALID;
-    const struct addMethodCallback *newCallback = handle;
-    UA_MethodNode *mnode = (UA_MethodNode*) node;
-    mnode->attachedMethod = newCallback->callback;
-    mnode->methodHandle   = newCallback->handle;
-    return UA_STATUSCODE_GOOD;
-}
-
-UA_StatusCode UA_EXPORT
-UA_Server_setMethodNode_callback(UA_Server *server, const UA_NodeId methodNodeId,
-                                 UA_MethodCallback method, void *handle) {
-    struct addMethodCallback cb = { method, handle };
-    UA_RCU_LOCK();
-    UA_StatusCode retval = UA_Server_editNode(server, &adminSession, &methodNodeId, editMethodCallback, &cb);
-    UA_RCU_UNLOCK();
-    return retval;
-}
-
-#endif
-
-UA_StatusCode
-__UA_Server_read(UA_Server *server, const UA_NodeId *nodeId, const UA_AttributeId attributeId, void *v) {
-    UA_ReadValueId item;
-    UA_ReadValueId_init(&item);
-    item.nodeId = *nodeId;
-    item.attributeId = attributeId;
-    UA_DataValue dv;
-    UA_DataValue_init(&dv);
-    UA_RCU_LOCK();
-    Service_Read_single(server, &adminSession, UA_TIMESTAMPSTORETURN_NEITHER,
-                        &item, &dv);
-    UA_RCU_UNLOCK();
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    if(dv.hasStatus)
-        retval = dv.hasStatus;
-    else if(!dv.hasValue)
-        retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_DataValue_deleteMembers(&dv);
-        return retval;
-    }
-    if(attributeId == UA_ATTRIBUTEID_VALUE ||
-       attributeId == UA_ATTRIBUTEID_ARRAYDIMENSIONS)
-        memcpy(v, &dv.value, sizeof(UA_Variant));
-    else {
-        memcpy(v, dv.value.data, dv.value.type->memSize);
-        dv.value.data = NULL;
-        dv.value.arrayLength = 0;
-        UA_Variant_deleteMembers(&dv.value);
-    }
-    return UA_STATUSCODE_GOOD;
-}
-
-UA_BrowseResult
-UA_Server_browse(UA_Server *server, UA_UInt32 maxrefs, const UA_BrowseDescription *descr) {
-    UA_BrowseResult result;
-    UA_BrowseResult_init(&result);
-    UA_RCU_LOCK();
-    Service_Browse_single(server, &adminSession, NULL, descr, maxrefs, &result);
-    UA_RCU_UNLOCK();
-    return result;
-}
-
-UA_BrowseResult
-UA_Server_browseNext(UA_Server *server, UA_Boolean releaseContinuationPoint,
-                     const UA_ByteString *continuationPoint) {
-    UA_BrowseResult result;
-    UA_BrowseResult_init(&result);
-    UA_RCU_LOCK();
-    UA_Server_browseNext_single(server, &adminSession, releaseContinuationPoint,
-                                continuationPoint, &result);
-    UA_RCU_UNLOCK();
-    return result;
-}
-
-#ifdef UA_ENABLE_METHODCALLS
-UA_CallMethodResult UA_Server_call(UA_Server *server, const UA_CallMethodRequest *request) {
-    UA_CallMethodResult result;
-    UA_CallMethodResult_init(&result);
-    UA_RCU_LOCK();
-    Service_Call_single(server, &adminSession, request, &result);
-    UA_RCU_UNLOCK();
-    return result;
-}
-#endif

+ 0 - 10
src/server/ua_services.h

@@ -98,11 +98,6 @@ void Service_AddNodes(UA_Server *server, UA_Session *session,
                       const UA_AddNodesRequest *request,
                       UA_AddNodesResponse *response);
 
-void Service_AddNodes_single(UA_Server *server, UA_Session *session,
-                             const UA_AddNodesItem *item,
-                             UA_AddNodesResult *result,
-                             UA_InstantiationCallback *instantiationCallback);
-
 /* Add an existing node. The node is assumed to be "finished", i.e. no
  * instantiation from inheritance is necessary */
 void
@@ -166,11 +161,6 @@ void Service_BrowseNext(UA_Server *server, UA_Session *session,
                         const UA_BrowseNextRequest *request,
                         UA_BrowseNextResponse *response);
 
-void UA_Server_browseNext_single(UA_Server *server, UA_Session *session,
-                                 UA_Boolean releaseContinuationPoint,
-                                 const UA_ByteString *continuationPoint,
-                                 UA_BrowseResult *result);
-
 /* Used to translate textual node paths to their respective ids. */
 void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
              const UA_TranslateBrowsePathsToNodeIdsRequest *request,

+ 126 - 35
src/server/ua_services_attribute.c

@@ -56,7 +56,8 @@ UA_StatusCode parse_numericrange(const UA_String *str, UA_NumericRange *range) {
         /* alloc dimensions */
         if(idx >= dimensionsMax) {
             struct UA_NumericRangeDimension *newds;
-            newds = UA_realloc(dimensions, sizeof(struct UA_NumericRangeDimension) * (dimensionsMax + 2));
+            size_t newdssize = sizeof(struct UA_NumericRangeDimension) * (dimensionsMax + 2);
+            newds = UA_realloc(dimensions, newdssize);
             if(!newds) {
                 retval = UA_STATUSCODE_BADOUTOFMEMORY;
                 break;
@@ -66,7 +67,8 @@ UA_StatusCode parse_numericrange(const UA_String *str, UA_NumericRange *range) {
         }
 
         /* read the dimension */
-        size_t progress = readDimension(&str->data[offset], str->length - offset, &dimensions[idx]);
+        size_t progress = readDimension(&str->data[offset], str->length - offset,
+                                        &dimensions[idx]);
         if(progress == 0) {
             retval = UA_STATUSCODE_BADINDEXRANGEINVALID;
             break;
@@ -100,15 +102,19 @@ UA_StatusCode parse_numericrange(const UA_String *str, UA_NumericRange *range) {
         break;                                                  \
     }
 
-static void handleServerTimestamps(UA_TimestampsToReturn timestamps, UA_DataValue* v) {
-    if(v && (timestamps == UA_TIMESTAMPSTORETURN_SERVER || timestamps == UA_TIMESTAMPSTORETURN_BOTH)) {
+static void
+handleServerTimestamps(UA_TimestampsToReturn timestamps, UA_DataValue* v) {
+    if(v && (timestamps == UA_TIMESTAMPSTORETURN_SERVER ||
+             timestamps == UA_TIMESTAMPSTORETURN_BOTH)) {
         v->hasServerTimestamp = true;
         v->serverTimestamp = UA_DateTime_now();
     }
 }
 
-static void handleSourceTimestamps(UA_TimestampsToReturn timestamps, UA_DataValue* v) {
-    if(timestamps == UA_TIMESTAMPSTORETURN_SOURCE || timestamps == UA_TIMESTAMPSTORETURN_BOTH) {
+static void
+handleSourceTimestamps(UA_TimestampsToReturn timestamps, UA_DataValue* v) {
+    if(timestamps == UA_TIMESTAMPSTORETURN_SOURCE ||
+       timestamps == UA_TIMESTAMPSTORETURN_BOTH) {
         v->hasSourceTimestamp = true;
         v->sourceTimestamp = UA_DateTime_now();
     }
@@ -123,8 +129,10 @@ static void forceVariantSetScalar(UA_Variant *v, const void *p, const UA_DataTyp
 }
 
 static UA_StatusCode
-getVariableNodeValue(UA_Server *server, UA_Session *session, const UA_VariableNode *vn,
-                     const UA_TimestampsToReturn timestamps, const UA_ReadValueId *id, UA_DataValue *v) {
+getVariableNodeValue(UA_Server *server, UA_Session *session,
+                     const UA_VariableNode *vn,
+                     const UA_TimestampsToReturn timestamps,
+                     const UA_ReadValueId *id, UA_DataValue *v) {
     UA_NumericRange range;
     UA_NumericRange *rangeptr = NULL;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
@@ -148,7 +156,8 @@ getVariableNodeValue(UA_Server *server, UA_Session *session, const UA_VariableNo
             handleSourceTimestamps(timestamps, v);
     } else {
         if(!vn->value.dataSource.read) {
-            UA_LOG_DEBUG_SESSION(server->config.logger, session, "DataSource cannot be read in ReadRequest");
+            UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                                 "DataSource cannot be read in ReadRequest");
             retval = UA_STATUSCODE_BADINTERNALERROR;
         } else {
             UA_Boolean sourceTimeStamp = (timestamps == UA_TIMESTAMPSTORETURN_SOURCE ||
@@ -169,7 +178,8 @@ getVariableNodeDataType(UA_Server *server, UA_Session *session,
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
         if(vn->value.variant.value.type) {
-            forceVariantSetScalar(&v->value, &vn->value.variant.value.type->typeId, &UA_TYPES[UA_TYPES_NODEID]);
+            forceVariantSetScalar(&v->value, &vn->value.variant.value.type->typeId,
+                                  &UA_TYPES[UA_TYPES_NODEID]);
         } else {
             UA_NodeId nullid;
             UA_NodeId_init(&nullid);
@@ -178,17 +188,20 @@ getVariableNodeDataType(UA_Server *server, UA_Session *session,
     } else {
         /* Read from the datasource to see the data type */
         if(!vn->value.dataSource.read) {
-            UA_LOG_DEBUG_SESSION(server->config.logger, session, "DataSource cannot be read in ReadRequest");
+            UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                                 "DataSource cannot be read in ReadRequest");
             return UA_STATUSCODE_BADINTERNALERROR;
         }
         UA_DataValue val;
         UA_DataValue_init(&val);
         val.hasValue = false; // always assume we are not given a value by userspace
-        retval = vn->value.dataSource.read(vn->value.dataSource.handle, vn->nodeId, false, NULL, &val);
+        retval = vn->value.dataSource.read(vn->value.dataSource.handle,
+                                           vn->nodeId, false, NULL, &val);
         if(retval != UA_STATUSCODE_GOOD)
             return retval;
         if(val.hasValue && val.value.type)
-          retval = UA_Variant_setScalarCopy(&v->value, &val.value.type->typeId, &UA_TYPES[UA_TYPES_NODEID]);
+          retval = UA_Variant_setScalarCopy(&v->value, &val.value.type->typeId,
+                                            &UA_TYPES[UA_TYPES_NODEID]);
         UA_DataValue_deleteMembers(&val);
     }
     return retval;
@@ -200,21 +213,25 @@ getVariableNodeArrayDimensions(UA_Server *server, UA_Session *session,
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
         UA_Variant_setArray(&v->value, vn->value.variant.value.arrayDimensions,
-                            vn->value.variant.value.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]);
+                            vn->value.variant.value.arrayDimensionsSize,
+                            &UA_TYPES[UA_TYPES_INT32]);
         v->value.storageType = UA_VARIANT_DATA_NODELETE;
     } else {
         if(!vn->value.dataSource.read) {
-            UA_LOG_DEBUG_SESSION(server->config.logger, session, "DataSource cannot be read in ReadRequest");
+            UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                                 "DataSource cannot be read in ReadRequest");
             return UA_STATUSCODE_BADINTERNALERROR;
         }
         /* Read the datasource to see the array dimensions */
         UA_DataValue val;
         UA_DataValue_init(&val);
-        retval = vn->value.dataSource.read(vn->value.dataSource.handle, vn->nodeId, false, NULL, &val);
+        retval = vn->value.dataSource.read(vn->value.dataSource.handle,
+                                           vn->nodeId, false, NULL, &val);
         if(retval != UA_STATUSCODE_GOOD)
             return retval;
         retval = UA_Variant_setArrayCopy(&v->value, val.value.arrayDimensions,
-                                         val.value.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]);
+                                         val.value.arrayDimensionsSize,
+                                         &UA_TYPES[UA_TYPES_INT32]);
         UA_DataValue_deleteMembers(&val);
     }
     return retval;
@@ -228,7 +245,8 @@ static const UA_String binEncoding = {sizeof("DefaultBinary")-1, (UA_Byte*)"Defa
 void Service_Read_single(UA_Server *server, UA_Session *session,
                          const UA_TimestampsToReturn timestamps,
                          const UA_ReadValueId *id, UA_DataValue *v) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Read the attribute %i", id->attributeId);
+    UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                         "Read the attribute %i", id->attributeId);
     if(id->dataEncoding.name.length > 0 &&
        !UA_String_equal(&binEncoding, &id->dataEncoding.name)) {
            v->hasStatus = true;
@@ -250,7 +268,8 @@ void Service_Read_single(UA_Server *server, UA_Session *session,
         return;
     }
 
-    /* When setting the value fails in the switch, we get an error code and set hasValue to false */
+    /* When setting the value fails in the switch, we get an error code and set
+       hasValue to false */
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     v->hasValue = true;
     switch(id->attributeId) {
@@ -407,7 +426,8 @@ void Service_Read(UA_Server *server, UA_Session *session,
             continue;
         UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
         ens->readNodes(ens->ensHandle, &request->requestHeader, request->nodesToRead,
-                       indices, (UA_UInt32)indexSize, response->results, false, response->diagnosticInfos);
+                       indices, (UA_UInt32)indexSize, response->results, false,
+                       response->diagnosticInfos);
     }
 #endif
 
@@ -433,20 +453,24 @@ void Service_Read(UA_Server *server, UA_Session *session,
         UA_Variant_init(&variant);
 
         UA_DateTime* expireArray = NULL;
-        expireArray = UA_Array_new(request->nodesToReadSize, &UA_TYPES[UA_TYPES_DATETIME]);
+        expireArray = UA_Array_new(request->nodesToReadSize,
+                                   &UA_TYPES[UA_TYPES_DATETIME]);
         variant.data = expireArray;
 
         /* expires in 20 seconds */
         for(UA_UInt32 i = 0;i < response->resultsSize;i++) {
             expireArray[i] = UA_DateTime_now() + 20 * 100 * 1000 * 1000;
         }
-        UA_Variant_setArray(&variant, expireArray, request->nodesToReadSize, &UA_TYPES[UA_TYPES_DATETIME]);
+        UA_Variant_setArray(&variant, expireArray, request->nodesToReadSize,
+                            &UA_TYPES[UA_TYPES_DATETIME]);
 
         size_t offset = 0;
         UA_ByteString str;
-        UA_ByteString_allocBuffer(&str, UA_calcSizeBinary(&variant, &UA_TYPES[UA_TYPES_VARIANT]));
+        size_t strlength = UA_calcSizeBinary(&variant, &UA_TYPES[UA_TYPES_VARIANT]);
+        UA_ByteString_allocBuffer(&str, strlength);
         /* No chunking callback for the encoding */
-        UA_StatusCode retval = UA_encodeBinary(&variant, &UA_TYPES[UA_TYPES_VARIANT], NULL, NULL, &str, &offset);
+        UA_StatusCode retval = UA_encodeBinary(&variant, &UA_TYPES[UA_TYPES_VARIANT],
+                                               NULL, NULL, &str, &offset);
         UA_Array_delete(expireArray, request->nodesToReadSize, &UA_TYPES[UA_TYPES_DATETIME]);
         if(retval == UA_STATUSCODE_GOOD){
             additionalHeader.content.encoded.body.data = str.data;
@@ -457,12 +481,47 @@ void Service_Read(UA_Server *server, UA_Session *session,
 #endif
 }
 
+UA_StatusCode
+__UA_Server_read(UA_Server *server, const UA_NodeId *nodeId,
+                 const UA_AttributeId attributeId, void *v) {
+    UA_ReadValueId item;
+    UA_ReadValueId_init(&item);
+    item.nodeId = *nodeId;
+    item.attributeId = attributeId;
+    UA_DataValue dv;
+    UA_DataValue_init(&dv);
+    UA_RCU_LOCK();
+    Service_Read_single(server, &adminSession, UA_TIMESTAMPSTORETURN_NEITHER,
+                        &item, &dv);
+    UA_RCU_UNLOCK();
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    if(dv.hasStatus)
+        retval = dv.hasStatus;
+    else if(!dv.hasValue)
+        retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_DataValue_deleteMembers(&dv);
+        return retval;
+    }
+    if(attributeId == UA_ATTRIBUTEID_VALUE ||
+       attributeId == UA_ATTRIBUTEID_ARRAYDIMENSIONS)
+        memcpy(v, &dv.value, sizeof(UA_Variant));
+    else {
+        memcpy(v, dv.value.data, dv.value.type->memSize);
+        dv.value.data = NULL;
+        dv.value.arrayLength = 0;
+        UA_Variant_deleteMembers(&dv.value);
+    }
+    return UA_STATUSCODE_GOOD;
+}
+
 /*******************/
 /* Write Attribute */
 /*******************/
 
-UA_StatusCode UA_Server_editNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
-                                 UA_EditNodeCallback callback, const void *data) {
+UA_StatusCode UA_Server_editNode(UA_Server *server, UA_Session *session,
+                                 const UA_NodeId *nodeId, UA_EditNodeCallback callback,
+                                 const void *data) {
     UA_StatusCode retval;
     do {
 #ifndef UA_ENABLE_MULTITHREADING
@@ -502,10 +561,12 @@ UA_StatusCode UA_Server_editNode(UA_Server *server, UA_Session *session, const U
     }
 
 static UA_StatusCode
-Service_Write_single_ValueDataSource(UA_Server *server, UA_Session *session, const UA_VariableNode *node,
-                                     const UA_WriteValue *wvalue) {
+Service_Write_single_ValueDataSource(UA_Server *server, UA_Session *session,
+                                     const UA_VariableNode *node, const UA_WriteValue *wvalue)
+{
     UA_assert(wvalue->attributeId == UA_ATTRIBUTEID_VALUE);
-    UA_assert(node->nodeClass == UA_NODECLASS_VARIABLE || node->nodeClass == UA_NODECLASS_VARIABLETYPE);
+    UA_assert(node->nodeClass == UA_NODECLASS_VARIABLE ||
+              node->nodeClass == UA_NODECLASS_VARIABLETYPE);
     UA_assert(node->valueSource == UA_VALUESOURCE_DATASOURCE);
 
     if(node->value.dataSource.write == NULL)
@@ -546,7 +607,8 @@ static enum type_equivalence typeEquivalence(const UA_DataType *t) {
 static UA_StatusCode
 CopyValueIntoNode(UA_VariableNode *node, const UA_WriteValue *wvalue) {
     UA_assert(wvalue->attributeId == UA_ATTRIBUTEID_VALUE);
-    UA_assert(node->nodeClass == UA_NODECLASS_VARIABLE || node->nodeClass == UA_NODECLASS_VARIABLETYPE);
+    UA_assert(node->nodeClass == UA_NODECLASS_VARIABLE ||
+              node->nodeClass == UA_NODECLASS_VARIABLETYPE);
     UA_assert(node->valueSource == UA_VALUESOURCE_VARIANT);
 
     UA_Variant *oldV = &node->value.variant.value;
@@ -597,7 +659,8 @@ CopyValueIntoNode(UA_VariableNode *node, const UA_WriteValue *wvalue) {
         UA_Variant_deleteMembers(&node->value.variant.value);
         UA_Variant_copy(newV, &node->value.variant.value);
     } else
-        retval = UA_Variant_setRangeCopy(&node->value.variant.value, newV->data, newV->arrayLength, range);
+        retval = UA_Variant_setRangeCopy(&node->value.variant.value, newV->data,
+                                         newV->arrayLength, range);
     if(node->value.variant.callback.onWrite)
         node->value.variant.callback.onWrite(node->value.variant.callback.handle, node->nodeId,
                                              &node->value.variant.value, rangeptr);
@@ -678,7 +741,9 @@ CopyAttributeIntoNode(UA_Server *server, UA_Session *session,
         if(((const UA_VariableNode*)node)->valueSource == UA_VALUESOURCE_VARIANT)
             retval = CopyValueIntoNode((UA_VariableNode*)node, wvalue);
         else
-            retval = Service_Write_single_ValueDataSource(server, session, (const UA_VariableNode*)node, wvalue);
+            retval = Service_Write_single_ValueDataSource(server, session,
+                                                          (const UA_VariableNode*)node,
+                                                          wvalue);
         break;
     case UA_ATTRIBUTEID_ACCESSLEVEL:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
@@ -721,8 +786,10 @@ CopyAttributeIntoNode(UA_Server *server, UA_Session *session,
     return retval;
 }
 
-UA_StatusCode Service_Write_single(UA_Server *server, UA_Session *session, const UA_WriteValue *wvalue) {
-    return UA_Server_editNode(server, session, &wvalue->nodeId, (UA_EditNodeCallback)CopyAttributeIntoNode, wvalue);
+UA_StatusCode Service_Write_single(UA_Server *server, UA_Session *session,
+                                   const UA_WriteValue *wvalue) {
+    return UA_Server_editNode(server, session, &wvalue->nodeId,
+                              (UA_EditNodeCallback)CopyAttributeIntoNode, wvalue);
 }
 
 void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request,
@@ -733,7 +800,8 @@ void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest
         return;
     }
 
-    response->results = UA_Array_new(request->nodesToWriteSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
+    response->results = UA_Array_new(request->nodesToWriteSize,
+                                     &UA_TYPES[UA_TYPES_STATUSCODE]);
     if(!response->results) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
@@ -769,3 +837,26 @@ void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest
             response->results[i] = Service_Write_single(server, session, &request->nodesToWrite[i]);
     }
 }
+
+UA_StatusCode
+__UA_Server_write(UA_Server *server, const UA_NodeId *nodeId,
+                  const UA_AttributeId attributeId, const UA_DataType *attr_type,
+                  const void *value) {
+    UA_WriteValue wvalue;
+    UA_WriteValue_init(&wvalue);
+    wvalue.nodeId = *nodeId;
+    wvalue.attributeId = attributeId;
+    if(attributeId != UA_ATTRIBUTEID_VALUE)
+        /* hacked cast. the target WriteValue is used as const anyway */
+        UA_Variant_setScalar(&wvalue.value.value, (void*)(uintptr_t)value, attr_type);
+    else {
+        if(attr_type != &UA_TYPES[UA_TYPES_VARIANT])
+            return UA_STATUSCODE_BADTYPEMISMATCH;
+        wvalue.value.value = *(const UA_Variant*)value;
+    }
+    wvalue.value.hasValue = true;
+    UA_RCU_LOCK();
+    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wvalue);
+    UA_RCU_UNLOCK();
+    return retval;
+}

+ 140 - 3
src/server/ua_services_nodemanagement.c

@@ -124,6 +124,11 @@ isNodeInTree(UA_NodeStore *ns, const UA_NodeId *rootNode, const UA_NodeId *nodeT
 /* Add Node */
 /************/
 
+static void
+Service_AddNodes_single(UA_Server *server, UA_Session *session,
+                        const UA_AddNodesItem *item, UA_AddNodesResult *result,
+                        UA_InstantiationCallback *instantiationCallback);
+
 static UA_StatusCode
 instantiateVariableNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
                         const UA_NodeId *typeId, UA_InstantiationCallback *instantiationCallback);
@@ -663,9 +668,10 @@ dataTypeNodeFromAttributes(const UA_AddNodesItem *item,
     return (UA_Node*)dtnode;
 }
 
-void Service_AddNodes_single(UA_Server *server, UA_Session *session,
-                             const UA_AddNodesItem *item, UA_AddNodesResult *result,
-                             UA_InstantiationCallback *instantiationCallback) {
+static void
+Service_AddNodes_single(UA_Server *server, UA_Session *session,
+                        const UA_AddNodesItem *item, UA_AddNodesResult *result,
+                        UA_InstantiationCallback *instantiationCallback) {
     if(item->nodeAttributes.encoding < UA_EXTENSIONOBJECT_DECODED ||
        !item->nodeAttributes.content.decoded.type) {
         result->statusCode = UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
@@ -798,6 +804,41 @@ void Service_AddNodes(UA_Server *server, UA_Session *session,
     }
 }
 
+UA_StatusCode
+__UA_Server_addNode(UA_Server *server, const UA_NodeClass nodeClass,
+                    const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId,
+                    const UA_NodeId referenceTypeId, const UA_QualifiedName browseName,
+                    const UA_NodeId typeDefinition, const UA_NodeAttributes *attr,
+                    const UA_DataType *attributeType,
+                    UA_InstantiationCallback *instantiationCallback, UA_NodeId *outNewNodeId) {
+    /* prepare the item */
+    UA_AddNodesItem item;
+    UA_AddNodesItem_init(&item);
+    item.parentNodeId.nodeId = parentNodeId;
+    item.referenceTypeId = referenceTypeId;
+    item.requestedNewNodeId.nodeId = requestedNewNodeId;
+    item.browseName = browseName;
+    item.nodeClass = nodeClass;
+    item.typeDefinition.nodeId = typeDefinition;
+    item.nodeAttributes = (UA_ExtensionObject){
+        .encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE,
+        .content.decoded = {attributeType, (void*)(uintptr_t)attr}};
+
+    /* run the service */
+    UA_AddNodesResult result;
+    UA_AddNodesResult_init(&result);
+    UA_RCU_LOCK();
+    Service_AddNodes_single(server, &adminSession, &item, &result, instantiationCallback);
+    UA_RCU_UNLOCK();
+
+    /* prepare the output */
+    if(outNewNodeId && result.statusCode == UA_STATUSCODE_GOOD)
+        *outNewNodeId = result.addedNodeId;
+    else
+        UA_NodeId_deleteMembers(&result.addedNodeId);
+    return result.statusCode;
+}
+
 /**************************************************/
 /* Add Special Nodes (not possible over the wire) */
 /**************************************************/
@@ -1262,3 +1303,99 @@ Service_DeleteReferences(UA_Server *server, UA_Session *session,
         response->results[i] =
             Service_DeleteReferences_single(server, session, &request->referencesToDelete[i]);
 }
+
+/***********************/
+/* Edit Node Callbacks */
+/***********************/
+
+static UA_StatusCode
+setValueCallback(UA_Server *server, UA_Session *session,
+                 UA_VariableNode *node, UA_ValueCallback *callback) {
+    if(node->nodeClass != UA_NODECLASS_VARIABLE)
+        return UA_STATUSCODE_BADNODECLASSINVALID;
+    node->value.variant.callback = *callback;
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_StatusCode UA_EXPORT
+UA_Server_setVariableNode_valueCallback(UA_Server *server, const UA_NodeId nodeId,
+                                        const UA_ValueCallback callback) {
+    UA_RCU_LOCK();
+    UA_StatusCode retval = UA_Server_editNode(server, &adminSession, &nodeId,
+                                              (UA_EditNodeCallback)setValueCallback, &callback);
+    UA_RCU_UNLOCK();
+    return retval;
+}
+
+static UA_StatusCode
+setDataSource(UA_Server *server, UA_Session *session,
+              UA_VariableNode* node, UA_DataSource *dataSource) {
+    if(node->nodeClass != UA_NODECLASS_VARIABLE)
+        return UA_STATUSCODE_BADNODECLASSINVALID;
+    if(node->valueSource == UA_VALUESOURCE_VARIANT)
+        UA_Variant_deleteMembers(&node->value.variant.value);
+    node->value.dataSource = *dataSource;
+    node->valueSource = UA_VALUESOURCE_DATASOURCE;
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_StatusCode
+UA_Server_setVariableNode_dataSource(UA_Server *server, const UA_NodeId nodeId,
+                                     const UA_DataSource dataSource) {
+    UA_RCU_LOCK();
+    UA_StatusCode retval = UA_Server_editNode(server, &adminSession, &nodeId,
+                                              (UA_EditNodeCallback)setDataSource, &dataSource);
+    UA_RCU_UNLOCK();
+    return retval;
+}
+
+static UA_StatusCode
+setObjectTypeLifecycleManagement(UA_Server *server, UA_Session *session,
+                                 UA_ObjectTypeNode* node,
+                                 UA_ObjectLifecycleManagement *olm) {
+    if(node->nodeClass != UA_NODECLASS_OBJECTTYPE)
+        return UA_STATUSCODE_BADNODECLASSINVALID;
+    node->lifecycleManagement = *olm;
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_StatusCode UA_EXPORT
+UA_Server_setObjectTypeNode_lifecycleManagement(UA_Server *server, UA_NodeId nodeId,
+                                                UA_ObjectLifecycleManagement olm) {
+    UA_RCU_LOCK();
+    UA_StatusCode retval = UA_Server_editNode(server, &adminSession, &nodeId,
+                                              (UA_EditNodeCallback)setObjectTypeLifecycleManagement, &olm);
+    UA_RCU_UNLOCK();
+    return retval;
+}
+
+#ifdef UA_ENABLE_METHODCALLS
+
+struct addMethodCallback {
+    UA_MethodCallback callback;
+    void *handle;
+};
+
+static UA_StatusCode
+editMethodCallback(UA_Server *server, UA_Session* session, UA_Node* node, const void* handle) {
+    if(node->nodeClass != UA_NODECLASS_METHOD)
+        return UA_STATUSCODE_BADNODECLASSINVALID;
+    const struct addMethodCallback *newCallback = handle;
+    UA_MethodNode *mnode = (UA_MethodNode*) node;
+    mnode->attachedMethod = newCallback->callback;
+    mnode->methodHandle   = newCallback->handle;
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_StatusCode UA_EXPORT
+UA_Server_setMethodNode_callback(UA_Server *server, const UA_NodeId methodNodeId,
+                                 UA_MethodCallback method, void *handle) {
+    struct addMethodCallback cb = { method, handle };
+    UA_RCU_LOCK();
+    UA_StatusCode retval = UA_Server_editNode(server, &adminSession,
+                                              &methodNodeId, editMethodCallback, &cb);
+    UA_RCU_UNLOCK();
+    return retval;
+}
+
+#endif

+ 32 - 6
src/server/ua_services_view.c

@@ -208,8 +208,9 @@ static void removeCp(struct ContinuationPointEntry *cp, UA_Session* session) {
  * @param result The entry in the request
  */
 void
-Service_Browse_single(UA_Server *server, UA_Session *session, struct ContinuationPointEntry *cp,
-                      const UA_BrowseDescription *descr, UA_UInt32 maxrefs, UA_BrowseResult *result) { 
+Service_Browse_single(UA_Server *server, UA_Session *session,
+                      struct ContinuationPointEntry *cp, const UA_BrowseDescription *descr,
+                      UA_UInt32 maxrefs, UA_BrowseResult *result) { 
     size_t referencesCount = 0;
     size_t referencesIndex = 0;
     /* set the browsedescription if a cp is given */
@@ -295,8 +296,10 @@ Service_Browse_single(UA_Server *server, UA_Session *session, struct Continuatio
         if(skipped < continuationIndex) {
             skipped++;
         } else {
-            retval |= fillReferenceDescription(server->nodestore, current, &node->references[referencesIndex],
-                                               descr->resultMask, &result->references[referencesCount]);
+            retval |= fillReferenceDescription(server->nodestore, current,
+                                               &node->references[referencesIndex],
+                                               descr->resultMask,
+                                               &result->references[referencesCount]);
             referencesCount++;
         }
     }
@@ -308,7 +311,8 @@ Service_Browse_single(UA_Server *server, UA_Session *session, struct Continuatio
     }
 
     if(retval != UA_STATUSCODE_GOOD) {
-        UA_Array_delete(result->references, result->referencesSize, &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION]);
+        UA_Array_delete(result->references, result->referencesSize,
+                        &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION]);
         result->references = NULL;
         result->referencesSize = 0;
         result->statusCode = retval;
@@ -409,7 +413,17 @@ void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseReque
     }
 }
 
-void
+UA_BrowseResult
+UA_Server_browse(UA_Server *server, UA_UInt32 maxrefs, const UA_BrowseDescription *descr) {
+    UA_BrowseResult result;
+    UA_BrowseResult_init(&result);
+    UA_RCU_LOCK();
+    Service_Browse_single(server, &adminSession, NULL, descr, maxrefs, &result);
+    UA_RCU_UNLOCK();
+    return result;
+}
+
+static void
 UA_Server_browseNext_single(UA_Server *server, UA_Session *session, UA_Boolean releaseContinuationPoint,
                             const UA_ByteString *continuationPoint, UA_BrowseResult *result) {
     result->statusCode = UA_STATUSCODE_BADCONTINUATIONPOINTINVALID;
@@ -446,6 +460,18 @@ void Service_BrowseNext(UA_Server *server, UA_Session *session, const UA_BrowseN
                                     &request->continuationPoints[i], &response->results[i]);
 }
 
+UA_BrowseResult
+UA_Server_browseNext(UA_Server *server, UA_Boolean releaseContinuationPoint,
+                     const UA_ByteString *continuationPoint) {
+    UA_BrowseResult result;
+    UA_BrowseResult_init(&result);
+    UA_RCU_LOCK();
+    UA_Server_browseNext_single(server, &adminSession, releaseContinuationPoint,
+                                continuationPoint, &result);
+    UA_RCU_UNLOCK();
+    return result;
+}
+
 /***********************/
 /* TranslateBrowsePath */
 /***********************/

+ 2 - 1
src/server/ua_subscription.c

@@ -67,7 +67,8 @@ void UA_MoniteredItem_SampleCallback(UA_Server *server, UA_MonitoredItem *monito
     rvid.nodeId = monitoredItem->monitoredNodeId;
     rvid.attributeId = monitoredItem->attributeID;
     rvid.indexRange = monitoredItem->indexRange;
-    Service_Read_single(server, sub->session, monitoredItem->timestampsToReturn, &rvid, &newvalue->value);
+    Service_Read_single(server, sub->session, monitoredItem->timestampsToReturn,
+                        &rvid, &newvalue->value);
 
     /* encode to see if the data has changed */
     size_t binsize = UA_calcSizeBinary(&newvalue->value.value, &UA_TYPES[UA_TYPES_VARIANT]);