Avatar

Please consider registering
guest

sp_LogInOut Log In sp_Registration Register

Register | Lost password?
Advanced Search

— Forum Scope —




— Match —





— Forum Options —





Minimum search word length is 3 characters - maximum search word length is 84 characters

sp_Feed Topic RSS sp_TopicIcon
Calling Methods with Structs
January 21, 2022
10:19, EET
Avatar
Sand0rf
Member
Members
Forum Posts: 6
Member Since:
January 21, 2022
sp_UserOfflineSmall Offline

Hi all,

I’m currently evaluating the ProSys Java SDK and so far all our tests have succeeded but now I’m stuck calling a method that has two parameters. One is a simple integer and the other is a complex type conisting of several integers, strings and doubles. I’ve previously tested calling a method with simple parameters which was not a problem and also reading and writing complete structs which was also not a problem. However I can’t seem to instantiate the DynamicStructure that is used as parameter. When browsing through the attributes of the node that is the completex type parameter and the attribute for value is null (which makes sense since it is a input parameter) and the attribute for DataTypeDefinition contains a StructureDefinition but how am I able to create a new empty dynamic structure that I can fill with data and post to the method?

Thanks for your reply!

Sander

January 21, 2022
11:45, EET
Avatar
Matti Siponen
Moderator
Members

Moderators
Forum Posts: 321
Member Since:
February 11, 2020
sp_UserOfflineSmall Offline

Hello,

To instantiate a Structure (which is the supertype of DynamicStructure) you will need its StructureSpecification. If your Client has succefully initialized the TypeDictionary, you should be able to get the StructureSpecification by calling

StructureSpecification structureSpecification = client.getEncoderContext().getStructureSpecification(dataTypeId)

where client is your UaClient and dataTypeId is the UaNodeId version of the NodeId of the Structure DataType. After you have the StructureSpecification, you can build a new Structure by calling

Structure structure = structureSpecification.toStructureBuilder().build()

You can then use structure.set(String fieldName, Object value) to set the value of a Field with the specified name to the given Object.

Finally you can create a Variant from the Structure by calling

Variant variant = new Variant(structure)

and use that Variant as an input argument of the Method.

January 27, 2022
14:38, EET
Avatar
Sand0rf
Member
Members
Forum Posts: 6
Member Since:
January 21, 2022
sp_UserOfflineSmall Offline

Thanks! That solved the issue with creating structs.

I do have a problem with calling methods that have multiple input arguments, not necessarily structs. I did test a method with one input and output parameter which worked without problem. However adding a second parameter gives a BadInvalidArgument exception. This is also when the method has just twee int16 parameters and the exception is also thrown when calling the method from UA Expert leading me to believe that the problem is not actually in the client but the server.

Is there a limit within the OPC specification on how this is done? Of is this vendor specific with regards to the OPC Server. I’m testing with a Siemens Simatic S7 PLC.

January 27, 2022
15:06, EET
Avatar
Matti Siponen
Moderator
Members

Moderators
Forum Posts: 321
Member Since:
February 11, 2020
sp_UserOfflineSmall Offline

Hello,

Since you’ve also tested this with UaExpert, I would assume there is problem in the Server.

However, Servers are allowed to return StatusCode Bad_InvalidArgument even if the given input argument has the correct DataType but its value is outside of acceptable range of values. This information should be available in the Value of the InputArguments Property Node of the Method Node, but it’s not mandatory to include this information in the Description Field of the Argument Structure.

February 15, 2022
12:09, EET
Avatar
Sand0rf
Member
Members
Forum Posts: 6
Member Since:
January 21, 2022
sp_UserOfflineSmall Offline

The problem was indeed related to the PLC. I’m not programming the PLC but from what I understand from it there was a mapping problem between the OPC Server and PLC logic which caused these errors. These have been resolved and we’re able to call methods with multiple input parameters.

March 22, 2022
11:49, EET
Avatar
Hendrik Ulbrich
Member
Members
Forum Posts: 14
Member Since:
June 27, 2018
sp_UserOfflineSmall Offline

How exactly do I get the “UaNodeId” object?

First I have the NodeId of the method and the method:
NodeId methodId = new NodeId(2, “xy921id”);
UaMethod method = client.getAddressSpace().getMethod(methodId);

Then I extract the InputArguements – in the example the first place is the CustomStructure:
Argument[] inArgs = method.getInputArguments();
NodeId inTypeNode = inArgs[0].getDataType();

Then I want to get the DynamicStructure via the Specification. I am failing to get the UaNodeId:
UaNodeId inUaNodeId = null; // ??
StructureSpecification inSpec = client.getEncoderContext().getStructureSpecification(inUaNodeId);
DynamicStructure inStructure = new DynamicStructure(inSpec);

