Java 具有历史数据访问功能的Milo OPC UA服务器

Java 具有历史数据访问功能的Milo OPC UA服务器,java,opc-ua,milo,Java,Opc Ua,Milo,Hy 我不熟悉(和OPC-UA),并尝试实现具有历史数据访问功能的OPC-UA服务器。我重用了当前的milo服务器示例,并创建了一个历史节点。在这个节点上,我可以(使用Prosys OPC UA客户端)查询空历史记录。我知道我必须自己实现历史节点的持久性。 到目前为止还不错,但是我找不到任何关于处理历史记录读取请求以及如何返回响应的信息。更准确地说,如何将HistoryData添加到HistoryReadResult @Override public void historyRead(Histo

Hy

我不熟悉(和OPC-UA),并尝试实现具有历史数据访问功能的OPC-UA服务器。我重用了当前的milo服务器示例,并创建了一个历史节点。在这个节点上,我可以(使用Prosys OPC UA客户端)查询空历史记录。我知道我必须自己实现历史节点的持久性。 到目前为止还不错,但是我找不到任何关于处理历史记录读取请求以及如何返回响应的信息。更准确地说,如何将
HistoryData
添加到
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);
}
@覆盖
public void historyRead(HistoryReadContext上下文、HistoryReadDetails readDetails、TimestampsToReturn时间戳、,
列出ReadValueID)
{
List results=Lists.newArrayListWithCapacity(readValueIds.size());
对于(HistoryReadValueId readValueId:readValueId){
//返回3个历史条目
DataValue v1=新的DataValue(新的变量(新的双精度(1)),StatusCode.GOOD,新的DateTime(Date.from(Instant.now().减(1,ChronoUnit.MINUTES)));
DataValue v2=新的DataValue(新的变量(新的双精度(2)),StatusCode.GOOD,新的DateTime(Date.from(Instant.now().减去(2,ChronoUnit.MINUTES)));
DataValue v3=新数据值(新变量(新双精度(3)),StatusCode.GOOD,新日期时间(Date.from(Instant.now().减(3,ChronoUnit.MINUTES)));
HistoryData数据=新的HistoryData(新数据值[]{v1,v2,v3});
//???
HistoryReadResult=新的HistoryReadResult(StatusCode.GOOD,ByteString.NULL_值,?);
结果。添加(结果);
}
上下文。完成(结果);
}

您需要访问规范才能成功实现历史访问服务。第四部分和第十一部分

HistoryReadResult
构造函数中的最后一个参数应该是
HistoryData
结构
ExtensionObject
基本上是对结构进行编码和传输的容器


要创建
ExtensionObject
,首先创建
HistoryData
(或
HistoryModifiedData
,具体取决于……请参见规范),然后执行类似
ExtensionObject.encode(HistoryData)的操作
要获取对象,您需要完成构建
HistoryReadResult

覆盖historyRead是正确的方法

HistoryReadResult result = new HistoryReadResult(StatusCode.GOOD, ByteString.NULL_VALUE,ExtensionObject.encode(data) );
但是,在使用特定的AccessLevel和历史化模式定义我的variableNode之前,通用客户端(如UA Expert)没有调用该方法,如下所示:

        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();
Set acclevels=new LinkedHashSet();
acclevels.add(AccessLevel.CurrentRead);
acclevels.add(AccessLevel.CurrentWrite);
acclevels.add(AccessLevel.HistoryRead);
UaVariableNode节点=新的UaVariableNode.UaVariableNodeBuilder(server.getNodeMap())
.setNodeId(新的NodeId(名称空间索引,“HelloWorld/Test/”+name))
.setAccessLevel(ubyte(AccessLevel.getMask(acclevels)))
.setUserAccessLevel(ubyte(AccessLevel.getMask(acclevels)))
.setBrowseName(新的限定名称(名称空间索引,名称))
.setDisplayName(LocalizedText.english(名称))
.setDataType(类型ID)
.setTypeDefinition(标识符.BaseDataVariableType)
.设置历史记录(true)
.build();