.NET Based OPC UA Client/Server SDK
Lesson 7: Adding Support for Historical Access for Data

Table of Contents

This lesson uses the model used for Lesson 3:

Figure 7.1:

Adding History

In this lesson we will add code that creates an in-memory historical archive for the Variables highlighted in red.

Step 1

The first step requires the UnderlyingSystem to be modified to store the history and provide access to it via an object that implements the IHistoryDataSource.

The following method on the UnderlyingSystem class provides that access:

public IHistoryDataSource GetHistoryDataSource(int blockAddress, int tag)

The implementation in this example is an instance of the InMemoryHistoryDataSource class which stores the values for a Variable as they change. The IHistoryDataSource is used by the NodeManager to access the raw data during a ReadRaw, ReadProcessed or ReadAtTime operation.

An implementation with an external archive such as an SQL database would replace the InMemoryHistoryDataSource with a class that knows how to access the external archive.

Step 2

The NodeManager needs to indicate which Variables have history. This is done by setting the AccessLevel on the Variable and by adding the History-Access-Configuration-Object (BrowseName HAConfiguration) as a child of the Variable.

The following code added to Lesson07Nodemanager.Startup() does this:

settings = AddHistorySettings(settings, block, property, variable);

Step 3

The NodeManager now needs to link calls to historical read to the IHistoryDataSource. This is done by overriding the CreateHistoryContinuationPoint method on BaseNodeManager:

private CreateObjectSettings AddHistorySettings(CreateObjectSettings settings, BlockConfiguration block, BlockProperty property, VariableNode variable)
if (property.History != null)
// enable historical access.
variable.AccessLevel |= AccessLevels.HistoryRead;
// this ensures the address is passed in history handles.
SetNodeUserData(variable.NodeId, new SystemAddress() { Address = block.Address, Offset = property.Offset });
NodeId configurationId = new NodeId(block.Name + "." + property.Name + "." + BrowseNames.HAConfiguration, InstanceNamespaceIndex);
// create historical configuration object.
settings = new CreateObjectSettings()
ParentNodeId = variable.NodeId,
ReferenceTypeId = ReferenceTypeIds.HasHistoricalConfiguration,
RequestedNodeId = configurationId,
BrowseName = BrowseNames.HAConfiguration,
// add optional properties.
OptionalBrowsePaths = new string[] { BrowseNames.StartOfArchive, BrowseNames.StartOfOnlineArchive }
CreateObject(Server.DefaultRequestContext, settings);
// link model to node.
LinkModelToNode(configurationId, property.HistoryConfiguration, null, null, 500);
return settings;

The HistoryDataRawReader class uses the IHistoryDataSource to access the archive. It is used to implement the ReadRaw, ReadProcessed and ReadAtTime operations.