March 22, 2022
13:10, EET
Avatar
Bjarne Boström
Moderator
Moderators
Forum Posts: 983
Member Since:
April 3, 2012
sp_UserOfflineSmall Offline

As a general rule, when there are no constructors, look for static factory methods, most IDEs show them when writing the class name (or typically you autocomplete it after few letters) and then a dot. Most of the “newer stuff” in the SDK uses static factory methods, whenever possible (I have forgotten it a few times).

Instances of UaNodeId are obtained via static factory methods. The methods for converting NodeIds and ExpandedNodeIds are:

UaNodeId.fromLocal(ExpandedNodeId)
UaNodeId.fromLocal(ExpandedNodeId, NamespaceTable)
UaNodeId.fromLocal(NodeId, NamespaceTable)

Get the NamespaceTable via client.getNamespaceTable(). The version without it is only for cases where the ExpandedNodeId has the uri and not the index.

UaNodeId.fromStandard(NodeId) works only for NodeIds of the base, standard, namespace (with index 0, as it’s uri is known and it is always index 0).

The “intended to be used” method … could maybe do some improvements, but is:

UaNodeId from(String namespaceUri, Object value)

It behaves like UaNodeId from(String namespaceUri, Object value) constructor does, i.e. value must be UnsignedInteger, String, UUID or ByteString. We probably should add some helpers in some future version like UaNodeId.numeric etc. to fix the type in the signature.

P.S.

For reasons, see e.g. https://www.baeldung.com/java-constructors-vs-static-factory-methods

So basically the static factory methods might return an existing instance (if it is equal to the given data) and/or a subtype of UaNodeId (we could save memory e.g. via https://refactoring.guru/design-patterns/flyweight, but I will skip details) . Like technically for the most parts that is not yet used, but we can do so in the future without breaking the API (could not do that with constructors, as they must always return a new instances and instance of that exact class).

P.S.2
And SDK-reasons (prev was “java-reasons”) is that NodeId should “never be used”, as the index is only constant for an NamespaceUri within a scope of a single Session (e.g. server can change it at will on e.g. a restart). ExpandedNodeId has the problem that it can be local or remote and have uri or index, so it is impossible to properly .equals it, without the context of NamespaceTable (since other could have index and other could have uri), so ExpandedNodeId should also not be used. OK, local ExpandedNodeIds should not have uri, but back in the days we ended up needing to use the uri-form in e.g. our Codegen (as NodeIds obiviously could not be used). Thus, to solve this issue we made UaNodeId (and UaExpandedNodeId and UaQualifiedName) as a better alternative that wont suffer from this problem (always local, always has uri). Thus most “new API” tries to use UaNodeId+UaQualifiedName as much as possible (this is more seen in PubSub configuration API)

March 22, 2022
13:22, EET
Avatar
Bjarne Boström
Moderator
Moderators
Forum Posts: 983
Member Since:
April 3, 2012
sp_UserOfflineSmall Offline

P.S.3
For the

UaNodeId from(String namespaceUri, Object value)

the NamespaceUri should be “known to you”, i.e. instead of writing “2” in the index like in NodeIds, you would write the known uri of that index. The index is basically just an transfer optimization technique in the binary stream (numbers take less space than uris), but it should _only_ be treated like that. Thus everything should begin with the uri in the “though process”. And pretty much every SDK has done the mistake of having APIs with indexes. It should just be an encoding level “step” (also many UIs do visualize NodeIds with the index), but it might take us a very long time to “fix this”, as half of the SDK APIs have used classes with the index.

The index to uri mappings can be seen the “Root/Objects/Server/NamespaceArray” node.

Forum Timezone: Europe/Helsinki

Most Users Ever Online: 518

Currently Online:
21 Guest(s)

Currently Browsing this Page:
1 Guest(s)

Top Posters:

hbrackel: 135

pramanj: 86

Francesco Zambon: 81

rocket science: 77

Ibrahim: 76

Sabari: 62

kapsl: 57

gjevremovic: 49

Xavier: 43

fred: 41

Member Stats:

Guest Posters: 0

Members: 681

Moderators: 16

Admins: 1

Forum Stats:

Groups: 3

Forums: 15

Topics: 1467

Posts: 6261

Newest Members:

graciela2073, sagarchau, elviralangwell4, Donnavek, Eddiefauth, DonaldPooma, fidelduke938316, Jan-Pfizer, DavidROunc, fen.pang@woodside.com

Moderators: Jouni Aro: 1010, Otso Palonen: 32, Tuomas Hiltunen: 5, Pyry: 1, Petri: 0, Bjarne Boström: 983, Heikki Tahvanainen: 402, Jukka Asikainen: 1, moldzh08: 0, Jimmy Ni: 26, Teppo Uimonen: 21, Markus Johansson: 42, Niklas Nurminen: 0, Matti Siponen: 321, Lusetti: 0, Ari-Pekka Soikkeli: 5

Administrators: admin: 1