1
votes

Comment écrire de manière fiable sur un serveur OPC UA?

J'essaye d'écrire quelques valeurs sur un serveur OPC UA. Pour le faire de manière fiable, j'ai probablement besoin de connaître le DataType du nœud sur lequel j'essaie d'écrire, sinon il semble que je suis enclin à avoir des incohérences de type de données.

Disons que mon serveur a un nœud TestNodeOne avec le type de données Int16

Mon API reçoit l'ordre d'écrire la valeur 3 sur ce nœud. Maintenant, je dois prendre une décision: est-ce que je gère le 3 comme un entier ou comme un UShort? Pour cela, il semble que j'aurais besoin du type de données de mon nœud.

Ce que j'ai essayé

Mon approche consistait simplement à parcourir le serveur et à créer un cache de tous ses nœuds avec le type de données correspondant. Voici à quoi cela ressemble:

// 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;
}

Je ne peux pas comprendre comment obtenir le type de données d'un nœud. Je pense que je suis sur la mauvaise voie ici. Comment puis-je connaître le type de données d'un nœud?


0 commentaires

3 Réponses :


0
votes

Le moyen le plus simple de savoir quel type de données écrire dans la valeur d'un nœud consiste à envoyer une valeur de demande de lecture avant l'écriture.

 - ServerStatusType      [ServerStatusDataType]
   - BuildInfo           [BuildInfoType] -> Also a complex Datatype
   - CurrentTime         [UtcTime]
   - SecondsTillShutdown [UInt32]
   - ShutdownReason      [LocalizedText]
   - StartTime           [UtcTime]
   - State               [ServerState] -> Also a complex Datatype

Dans la réponse de lecture, vous obtiendrez la valeur actuelle du nœud ainsi que son type de données OPC UA.

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

Edit: Comme indiqué dans le commentaire, il est également possible que la valeur actuelle soit Null. Ainsi, au lieu de lire l'attribut Value du Node, vous pouvez également lire l'attribut DataType.

Dans le cas d'un DataTypes OPC UA "non intégré", vous devrez soit:

  • Connaissez déjà le type de données et sa structure pour vous permettre d'écrire les informations correctes

  • Parcourez le TargetNode de HasTypeDefinition.

ie: Server_ServerStatus (NodeId [i = 2256]) a un DataType ServerStatusDataType (NodeId [i = 862]) et un HasTypeDefinition ServerStatusType (NodeId [i = 2138]). En parcourant le ServerStatusType, vous obtiendrez la structure de ce DataType complexe. Dans ce cas:

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


1 commentaires

Ça n'est pas correct. Vous ne devez pas lire l'attribut Value. Vous devez lire l'attribut DataType. La valeur peut ne pas avoir de type (null) ou il peut s'agir d'un sous-type spécifique du DataType autorisé. En outre, la lecture de la valeur peut impliquer une communication lente avec le périphérique cible, contrairement à la lecture de DataType.



1
votes

L'approche la plus simple consiste à lire l'attribut DataType avant chaque écriture afin de savoir quel type de valeur envoyer.

Une fois que cela fonctionne, vous pouvez essayer quelque chose d'un peu plus délicat, comme la mise en cache du DataType afin que les écritures ultérieures sur le même nœud n'aient pas besoin de lire à nouveau l'attribut DataType, puis si une écriture échoue avec Bad_TypeMismatch, vous pouvez invalider l'entrée dans le cache de ce nœud, puis réessayez la lecture + écriture.


3 commentaires

J'espérais en quelque sorte pouvoir obtenir les types de données de mes nœuds pendant la navigation ... Y a-t-il une différence entre l'utilisation d'un ReadRequest et l'utilisation de client.readValues? Et comment les clients GUI qui affichent des types de données obtiennent-ils les informations sur les types de données? Merci pour votre suggestion, je vais essayer de la mettre en œuvre.


Les informations DataType ne font pas partie de la réponse de navigation. Les clients GUI lisent l'attribut DataType (et généralement tous les autres attributs également).


OpcUaClient # readValue (s) est spécifiquement pour l'attribut Value, vous devez utiliser OpcUaClient # read pour les autres attributs. Ces deux méthodes envoient une ReadRequest en dessous et utilisent le service Read.



0
votes

Tout d'abord, je pense que vous devriez lire le type de valeur, tel que

Variant v = new Variant(Unsigned.ushort(22));

Deuxièmement, utilisez des méthodes de classe Unsigned pour trouver ce que vous voulez taper:

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


0 commentaires