OPC-UA - Method call with "complex" input values

In relation to calling OPC-UA method calls on RFID readers supporting OPC-UA AutoID companion specifications (https://opcfoundation.org/markets-collaboration/aim/) I am hitting some challenges.

Specifically it is when calling the method “SCAN”
(https://opcfoundation.org/developer-tools/specifications-unified-architecture/opc-unified-architecture-for-autoid/)
Here i am trying to pass this structure - https://reference.opcfoundation.org/v104/AutoID/DataTypes/ScanSettings/

In the “inputs” parameter of system.opua.callMethod.
When sending the list i am getting following error message - which makes sense:

INFO | jvm 1 | 2020/01/09 15:17:32 | (2158690304L, u’Bad_InvalidArgument’, u’One or more arguments are invalid.’)
INFO | jvm 1 | 2020/01/09 15:17:32 | [(2155085824L, u’Bad_TypeMismatch’, u"The value supplied for the attribute is not of the same type as the attribute’s value.")]

I would like help on creating the “struct” that is required to send as an input parameter using system.opua.callMethod

My current code looks like this:

    ScanSettings = [2000,2,0] 

    result = system.opcua.callMethod(
	    "S000001",
    	"ns=4;i=5002",
    	"ns=4;i=7010",
    	[ScanSettings]
	)
1 Like

This miiiiight work if you pass anything that’s supposed to be a struct as a JSON string. ScanSettings would be:

{
    duration: 0.0,
    cycles: 0,
    dataAvailable: false,
    locationType: 0
}

I tried. Not too successfull :slight_smile: - throws same error.

Is there anyway to debug what is actually sent “raw” or see how system.opua.callMethod is put together?

I will try a bit more as i have several different AutoID devices. They should all behave similar, but it might prove otherwise testing a bit more.

Getting a capture with Wireshark might help.

Ah, also find the logger for opcua.gateway.scripting.CallMethod and turn it to DEBUG.

Hi Kevin - Super with the logger.

It looks like this. Do you have an idea what could be the exact reason.

CallMethod	14Jan2020 09:39:08	coerced outputs: []
CallMethod	14Jan2020 09:39:08	coerced inputs: [Variant{value=null}]
CallMethod	14Jan2020 09:39:08	raw inputs: [u'{"duration":2000.0,"dataAvailable":0,"cycles":4}']
CallMethod	14Jan2020 09:39:08	arguments: MethodArguments(inputArguments=[Argument{Name=Setting, DataType=NodeId{ns=3, id=3010}, ValueRank=-1, ArrayDimensions=[], Description=LocalizedText{text=null, locale=null}}], outputArguments=[Argument{Name=Results, DataType=NodeId{ns=3, id=3007}, ValueRank=1, ArrayDimensions=[], Description=LocalizedText{text=null, locale=null}}, Argument{Name=Status, DataType=NodeId{ns=3, id=3013}, ValueRank=-1, ArrayDimensions=[], Description=LocalizedText{text=null, locale=null}}])
CallMethod	14Jan2020 09:39:08	objectId=NodeId{ns=4, id=5002}, methodId=NodeId{ns=4, id=7010}

I sent with different “raw inputs” like below but gets same result:
raw inputs: [u’{“duration”:2000.0,“locationType”:0,“dataAvailable”:“false”,“cycles”:4}’]
raw inputs: [u’{“duration”:2000.0,“locationType”:0,“dataAvailable”:0,“cycles”:4}’]
raw inputs: [u’{“duration”:2000.0,“dataAvailable”:0,“cycles”:4}’]

In “arguments:” above NodeID references looks like this on the server through UaExpert:

Position:
Root - Types - DataTypes - Enumeration - Structure

  • ns=3, id=3007 - RfidScanResult
    1
  • ns=3, id=3010 - ScanSettings
    2

Root - Types - DataTypes - Enumeration

  • ns=3, id=3013 - AutoIdOperationStatusEnumeration
    3

Small addition

Trying it with another RFID reader supporting the OPC-UA AutoID companion specification i get these “Description=” values on the definition of “MethodArguments”.

MethodArguments(
    inputArguments=
        [
            Argument{Name=Settings, DataType=NodeId{ns=3, id=3010}, ValueRank=-1, ArrayDimensions=[], Description=LocalizedText{text=Settings for reading the UIDs or EPCs , locale=en}}
        ]
    
    , outputArguments=
        [
            Argument{Name=Results, DataType=NodeId{ns=3, id=3007}, ValueRank=1, ArrayDimensions=[], Description=LocalizedText{text=UID, EPC of the tags, locale=en}}, 
            Argument{Name=Status, DataType=NodeId{ns=3, id=3013}, ValueRank=1, ArrayDimensions=[], Description=LocalizedText{text=Status of the scan, locale=en}}
        ]
)

Does not reveal much more, but just confirms the other OPC-UA server acts the same way.

Can you turn the logger you find searching “DataTypeDictionaryReader” to TRACE, edit/save the OPC UA connection to this server, and then once it is connected again export the logs and upload them for me?

Also if you’re aware of a test server or some other way I can test this out myself it would help.

edit: and one more logger: com.inductiveautomation.ignition.gateway.opcua.util.Conversions

Try to invoke the method again after edit/saving the connection and letting it connect.

Set “DataTypeDictionaryReader” to TRACE and got a reload of the OPC server structure in the log.
Sent the log to you.

For testing i suggest a Skype session. I have nothing i can expose to you directly.

For the other logger, i cannot find it. Any dependency to OPC-UA module?
Or can I use the less specific: com.inductiveautomation.ignition.gateway.opcua.util
My current build is: OPC-UA 8.0.0 (b2019040719)

Ah, can you upgrade to 8.0.7 while we continue testing?

I’d like to see these same procedure and logs after the upgrade - there should be a little more information, and you should be able to find the Conversions logger.

Ok, I was able to see the structure definition is a little more complex than the documentation suggested, so the JSON value you feed it has to be a little more complicated as well.

Try this instead:

{
    "LocationTypeSpecified": 1,
    "Reserved1": 0,
    "Duration": 0.0,
    "Cycles": 0,
    "DataAvailable": false,
    "LocationType": 0
}

Alternatively you can omit the LocationType member at the end if you set the “LocationTypeSpecified” member to 0.

Eventually you should not have to include these extra members that should be considered implementation details but the dynamic codec support in our OPC UA SDK is a little bit primitive right now.

I still recommend upgrading to 8.0.7 even if this works, though.

For anyone who finds this in the future the problem becomes clear with the “Conversions” logger on:

2020/01/16 02:50:19:445 | DEBUG | [Conversions                   ] 
failed to encode 
    dataTypeId=NodeId{ns=3, id=3010} 
    binaryEncodingId=NodeId{ns=3, id=5015} 
    value={"Cycles":4,"LocationTypeSpecified":0,"Duration":2000.0,"DataAvailable":0,"Reserved1":0}

org.eclipse.milo.opcua.stack.core.UaSerializationException: 
java.lang.ClassCastException: class com.google.gson.internal.LazilyParsedNumber cannot be cast to class java.lang.Integer (com.google.gson.internal.LazilyParsedNumber is in unnamed module of loader com.inductiveautomation.ignition.gateway.modules.ModuleClassLoader @2232ff6c; java.lang.Integer is in module java.base of loader 'bootstrap')
	at org.eclipse.milo.opcua.stack.core.types.OpcUaDefaultBinaryEncoding.encode(OpcUaDefaultBinaryEncoding.java:85)
	at org.eclipse.milo.opcua.stack.core.types.builtin.ExtensionObject.encode(ExtensionObject.java:219)

It’s an issue that needs to be fixed in the OPC UA SDK and then integrated into Ignition once it’s released.

Hi Kevin,

This may be a simple question, but I’m trying to send an array of strings as the input arguments. How would I construct this in my Ignition Script?

Attached is a wireshark capture of a good method call through UA Expert.

Good method call.zip (824 Bytes)

You’ll have to do something annoying like this to create an array value for now:

	import jarray
	from java.lang import String
	
	arg = ["foo", "bar"]
	arr = jarray.array(arg, String)

We can probably add support for just using a list without conversion to an array first to system.opcua.callMethod but for now you’ll have to do the conversion yourself.

1 Like

Thanks, that works for me!

Trying to do the same but I can’t figure out the error.
Siemens AutoId Reader, trying to call the ReadTag method, which works fine with UAExpert.

Arguments should be

  • Identifier
  • CodeType
  • Region
  • Offset
  • Length
  • Password

No matter what I input, the result is always “The value supplied for the attribute is not of the same type as the attribute’s value.”

Output of the callMethod return :
((2158690304L, u’Bad_InvalidArgument’, u’One or more arguments are invalid.’), [(2155085824L, u’Bad_TypeMismatch’, u"The value supplied for the attribute is not of the same type as the attribute’s value."), (0L, u’Good’, u’Good; unspecified.’), (0L, u’Good’, u’Good; unspecified.’), (0L, u’Good’, u’Good; unspecified.’), (0L, u’Good’, u’Good; unspecified.’), (2155085824L, u’Bad_TypeMismatch’, u"The value supplied for the attribute is not of the same type as the attribute’s value.")], [])

Debug messages for one of my attempt :
Arguments on this call :
res = system.opcua.callMethod(“SIL_E2_RFID”, “ns=4;i=5002”, “ns=4;i=7009”, [
system.util.jsonEncode({‘Identifier’ : 0}), ‘UId’, 0, 0, 8, system.util.jsonEncode({‘Password’ : ‘’}) ])

CallMethod 19Oct2020 14:17:46
coerced inputs: [Variant{value=null}, Variant{value=UId}, Variant{value=0}, Variant{value=0}, Variant{value=8}, Variant{value={“Password”:""}}]
Conversions 19Oct2020 14:17:46
failed to encode dataTypeId=NodeId{ns=3, id=3020} binaryEncodingId=NodeId{ns=3, id=5030} value={“Identifier”:0}
CallMethod 19Oct2020 14:17:46
raw inputs: [u’{“Identifier”:0}’, ‘UId’, 0, 0, 8, u’{“Password”:""}’]
CallMethod 19Oct2020 14:17:46
arguments: MethodArguments(inputArguments=[Argument(name=Identifier, dataType=NodeId{ns=3, id=3020}, valueRank=-1, arrayDimensions=[], description=LocalizedText{text=null, locale=null}), Argument(name=CodeType, dataType=NodeId{ns=3, id=3031}, valueRank=-1, arrayDimensions=[], description=LocalizedText{text=null, locale=null}), Argument(name=Region, dataType=NodeId{ns=0, id=5}, valueRank=-1, arrayDimensions=[], description=LocalizedText{text=null, locale=null}), Argument(name=Offset, dataType=NodeId{ns=0, id=7}, valueRank=-1, arrayDimensions=[], description=LocalizedText{text=null, locale=null}), Argument(name=Length, dataType=NodeId{ns=0, id=7}, valueRank=-1, arrayDimensions=[], description=LocalizedText{text=null, locale=null}), Argument(name=Password, dataType=NodeId{ns=0, id=15}, valueRank=-1, arrayDimensions=[], description=LocalizedText{text=null, locale=null})], outputArguments=[Argument(name=ResultData, dataType=NodeId{ns=0, id=15}, valueRank=-1, arrayDimensions=[], description=LocalizedText{text=null, locale=null}), Argument(name=Status, dataType=NodeId{ns=3, id=3013}, valueRank=-1, arrayDimensions=[], description=LocalizedText{text=null, locale=null})])

Maybe the issue is related to the fact the argument is considered an Union Datatype (https://reference.opcfoundation.org/v104/AutoID/v100/docs/9.4.2/) but I have no idea how to format it.

Ho, Ignition is 8.0.16 (b2020082513)

According the response the ‘Identifier’ and ‘Password’ arguments you’ve supplied are incorrect.

I think ‘Identifier’ needs to have a JSON value more like this, though I don’t know what type in that union you’re actually trying to use, and it will affect the switch value and object contents:

{"SwitchField":2,"String":"stringIdGoesHere"}

‘Password’ in this method is actually supposed to be a ByteString… which is a bit difficult to create. Maybe something like this:

from org.eclipse.milo.opcua.stack.core.types.builtin import ByteString
	
pwd = "my_password"
pwd_bs = ByteString.of(pwd.encode())

edit:
if the value for 'Identifier" is really supposed to be 0 it may be this:

{"SwitchField":4,"Custom":0}
{"SwitchField":0,"Custom":0}

did the trick for the 1st argument. It was working with None (0) in UAExpert.

Sadly, 2nd part for the last argument fails : I can’t import milo

script 19Oct2020 15:27:22
Error running action ‘dom.onClick’ on test@D/root/Button: Traceback (most recent call last): File “function:runAction”, line 2, in runAction ImportError: No module named milo

Ah, right, you wouldn’t be able to.

See if the server will accept a String argument instead, then.

For the purpose of testing, the reader has no security, so no password. Putting security would have been the next step :slight_smile:

I tried a few strings to no avail :

  • None
  • “”
  • “Password:None”
  • “Password:”
  • b""
  • {} >> Crash (Not a built-in type: class org.python.core.PyDictionary)
  • {“Password”: “”} >> Crash (Not a built-in type: class org.python.core.PyDictionary)
  • 0x00