02/09/2023
Hi everyone,
I work on a Siemens RF188C with a RF210R (reader RFID tags). I communic by OPC UA protocol with a Python program.
The goal is to get data tag when we call the opc-ua method "ReadTag". I can watch the opc-ua server via a opc-ua browser and I can easily call the method in question by directly filling in the input data (there are 6 parameters and 5 of them are "null" or equal to 0) . I then receive the correct values written on the RFID label which I scan.
Now I make a python program to catch this data. So I manage to connect to the server. But the problem is when i call the "ReadTag" method, we must pass him 6 input parameter and two of then are complex data type.
The first one is "ScanData" type, a array of 4 differents types : "ByteString", "String", "Epc"(custom type), "Custom"(custom type). He can be located with the NodeID "ns=3;i=3020".
The second is "CodeTypeDataType" type located with the NodeID "ns=3;i=3031".
Here the python code :
---------------------------------------------------------------------------------------------------------
import sys
from opcua import Client, ua
from opcua.common.ua_utils import data_type_to_variant_type
try:
# Connect to the OPC-UA server
client = Client("opc.tcp://10.1.40.26:4840")
client.connect()
# Get the parent node and the method node
parent = client.get_node("ns=4;i=5002")
method = parent.get_child("3:ReadTag")
# Get the input argument nodes
ScanData = client.get_node("ns=3;i=3020")
CodeTypeDataType = client.get_node("ns=3;i=3031")
# Define the values for each input argument
scan_data = ua.Variant(ua.get_default_value(data_type_to_variant_type(ScanData)), data_type_to_variant_type(ScanData))
cd_data = ua.Variant(ua.get_default_value(data_type_to_variant_type(CodeTypeDataType)), data_type_to_variant_type(CodeTypeDataType))
region = ua.Variant(ua.get_default_value(ua.VariantType.UInt16), ua.VariantType.UInt16)
offset = ua.Variant(ua.get_default_value(ua.VariantType.UInt32), ua.VariantType.UInt32)
length = ua.Variant(8, ua.VariantType.UInt32)
password = ua.Variant(ua.get_default_value(ua.VariantType.ByteString), ua.VariantType.ByteString)
# Call the method with the input argument values
out = parent.call_method(method,
scan_data,
cd_data,
region,
offset,
length,
password)
print( out[0] )
# Disconnect from the OPC-UA server
client.disconnect()
client.close_session()
except Exception as error:
exception_type, exception_object, exception_traceback = sys.exc_info()
line_number = exception_traceback.tb_lineno
print(error," , l.",line_number)
try:
client.disconnect()
client.close_session()
print("Disconnected")
except:
pass
----------------------------------------------------------------------------------------------------------------
When i run the python code, a error message spawn : "One or more arguments are invalid."(BadInvalidArgument) , l. 33
I think the problem is when i defined all parameters values (Particularly the ScanData and the CodeTypeDataType).
Could you please help me to solve this problem ?
05/30/2017
I am not sure what the line:
scan_data = ua.Variant(ua.get_default_value(data_type_to_variant_type(ScanData)), data_type_to_variant_type(ScanData))
Does, however, what it should be doing is creating an ExtensionObject with the NodeId of a DataTypeEncoding Node (this is different from the DataType Node). Can you inspect the contents of scan_data and see if it looks right?
02/09/2023
Hi Randy,
Thank's for your answer. ScanData is compose of 4 differents parameters : ByteString ; String ; Epc ; Custom.
When I use an opc-ua browser (Prosys opcua browser), I directly enter the parameters when I want to call the method. For ScanData, you can select the "Null" option, which will automatically fill the entry with: ScanData [ByteString="null", String="null", Epc="null", Custom="null"]
"Epc" is also a custom type : ScanDataEpc. It is available at NodeID : ns=3;i=3024.
"Custom" can be a lot of type but i thinks it's not the problem.
It's maybe the way I initialize all parameters that aren't not correct. The fact is that I just need to inform the "length" parameter and that's it, the other parameters are useless for my utilisation.
05/30/2017
The structure you need to see inside the variant would look something like this JSON object:
{ "Type": "ns=3;i=<DataTypeEncodingId>" "Body": { "ByteString": "<bytedata>", "String": "<stringdata>", "Epc": { <epc fields> }, "Custom": ???? } } Do you see anything like this? What is the DataType of Custom?
02/09/2023
I not see exactly a JSON object (maybe i not find it with the browser).
I can visualize input argument of the method (it's the subsection of "ReadTag" method, the other subsection is the output argument). They inform me of 6 parameters :
Name | DataType | Value |
InputArguments | Argument | Argument[6] |
[0] | Argument | Argument |
Name | String | Identifier |
DataType | NodeID | ns=3;i=3020 |
ValueRank | Int32 | -1 |
ArrayDimensions | UInt32 | UInt32[0] |
Description | LocalizedText | |
[1] | Argument | Argument |
Name | String | CodeType |
DataType | NodeID | ns=3;i=3031 |
...(same like [0]) | ||
[2] | Argument | Argument |
Name | String | Region |
DataType | NodeID | i=5 |
...(same like [0]) | ||
[3] | Argument | Argument |
Name | String | Offset |
DataType | NodeID | i=5 |
...(same like [0]) | ||
[4] | Argument | Argument |
Name | String | Length |
DataType | NodeID | i=7 |
...(same like [0]) | ||
[5] | Argument | Argument |
Name | String | Password |
DataType | NodeID | i=15 |
...(same like [0]) |
The NodeID of [0] and [1] correspond respectively at "ScanData" type and "CodeTypeDataType" type. It's the only think i can see, apparently there are not defaults values. Also, I can’t change the method because I don’t have the right to do so. On the python code i try to put a JSON type for the "ScanData" parameter and he doesn't like (error like invalid argument).
The DataType of Custom can be a lot of type, String, UInt32, Int32, ...
It's possible to put directly a JSON type on input parameters when we use the "call_method" ?
How to make a custom type variable on a python program with the type stored in the opc-ua server ?
05/30/2017
What I trying to do is verify that the python code is constructing the variant properly.
This is most likely source of error.
Can you locate the DataTypeEncoding node for ns=3;i=3020 in the AddressSapce?
It is linked by a HasEncoding reference from the DataType Node.
What is the NodeId?
02/09/2023
When I search the NodeID : "ns=3;i=3020" I find the ScanData type.
The section DataTypeDefinition give us : DefaultEncodingId : "ns=3;i=5030"
BaseDataType : "i=12756"
StructureType : "2(Union)"
Field :
"[StructureField [Name="ByteString", Description="", DataType="i=15", ValueRank="-1", ArrayDimensions="[]", MaxStringLength="0", IsOptional="false"], StructureField [Name="String", Description="", DataType="i=12", ValueRank="-1", ArrayDimensions="[]", MaxStringLength="0", IsOptional="false"], StructureField [Name="Epc", Description="", DataType="ns=3;i=3024", ValueRank="-1", ArrayDimensions="[]", MaxStringLength="0", IsOptional="false"], StructureField [Name="Custom", Description="", DataType="i=24", ValueRank="-1", ArrayDimensions="[]", MaxStringLength="0", IsOptional="false"]]"
When i research the NodeId of the Encoding, i find a NodeClass name "DataType" (NodeId : i=304).
05/30/2017
When you call a method you pass an array of Variants.
Structures are stored in Variant with wrapper called an ExtensionObject.
The ExtensionObject has a field called TypeId which is the NodeId of the DataTypeEncoding Node.
So if use wireshark to look CallRequest you are sending you should see the NodeId ns=3;i=5030.
If you don't it is problem with the python APIs or how you are using the python APIs.
If you do see the NodeId ns=3;i=5030 then this possible source of the problem can be eliminated and we move on to the next possible source of error.
02/09/2023
I have new information. The OPC-UA server respects the characteristics of OPC 30010: AutoID Devices (https://reference.opcfoundatio.....v101/docs/)
ScanData is actually a Union type
02/09/2023
I have the NodeId of the encoding type. But what to do with that.
I use open62541 in my C program and I have the same problem like Python. When I call the method, I need to pass a ScanData type but I don't know how to create this with only the nodeId of the encoding or the data type.
05/30/2017
You need to find the definition of the ExtensionObject type and create instance.
To create a ExtensionObject the body must be serialized as a UA Binary blob.
For Server defined types this can be a manual step.
With SDKs like the .NET Standard SDK you can register the ScanData type with the stack and the stack will do the serialization for you.
Other SDKs will use different approaches:
This example seems to show how to pack a custom datatype into and get it out of a Variant:
1 Guest(s)