OPCFoundation / UA-Java-Legacy

This repository is provided by OPC Foundation as legacy support for an Java version for OPC UA.
https://github.com/OPCFoundation/UA-.NETStandard
Other
354 stars 226 forks source link

Read complex structure #198

Open friko16 opened 4 years ago

friko16 commented 4 years ago

Can someone help ? I am sitting on this for days and can't figure out how to read a non-leaf, complex data structure:


 ReadValueId[] nodesToRead = new ReadValueId[1];
 NodeId nodeId = new NodeId(3, "\"Trace\".\"DataRead\".\"TP\"[0]");
 nodesToRead[i] = new ReadValueId(nodeId, Attributes.Value, null,null);
 ReadRequest req = new ReadRequest(null, maxAge, TimestampsToReturn.Both, nodesToRead);
 ReadResponse res = connection.getSession().Read(req);

this data structure when viewed in UAExpert expands into multiple leaf nodes with String and boolean leafs.

and I am getting and exception:

Exception in thread "main" org.opcfoundation.ua.common.ServiceResultException: Bad_InternalError (code=0x80020000, description="2147614720, namespaceUri; value=TE_"Trace"."DataRead"."TP"")
    at org.opcfoundation.ua.transport.tcp.io.TcpConnection$ReadThread.run(TcpConnection.java:1193)
Caused by: java.lang.NullPointerException: namespaceUri; value=TE_"Trace"."DataRead"."TP"
    at org.opcfoundation.ua.builtintypes.ExpandedNodeId.<init>(ExpandedNodeId.java:204)
    at org.opcfoundation.ua.common.NamespaceTable.toExpandedNodeId(NamespaceTable.java:106)
    at org.opcfoundation.ua.encoding.binary.BinaryDecoder.getExtensionObject(BinaryDecoder.java:970)
    at org.opcfoundation.ua.encoding.binary.BinaryDecoder.getScalarObject(BinaryDecoder.java:1436)
    at org.opcfoundation.ua.encoding.binary.BinaryDecoder.getVariant(BinaryDecoder.java:1659)
    at org.opcfoundation.ua.encoding.binary.BinaryDecoder.getDataValue(BinaryDecoder.java:651)
    at org.opcfoundation.ua.encoding.binary.BinaryDecoder.getDataValueArray(BinaryDecoder.java:674)
    at org.opcfoundation.ua.core.EncodeableSerializer$171.getEncodeable(EncodeableSerializer.java:4491)
    at org.opcfoundation.ua.encoding.utils.AbstractSerializer.getEncodeable(AbstractSerializer.java:197)
    at org.opcfoundation.ua.encoding.utils.SerializerComposition.getEncodeable(SerializerComposition.java:104)
    at org.opcfoundation.ua.encoding.binary.BinaryDecoder.getMessage(BinaryDecoder.java:1255)
    at org.opcfoundation.ua.transport.tcp.io.TcpConnection$ReadThread.run(TcpConnection.java:1115)

with UAExpert tool, I can read that node with no problems and all it's leafs. opcua.1.4.1.jar

I have read in other issue that I need to read namespaceArray and set is as context, but have no idea how to set it, neither are there examples for that. please help.

jouniaro commented 4 years ago

Decoding custom structure types is not supported by default by the stack. You will need to register your own serialiser for the type, which requires that you know the structure a priori. See the existing implementations for the standard structure types generated inside the EncodeableSerializer class for examples.

In order to support any previously unknown structure in the client, you will need to parse the TypeDictionary provided by the server. That functionality is out of the scope of the stack and you will need an SDK for that or build it yourself.

friko16 commented 4 years ago

Thanks for response, @jouniaro . It's been a long time, I gave up on this problem, but now it came back and trying to solve it again. The reason I am trying to read the whole structure at once is that now I am passing a list of ~30 ReadValueIds to sessionChannel.Read() and the read takes around 200-300ms. The 30 values is a list of primitive opc types being part of a larger structure. To give you a glimpse of it, I am attaching print screen from UaExpert tool for browsing plc: Screenshot from 2020-08-09 07-31-45. Why does the read take so long ? this is how I do it:

