# Edit the address space

> How-to guide: Create and delete nodes and references in the address space of a server from the client.

> This content was machine translated and may contain errors. [Read in German](https://www.traeger.de/docs/development/opcua/client/address-space)

**Sample applications:**

- Client.ClientAddedNodesWithHistory ([C#](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/cs/Scenarios/Client.ClientAddedNodesWithHistory), [VB.NET](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/vb/Scenarios/Client.ClientAddedNodesWithHistory)): This use case demonstrates how a client adds a variable node with history access to the server and writes values that the server records as history.
- Server.ClientAddedNodesWithHistory ([C#](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/cs/Scenarios/Server.ClientAddedNodesWithHistory), [VB.NET](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/vb/Scenarios/Server.ClientAddedNodesWithHistory)): This use case implementes the necessary logic required to support nodes which are added through a client application which wants the server to enable historical data.

## Create nodes

Types used: `OpcClient`, `OpcAddNode`, `OpcAddNodeResult`, `OpcAddNodeResultCollection`, `OpcAddDataItemNode`, `OpcAddAnalogItemNode`, `OpcAddObjectNode`, `OpcAddFolderNode`, `OpcAddDataVariableNode`, `OpcAddPropertyNode`

A server provides its clients with a predefined set of standard nodes. A client can make the server provide additional nodes. For this, the client uses the `AddNodes` interface of the server. For type-specific nodes, the SDK provides many subclasses of the `OpcAddNode` class. To create a new folder node:

```csharp
OpcAddNodeResult result = client.AddNode(new OpcAddFolderNode(
    name: "Jobs",
    nodeId: OpcNodeId.Null,
    parentNodeId: "ns=2;s=Machine"));

```

```vbnet
Dim result As OpcAddNodeResult = client.AddNode(New OpcAddFolderNode( _
        name:="Jobs", _
        nodeId:=OpcNodeId.Null, _
        parentNodeId:="ns=2;s=Machine"))

```

The parameters in the example contain the minimum required information. The first parameter, `name`, sets the name (property `Name`, the BrowseName of the node) and the display name (property `DisplayName`) of the node. To set a description, use the `Description` property. The second parameter, `nodeId`, tells the server which identifier to assign to the node. If another node in the address space of the server already uses this identifier, the server does not create the node. A server developed with the SDK then returns the code `BadNodeIdRejected`; other servers might return `BadNodeIdExists`. If you pass `OpcNodeId.Null` as in the example, the server generates a new identifier itself and assigns it to the node. Finally, the `parentNodeId` parameter sets the identifier of the parent node under which the server creates the new node in the tree. If you do not specify a parent node, the server creates the node below the ObjectsFolder.

The `AddNode` method returns an instance of the `OpcAddNodeResult` class. Besides the outcome of the operation, it contains the identifier the server actually used for the new node:

```csharp
if (result.IsGood)
    Console.WriteLine("NodeId of 'Jobs': {0}", result.NodeId);
else
    Console.WriteLine("Failed to add node: {0}", result.Description);

```

```vbnet
If result.IsGood Then
    Console.WriteLine("NodeId of 'Jobs': {0}", result.NodeId)
Else
    Console.WriteLine("Failed to add node: {0}", result.Description)
End If
```

To create multiple nodes at once:

```csharp
OpcNodeId jobsNodeId = result.NodeId;
 
OpcAddNodeResultCollection results = client.AddNodes(
        new OpcAddDataVariableNode<string>("CurrentJob", jobsNodeId),
        new OpcAddDataVariableNode<string>("NextJob", jobsNodeId),
        new OpcAddDataVariableNode<int>("NumberOfJobs", jobsNodeId));

```

```vbnet
Dim jobsNodeId As OpcNodeId = result.NodeId

Dim results As OpcAddNodeResultCollection = client.AddNodes( _
        New OpcAddDataVariableNode(Of String)("CurrentJob", jobsNodeId), _
        New OpcAddDataVariableNode(Of String)("NextJob", jobsNodeId), _
        New OpcAddDataVariableNode(Of Integer)("NumberOfJobs", jobsNodeId))

```

The `AddNodes` method returns an instance of the `OpcAddNodeResultCollection` class. It contains instances of `OpcAddNodeResult`, which you evaluate and process as described above.

You can also pass entire trees of nodes to the `AddNode` and `AddNodes` methods:

```csharp
OpcAddNodeResultCollection results = client.AddNodes(
        new OpcAddObjectNode(
                "JOB001",
                nodeId: OpcNodeId.Null,
                parentNodeId: jobsNodeId,
                new OpcAddDataVariableNode<sbyte>("Status", -1),
                new OpcAddDataVariableNode<string>("Serial", "J01-DX-11.001"),
                new OpcAddAnalogItemNode<float>("Speed", 1200f) {
                    EngineeringUnit = new OpcEngineeringUnitInfo(5067859, "m/s", "metre per second"),
                    EngineeringUnitRange = new OpcValueRange(5400, 0),
                    Definition = "DB100.DBW 0"
                },
                new OpcAddObjectNode(
                        "Setup",
                        new OpcAddPropertyNode<bool>("UseCutter"),
                        new OpcAddPropertyNode<bool>("UseDrill")),
                new OpcAddObjectNode(
                        "Schedule",
                        new OpcAddPropertyNode<DateTime>("EarliestStartTime"),
                        new OpcAddPropertyNode<DateTime>("LatestStartTime"),
                        new OpcAddPropertyNode<TimeSpan>("EstimatedRunTime"))),
        new OpcAddObjectNode(
                "JOB002",
                nodeId: OpcNodeId.Null,
                parentNodeId: jobsNodeId,
                new OpcAddDataVariableNode<sbyte>("Status", -1),
                new OpcAddDataVariableNode<string>("Serial", "J01-DX-53.002"),
                new OpcAddAnalogItemNode<float>("Speed", 3210f) {
                    EngineeringUnit = new OpcEngineeringUnitInfo(5067859, "m/s", "metre per second"),
                    EngineeringUnitRange = new OpcValueRange(5400, 0),
                    Definition = "DB200.DBW 0"
                },
                new OpcAddObjectNode(
                        "Setup",
                        new OpcAddPropertyNode<bool>("UseCutter"),
                        new OpcAddPropertyNode<bool>("UseDrill")),
                new OpcAddObjectNode(
                        "Schedule",
                        new OpcAddPropertyNode<DateTime>("EarliestStartTime"),
                        new OpcAddPropertyNode<DateTime>("LatestStartTime"),
                        new OpcAddPropertyNode<TimeSpan>("EstimatedRunTime"))));

```

```vbnet
Dim results As OpcAddNodeResultCollection = client.AddNodes( _
        New OpcAddObjectNode( _
                "JOB001", _
                nodeId:=OpcNodeId.Null, _
                parentNodeId:=jobsNodeId, _
                New OpcAddDataVariableNode(Of SByte)("Status", -1), _
                New OpcAddDataVariableNode(Of String)("Serial", "J01-DX-11.001"), _
                New OpcAddAnalogItemNode(Of Single)("Speed", 1200F) With {
                    .EngineeringUnit = New OpcEngineeringUnitInfo(5067859, "m/s", "metre per second"),
                    .EngineeringUnitRange = New OpcValueRange(5400, 0),
                    .Definition = "DB100.DBW 0"
                }, _
                New OpcAddObjectNode( _
                        "Setup", _
                        New OpcAddPropertyNode(Of Boolean)("UseCutter"), _
                        New OpcAddPropertyNode(Of Boolean)("UseDrill")), _
                New OpcAddObjectNode( _
                        "Schedule", _
                        New OpcAddPropertyNode(Of DateTime)("EarliestStartTime"), _
                        New OpcAddPropertyNode(Of DateTime)("LatestStartTime"), _
                        New OpcAddPropertyNode(Of TimeSpan)("EstimatedRunTime"))), _
        New OpcAddObjectNode( _
                "JOB002", _
                nodeId:=OpcNodeId.Null, _
                parentNodeId:=jobsNodeId, _
                New OpcAddDataVariableNode(Of SByte)("Status", -1), _
                New OpcAddDataVariableNode(Of String)("Serial", "J01-DX-53.002"), _
                New OpcAddAnalogItemNode(Of Single)("Speed", 3210F) With {
                    .EngineeringUnit = New OpcEngineeringUnitInfo(5067859, "m/s", "metre per second"),
                    .EngineeringUnitRange = New OpcValueRange(5400, 0),
                    .Definition = "DB200.DBW 0" _
                }, _
                New OpcAddObjectNode( _
                        "Setup", _
                        New OpcAddPropertyNode(Of Boolean)("UseCutter"), _
                        New OpcAddPropertyNode(Of Boolean)("UseDrill")), _
                New OpcAddObjectNode( _
                        "Schedule", _
                        New OpcAddPropertyNode(Of DateTime)("EarliestStartTime"), _
                        New OpcAddPropertyNode(Of DateTime)("LatestStartTime"), _
                        New OpcAddPropertyNode(Of TimeSpan)("EstimatedRunTime"))))

```

You can also build such a tree through the corresponding properties:

```csharp
var jobsNodeId = result.NodeId;
 
var job = new OpcAddObjectNode(
        name: "JOB003",
        nodeId: OpcNodeId.Null,
        parentNodeId: jobsNodeId);
 
job.Children.Add(new OpcAddDataVariableNode<sbyte>("Status", -1));
job.Children.Add(new OpcAddDataVariableNode<string>("Serial", "J01-DX-78.003"));
job.Children.Add(new OpcAddAnalogItemNode<float>("Speed", 1200f) {
    EngineeringUnit = new OpcEngineeringUnitInfo(5067859, "m/s", "metre per second"),
    EngineeringUnitRange = new OpcValueRange(5400, 0),
    Definition = "DB100.DBW 0"
});
 
var setup = new OpcAddObjectNode("Setup");
setup.Children.Add(new OpcAddPropertyNode<bool>("UseCutter"));
setup.Children.Add(new OpcAddPropertyNode<bool>("UseDrill"));
 
job.Children.Add(setup);
 
var schedule = new OpcAddObjectNode("Schedule");
schedule.Children.Add(new OpcAddPropertyNode<DateTime>("EarliestStartTime"));
schedule.Children.Add(new OpcAddPropertyNode<DateTime>("LatestStartTime"));
schedule.Children.Add(new OpcAddPropertyNode<TimeSpan>("EstimatedRunTime"));
 
job.Children.Add(schedule);
 
OpcAddNodeResult result = client.AddNode(job);

```

```vbnet
Dim jobsNodeId = result.NodeId
 
Dim job = New OpcAddObjectNode( _
        name:="JOB003", _
        nodeId:=OpcNodeId.Null, _
        parentNodeId:=jobsNodeId)
 
job.Children.Add(New OpcAddDataVariableNode(Of SByte)("Status", -1))
job.Children.Add(New OpcAddDataVariableNode(Of String)("Serial", "J01-DX-78.003"))
job.Children.Add(new OpcAddAnalogItemNode(Of Single)("Speed", 1200f) With {
    .EngineeringUnit = new OpcEngineeringUnitInfo(5067859, "m/s", "metre per second"),
    .EngineeringUnitRange = new OpcValueRange(5400, 0),
    .Definition = "DB100.DBW 0" _
})
 
Dim setup = New OpcAddObjectNode("Setup")
setup.Children.Add(New OpcAddPropertyNode(Of Boolean)("UseCutter"))
setup.Children.Add(New OpcAddPropertyNode(Of Boolean)("UseDrill"))

job.Children.Add(setup)

Dim schedule = New OpcAddObjectNode("Schedule")
schedule.Children.Add(New OpcAddPropertyNode(Of DateTime)("EarliestStartTime"))
schedule.Children.Add(New OpcAddPropertyNode(Of DateTime)("LatestStartTime"))
schedule.Children.Add(New OpcAddPropertyNode(Of TimeSpan)("EstimatedRunTime"))

job.Children.Add(schedule)

Dim result As OpcAddNodeResult = client.AddNode(job)

```

To use type definitions other than those of the `OpcAddNode` subclasses, create object and variable nodes based on their type definition:

```csharp
client.AddObjectNode(OpcObjectType.DeviceFailureEventType, "FailureInfo");
client.AddVariableNode(OpcVariableType.XYArrayItem, "Coordinates");

```

```vbnet
client.AddObjectNode(OpcObjectType.DeviceFailureEventType, "FailureInfo")
client.AddVariableNode(OpcVariableType.XYArrayItem, "Coordinates")

```

Besides the type definitions defined by the OPC Foundation, you can also create nodes based on the identifier of their type definition. To do this, declare the type in advance through an object-specific or variable-specific type definition. `OpcNodeId.Parse` creates the identifier of the type definition from its string. You can use this identifier as often as you like to create more nodes of this type:

```csharp
// Declare Job Type
var jobType = OpcAddObjectNode.OfType(OpcNodeId.Parse("ns=2;s=Types/JobType"));
 
client.AddNodes(
        jobType.Create("JOB001", nodeId: OpcNodeId.Null, parentNodeId: jobsNodeId),
        jobType.Create("JOB002", nodeId: OpcNodeId.Null, parentNodeId: jobsNodeId),
        jobType.Create("JOB003", nodeId: OpcNodeId.Null, parentNodeId: jobsNodeId),
        jobType.Create("JOB004", nodeId: OpcNodeId.Null, parentNodeId: jobsNodeId));
 
var scheduleNodeId = OpcNodeId.Parse("ns=2;s=Machine/JOB002/Schedule");
 
// Declare Shift Time Type
var shiftTimeType = OpcAddVariableNode.OfType(OpcNodeId.Parse("ns=2;s=Types/ShiftTimeType"));
 
OpcAddNodeResult result = client.AddNode(new OpcAddObjectNode(
        "ShiftPlanning",
        nodeId: OpcNodeId.Null,
        parentNodeId: scheduleNodeId,
        shiftTimeType.Create("Early"),
        shiftTimeType.Create("Noon"),
        shiftTimeType.Create("Late")));
```

```vbnet
'Declare Job Type
Dim jobType = OpcAddObjectNode.OfType(OpcNodeId.Parse("ns=2;s=Types/JobType"))

client.AddNodes( _
        jobType.Create("JOB001", nodeId:=OpcNodeId.Null, parentNodeId:=jobsNodeId), _
        jobType.Create("JOB002", nodeId:=OpcNodeId.Null, parentNodeId:=jobsNodeId), _
        jobType.Create("JOB003", nodeId:=OpcNodeId.Null, parentNodeId:=jobsNodeId), _
        jobType.Create("JOB004", nodeId:=OpcNodeId.Null, parentNodeId:=jobsNodeId))

Dim scheduleNodeId = OpcNodeId.Parse("ns=2;s=Machine/JOB002/Schedule")

'Declare Shift Time Type
Dim shiftTimeType = OpcAddVariableNode.OfType(OpcNodeId.Parse("ns=2;s=Types/ShiftTimeType"))

Dim result As OpcAddNodeResult = client.AddNode(New OpcAddObjectNode( _
        "ShiftPlanning", _
        nodeId:=OpcNodeId.Null, _
        parentNodeId:=scheduleNodeId, _
        shiftTimeType.Create("Early"), _
        shiftTimeType.Create("Noon"), _
        shiftTimeType.Create("Late")))
```

## Delete nodes

Types used: `OpcClient`, `OpcDeleteNode`, `OpcStatus`, `OpcStatusCollection`

Through the `DeleteNodes` interface of the server, you can delete both nodes provided by the server and nodes created by a client. Above all, you need the identifier of the node you want to delete:

```csharp
OpcStatus result = client.DeleteNode("ns=2;s=Machine/Jobs");

```

```vbnet
Dim result As OpcStatus = client.DeleteNode("ns=2;s=Machine/Jobs")

```

The example above deletes the node with an instance of the `OpcDeleteNode` class. By default, it also deletes all references that point to the node. To keep the references to the node, also set the `includeTargetReferences` parameter to `false`:

```csharp
OpcStatus result = client.DeleteNode(
        "ns=2;s=Machine/Jobs",
        includeTargetReferences: false);

```

```vbnet
Dim result As OpcStatus = client.DeleteNode( _
        "ns=2;s=Machine/Jobs", _
        includeTargetReferences:=False)

```

To delete multiple nodes at once:

```csharp
OpcStatusCollection results = client.DeleteNodes(
        new OpcDeleteNode("ns=2;s=Machine/Jobs/JOB001"),
        new OpcDeleteNode("ns=2;s=Machine/Jobs/JOB002"),
        new OpcDeleteNode("ns=2;s=Machine/Jobs/JOB003"));

```

```vbnet
Dim results As OpcStatusCollection = client.DeleteNodes( _
        New OpcDeleteNode("ns=2;s=Machine/Jobs/JOB001"), _
        New OpcDeleteNode("ns=2;s=Machine/Jobs/JOB002"), _
        New OpcDeleteNode("ns=2;s=Machine/Jobs/JOB003"))

```

## Create references

Types used: `OpcClient`, `OpcAddReference`, `OpcStatus`, `OpcStatusCollection`

Nodes in the address space of a server can relate to each other in different ways. The address space describes these relationships through references. Besides the parent-child arrangement, nodes can have further logical relationships. These relationships describe in more detail what function the nodes have and how they depend on each other. References also let you define additional trees in the address space of the server without reorganizing existing nodes.

To create a reference, the client uses the `AddReferences` interface of the server:

```csharp
client.AddReference(
        "ns=2;s=Plant",
        targetNodeId: "ns=2;s=Machines/MAC01",
        targetNodeCategory: OpcNodeCategory.Object);
```

```vbnet
client.AddReference( _
        "ns=2;s=Plant", _
        targetNodeId:="ns=2;s=Machines/MAC01", _
        targetNodeCategory:=OpcNodeCategory.Object)
```

The example creates a reference of the type Organizes that leads from the node with the identifier `ns=2;s=Plant` to the node `ns=2;s=Machines/MAC01`. By default, the client uses the reference type Organizes and the direction `OpcReferenceDirection.ParentToChild`. With this direction, the first node becomes the parent node. The statement therefore has the same result as the following example:

```csharp
client.AddReference(
        "ns=2;s=Plant",
        targetNodeId: "ns=2;s=Machines/MAC01",
        targetNodeCategory: OpcNodeCategory.Object,
        direction: OpcReferenceDirection.ParentToChild,
        referenceType: OpcReferenceType.Organizes);
```

```vbnet
client.AddReference( _
        "ns=2;s=Plant", _
        targetNodeId:="ns=2;s=Machines/MAC01", _
        targetNodeCategory:=OpcNodeCategory.Object, _
        direction:=OpcReferenceDirection.ParentToChild, _
        referenceType:=OpcReferenceType.Organizes)
```

The first identifier always specifies the source node, the `targetNodeId` parameter the identifier of the target node. With the direction `OpcReferenceDirection.ChildToParent`, the client creates the reference from the target node to the source node instead. The additional `targetNodeCategory` parameter must match the `Category` property of the target node, the NodeClass in the specification. The server uses this value to make sure that it knows the requested target node well enough.

To create multiple references at once:

```csharp
client.AddReferences(
        new OpcAddReference(
                "ns=2;s=Plant01",
                targetNodeId: "ns=2;s=Machines/MAC01",
                targetNodeCategory: OpcNodeCategory.Object),
        new OpcAddReference(
                "ns=2;s=Plant01",
                targetNodeId: "ns=2;s=Machines/MAC02",
                targetNodeCategory: OpcNodeCategory.Object),
        new OpcAddReference(
                "ns=2;s=Plant02",
                targetNodeId: "ns=2;s=Machines/MAC03",
                targetNodeCategory: OpcNodeCategory.Object));
```

```vbnet
client.AddReferences( _
        New OpcAddReference( _
                "ns=2;s=Plant01", _
                targetNodeId:="ns=2;s=Machines/MAC01", _
                targetNodeCategory:=OpcNodeCategory.Object), _
        New OpcAddReference( _
                "ns=2;s=Plant01", _
                targetNodeId:="ns=2;s=Machines/MAC02", _
                targetNodeCategory:=OpcNodeCategory.Object), _
        New OpcAddReference( _
                "ns=2;s=Plant02", _
                targetNodeId:="ns=2;s=Machines/MAC03", _
                targetNodeCategory:=OpcNodeCategory.Object))
```

The example above also places three nodes below the nodes "Plant01" and "Plant02". Each of these nodes represents a machine and is located below the node "Machines". Because the default reference type is Organizes, the nodes remain available below "Machines". In addition, "MAC01" and "MAC02" appear below "Plant01", and "MAC03" appears below "Plant02".

## Delete references

Types used: `OpcClient`, `OpcDeleteReference`, `OpcStatus`, `OpcStatusCollection`

To delete existing references between nodes in the address space of the server, use the `DeleteReferences` interface of the server:

```csharp
client.DeleteReference(
        nodeId: "ns=2;s=Machines/MAC03",
        targetNodeId: "ns=2;s=Plant");

```

```vbnet
client.DeleteReference( _
        nodeId:="ns=2;s=Machines/MAC03", _
        targetNodeId:="ns=2;s=Plant")

```

The example deletes all Organizes references between the nodes with the identifiers `ns=2;s=Machines/MAC03` and `ns=2;s=Plant`, in both directions. To delete only the Organizes references in one direction, from the source node to the target node:

```csharp
client.DeleteReference(
        nodeId: "ns=2;s=Machines/MAC03",
        targetNodeId: "ns=2;s=Plant",
        direction: OpcReferenceDirection.ChildToParent);

```

```vbnet
client.DeleteReference( _
        nodeId:="ns=2;s=Machines/MAC03", _
        targetNodeId:="ns=2;s=Plant", _
        direction:=OpcReferenceDirection.ChildToParent)

```

To delete references of a type other than Organizes, specify the type through the additional `referenceType` or `referenceTypeId` parameter:

```csharp
client.DeleteReference(
        nodeId: "ns=2;s=Machines/MAC03",
        targetNodeId: "ns=2;s=Plant",
        direction: OpcReferenceDirection.ChildToParent, 
        referenceType: OpcReferenceType.HierarchicalReferences);

```

```vbnet
client.DeleteReference( _
        nodeId:="ns=2;s=Machines/MAC03", _
        targetNodeId:="ns=2;s=Plant", _
        direction:=OpcReferenceDirection.ChildToParent, _
        referenceType:=OpcReferenceType.HierarchicalReferences)

```

To delete multiple references at once:

```csharp
client.DeleteReferences(
        new OpcDeleteReference("ns=2;s=Machines/MAC01", "ns=2;s=Plant01"),
        new OpcDeleteReference("ns=2;s=Machines/MAC02", "ns=2;s=Plant01"),
        new OpcDeleteReference("ns=2;s=Machines/MAC03", "ns=2;s=Plant02"));

```

```vbnet
client.DeleteReferences( _
        New OpcDeleteReference("ns=2;s=Machines/MAC01", "ns=2;s=Plant01"), _
        New OpcDeleteReference("ns=2;s=Machines/MAC02", "ns=2;s=Plant01"), _
        New OpcDeleteReference("ns=2;s=Machines/MAC03", "ns=2;s=Plant02"))

```
