1

我正在尝试将一些值写入 OPC UA 服务器。为了可靠地做到这一点,我可能需要知道我正在尝试写入的节点的数据类型,否则我似乎很容易出现数据类型不匹配的情况。

假设我的服务器有一个数据类型为 Int16 的节点 TestNodeOne

我的 API 被告知将值 3 写入该节点。现在我需要做出决定:我将 3 作为 Integer 还是作为 UShort 处理?为此,我似乎需要节点的数据类型。

我试过的

我的方法是浏览服务器并使用相应的数据类型创建所有节点的缓存。看起来是这样的:

// Recursively browse entire server
@SuppressWarnings("deprecation")
private HashMap<String, NodeId> browseNode(String indent, OpcUaClient client, NodeId browseRoot, HashMap<String, NodeId> nodesMap) {

    BrowseDescription browse = new BrowseDescription(
            browseRoot,
            BrowseDirection.Forward,
            Identifiers.References,
            true, uint(NodeClass.Object.getValue() | NodeClass.Variable.getValue()),
            uint(BrowseResultMask.All.getValue()));

    try {
        BrowseResult browseResult = client.browse(browse).get();
        List<ReferenceDescription> references = toList(browseResult.getReferences());

        for (ReferenceDescription rd : references) {

            UShort namespaceIndex = rd.getNodeId().getNamespaceIndex();
            String identifier = rd.getNodeId().getIdentifier().toString();
            NodeId node = new NodeId(namespaceIndex, identifier);
            nodesMap.put(rd.getNodeId().getIdentifier().toString(), node);

            logger.info("----------------------------------------------------------------");
            logger.info(identifier);
            logger.info("TYPE " + rd.getTypeDefinition()); // Not the right node
            logger.info("TYPE " + rd.getTypeId().getIdentifier().toString()); // Not the right node
            logger.info("TYPE " + rd.getReferenceTypeId().getIdentifier().toString()); // Not the right node

            rd.getNodeId().local().ifPresent(nodeId -> {
                browseNode(indent + "  ", client, nodeId, nodesMap);
            });
        }

    } catch (InterruptedException | ExecutionException e) {
        logger.error("Browsing nodeId={} failed: {}", browseRoot, e.getMessage(), e);
    }

    return nodesMap;
}

我不知道如何获取节点的数据类型。我想我在这里走错了路。如何找出节点的数据类型?

4

3 回答 3

1

最简单的方法是在每次写入之前读取 DataType 属性,这样您就知道要发送什么样的值。

一旦你有了这个工作,你可以尝试一些更棘手的事情,比如缓存 DataType 以便后续写入同一个节点不需要再次读取 DataType 属性,然后如果写入失败并出现 Bad_TypeMismatch 你可以使条目无效在该节点的缓存中,然后重试读+写。

于 2019-10-09T14:58:22.550 回答
0

知道在节点的值中写入哪种数据类型的最简单方法是在写入之前发送读取请求值。

ReadRequest
  -  ...
  -  NodesToRead [ReadValueId]
    - ReadValue[0]
      - NodeId:       {NodeId of your Node}
      - AttribueId:   Value (0x0d)
      - IndexRange:   Null
      - DataEncoding: Null

在读取响应中,您将获得节点的当前值及其 OPC UA 数据类型。

ReadResponse
  - ResponseHeader
  - Results [DataValue]
     - DataValue[0]
       - EncodingMask
       - Value 
         - VariantType: Int16
         - Int16:       3

编辑:如评论中所述,当前值也可能为 Null。因此,除了读取 Node 的 Value 属性,您还可以读取 DataType 属性。

对于“非内置”OPC UA 数据类型,您必须:

  • 已经知道数据类型及其结构,以便您编写正确的信息

  • 浏览 HasTypeDefinition 的 TargetNode。

即:Server_ServerStatus (NodeId [i=2256]) 有一个 DataType ServerStatusDataType (NodeId [i=862]) 和一个 HasTypeDefinition ServerStatusType (NodeId [i=2138])。通过浏览 ServerStatusType,您将获得这个复杂 DataType 的结构。在这种情况下:

 - ServerStatusType      [ServerStatusDataType]
   - BuildInfo           [BuildInfoType] -> Also a complex Datatype
   - CurrentTime         [UtcTime]
   - SecondsTillShutdown [UInt32]
   - ShutdownReason      [LocalizedText]
   - StartTime           [UtcTime]
   - State               [ServerState] -> Also a complex Datatype
于 2019-10-09T14:13:47.263 回答
0

首先,我认为您应该阅读值类型,例如

DataValue value = opcUaClient.readValue(0.0, TimestampsToReturn.Neither, nodeId).get();
Optional<ExpandedNodeId> dataType = value.getValue().getDataType();

其次,使用Unsigned类方法找到你想要的类型:

Variant v = new Variant(Unsigned.ushort(22));
于 2020-11-24T09:11:42.167 回答