String[] nodePaths = ...// initialized list of string nodeId's e.g. "DB_601_Trace_OPC.Read.TP[0].Value"    
int namespaceIdx = 3;
for (int i = 0; i < nodePaths.length; i++) {
    NodeId nodeId = new NodeId(namespaceIdx, nodePaths[i]);
    nodesToRead[i] = new ReadValueId(nodeId, Attributes.Value, null,null);
 }
ReadRequest req = new ReadRequest(null, maxAge, TimestampsToReturn.Neither, nodesToRead);
ReadResponse res = connection.getSession().Read(req);

This is unbearable for my requirements, I thought reading the whole structure's root will help. I have tested reading a whole structure with node's opcua parallel project (https://node-opcua.github.io/) and the read takes 20-30 ms for whole structure, and no serializaers/desarializers registration is needed, just pure JSON returned.

I have tried a bit with those structure serializers:

    private static class MachineType extends AbstractStructure {

        public static final ExpandedNodeId ID = new ExpandedNodeId(new NodeId(3, NodePath.dataMachine(0)));
        public static final ExpandedNodeId BINARY = new ExpandedNodeId(new NodeId(3, NodePath.dataMachine(0)));
        public static final ExpandedNodeId XML = new ExpandedNodeId(new NodeId(3, NodePath.dataMachine(0)));

        public ExpandedNodeId getTypeId() {
            return ID;
        }

        public ExpandedNodeId getXmlEncodeId() {
            return XML;
        }

        public ExpandedNodeId getBinaryEncodeId() {
            return BINARY;
        }
    }

    private void customStructure(ConnectionHandler connection) {

        EncodeableSerializer ser = EncodeableSerializer.getInstance();

        ser.addSerializer(new AbstractSerializer(MachineType.class, MachineType.BINARY, MachineType.XML, MachineType.ID) {
            public void calcEncodeable(IEncodeable encodeable, IEncoder calculator) throws EncodingException {
                MachineType obj = (MachineType) encodeable;
// ENCODING COMMENTED OUT FOR NOW...
//                calculator.putQualifiedName(null,  (obj==null)?null:obj.getKey() );
//                calculator.putVariant(null,  (obj==null)?null:obj.getValue() );
            }
            public void putEncodeable(IEncodeable encodeable, IEncoder encoder) throws EncodingException {
// ENCODING COMMENTED OUT FOR NOW...
//                encoder.putQualifiedName("Key",  (obj==null)?null:obj.getKey() );
//                encoder.putVariant("Value",  (obj==null)?null:obj.getValue() );
            }
            public IEncodeable getEncodeable(IDecoder decoder) throws DecodingException {
                MachineType result = new MachineType();
                QualifiedName qn = decoder.getQualifiedName("Key");
                Variant var = decoder.getVariant("Value");
                return result;
            }
        });
    }

but getting an error:

Exception in thread "Thread-0" java.lang.IllegalArgumentException: Only ExpandedNodeIds that contain the URI are allowed as parameter
    at org.opcfoundation.ua.encoding.utils.AbstractSerializer.fixAndValidateId(AbstractSerializer.java:58)
    at org.opcfoundation.ua.encoding.utils.AbstractSerializer.<init>(AbstractSerializer.java:107)
    at org.opcfoundation.ua.encoding.utils.AbstractSerializer.<init>(AbstractSerializer.java:88

Can some one help, as this is a huge limiter for us and prohibits completing the project. The solution can be either reading a list of NodeIs as of now, but speed it up to ~30ms, or solving the read speed by reading the whole structure at once. If reading the whole structure help with read speed, I don't expect someone to provide full implementation of Structure and Serializers, but some pseudo-code would help, doesn't have to compile.