rich.ard
rich.ard

Reputation: 11

Milo OPC UA Server with Historical Data Access

Hy,

I’m new to milo (and OPC-UA) and try to implement an OPC-UA server with Historical Data Access. I reused the current milo server example and create a history node. On this node I can query (with the Prosys OPC UA Client) the empty history. I know that I have to implement the persistency of the history nodes by myself. So far so good – but I could not found any information about to handle the history read request and how to return the response. More precisely how to add the HistoryData to an HistoryReadResult

@Override
public void historyRead(HistoryReadContext context, HistoryReadDetails readDetails, TimestampsToReturn timestamps,
        List<HistoryReadValueId> readValueIds)
{
     List<HistoryReadResult> results = Lists.newArrayListWithCapacity(readValueIds.size());
     for (HistoryReadValueId readValueId : readValueIds){
         //return 3 historical entries
         DataValue v1 = new DataValue(new Variant(new Double(1)), StatusCode.GOOD, new DateTime(Date.from(Instant.now().minus(1, ChronoUnit.MINUTES))));
         DataValue v2 = new DataValue(new Variant(new Double(2)), StatusCode.GOOD, new DateTime(Date.from(Instant.now().minus(2, ChronoUnit.MINUTES))));
         DataValue v3 = new DataValue(new Variant(new Double(3)), StatusCode.GOOD,  new DateTime(Date.from(Instant.now().minus(3, ChronoUnit.MINUTES))));
         HistoryData data = new HistoryData(new DataValue[] {v1,v2,v3});
         //???
         HistoryReadResult result = new HistoryReadResult(StatusCode.GOOD, ByteString.NULL_VALUE, ??? );
         results.add(result);
     }
     context.complete(results);
}

Upvotes: 1

Views: 1037

Answers (2)

Mumbai
Mumbai

Reputation: 11

Overrides historyRead is the correct way to do.

HistoryReadResult result = new HistoryReadResult(StatusCode.GOOD, ByteString.NULL_VALUE,ExtensionObject.encode(data) );

However method was not called by generic client such as UA-Expert before defining my variableNode with specific AccessLevel and Historizing mode like this :

        Set<AccessLevel> acclevels = new LinkedHashSet<>();
        acclevels.add(AccessLevel.CurrentRead);
        acclevels.add(AccessLevel.CurrentWrite);
        acclevels.add(AccessLevel.HistoryRead);

        UaVariableNode node = new UaVariableNode.UaVariableNodeBuilder(server.getNodeMap())
                .setNodeId(new NodeId(namespaceIndex, "HelloWorld/Test/" + name))
                .setAccessLevel(ubyte(AccessLevel.getMask(acclevels)))                  
                .setUserAccessLevel(ubyte(AccessLevel.getMask(acclevels)))
                .setBrowseName(new QualifiedName(namespaceIndex, name))
                .setDisplayName(LocalizedText.english(name))
                .setDataType(typeId)
                .setTypeDefinition(Identifiers.BaseDataVariableType)
                .setHistorizing(true)
                .build();

Upvotes: 1

Kevin Herron
Kevin Herron

Reputation: 7005

You're going to need access to the spec to successfully implement historical access services. Part 4 and Part 11.

The last parameter in the HistoryReadResult constructor is supposed to be a HistoryData structure. ExtensionObject is basically the container that structures are encoded and transferred in.

To create that ExtensionObject you would first create a HistoryData (or HistoryModifiedData, depends... see the spec) and then do something like ExtensionObject.encode(historyData) to get the object you need to finish building the HistoryReadResult.

Upvotes: 2

Related Questions