FreeOpcUa / python-opcua

LGPL Pure Python OPC-UA Client and Server
http://freeopcua.github.io/
GNU Lesser General Public License v3.0
1.32k stars 661 forks source link

How to use method call in client with complex arguments #1521

Closed hbszjjw closed 1 year ago

hbszjjw commented 1 year ago

I have a question about method call, I have a RFID reader which I have to use method call to read tag, the method name is ReadTag which is declared like:

ReadTag (
[in] ScanData Identifier
[in] CodeTypeDataType CodeType
[in] UInt16 Region
[in] UInt32 Offset
[in] UInt32 Length
[in] ByteString Password
[out] ByteString ResultData
[out] AutoIdOperationStatusEnumeration Status);

the problem is I don't know how to assign these structure arguments Identifier and CodeType when method call. I just do it like this:

client.connect()
client.load_type_definitions()
ex = ua.ScanData
identifier = ua.Variant(ex, ua.VariantType.ExtensionObject)
codeType = ua.Variant('', ua.VariantType.String)
region = ua.Variant(3, ua.VariantType.UInt16)
offset = ua.Variant(0, ua.VariantType.UInt32)
length = ua.Variant(10, ua.VariantType.UInt32)
password = ua.Variant('', ua.VariantType.ByteString)
arguments = [identifier, codeType, region, offset, length, password]
readPoint11 = client.get_node("ns=4;i=5002")
readTag = client.get_node("ns=4;i=7009")
out = readPoint11.call_method(readTag, arguments)

ScanData is a Union type compose of 4 differents items: ByteString ; String ; Epc ; Custom.

ScanData: Union
  ByteString: ByteString
  String: String
  Epc: ScanDataEpc
  Custom: BaseDataType

actually I just need set its value to null in UaExpert, but I don't know how to do it in Python.

actually, I don't need assign the arguments Identifier with actual value, just null is ok, and also for code type empty string, but after I run this code, it returns nothing, it only show 'type' in terminal, and even I use print(out), it also show nothing.

can anyone give me instructions about how to use method call with complex arguments, thanks!

schroeder- commented 1 year ago

I don't know if you can get it working with python-opcua, But my guess is that you have to init a class instead of passing the class: ex = ua.ScanData()

I would switch to asyncua which has a sync wrapper for easy porting. Also this library isn't supported so another bonous if you switch to asyncua. The code would look like this:

client.connect()
client.load_type_definitions()
ex = ua.ScanData
identifier = ua.ScanData()
codeType = ''
region = ua.UInt16(3)
offset = ua.UInt32(0)
length = ua.UInt32(10)
password = b''
arguments = [identifier, codeType, region, offset, length, password]
readPoint11 = client.get_node("ns=4;i=5002")
readTag = client.get_node("ns=4;i=7009")
out = readPoint11.call_method(readTag, arguments)
hbszjjw commented 1 year ago

thanks for your suggestion, but I can't set variable as ua.UInt16(3) , maybe beacuse I use opcua package, now I change my code as follows:

  eo = ua.ScanData()
  # eo.SwitchField = 0
  identifier = ua.Variant(eo, ua.VariantType.ExtensionObject)
  codeType = ua.Variant('', ua.VariantType.String)
  region = ua.Variant(3, ua.VariantType.UInt16)
  offset = ua.Variant(0, ua.VariantType.UInt32)
  length = ua.Variant(10, ua.VariantType.UInt32)
  password = ua.Variant(b'', ua.VariantType.ByteString)

after I run, it throws following error:


Traceback (most recent call last):
  File "C:\03_Python_Project\SiemensRfidReader\main.py", line 64, in StartSiemensRfidOpcUaClient
    out = readPoint11.call_method(readTag, arguments)
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\common\node.py", line 685, in call_method
    return opcua.common.methods.call_method(self, methodid, *args)
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\common\methods.py", line 17, in call_method
    result = call_method_full(parent, methodid, *args)
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\common\methods.py", line 40, in call_method_full
    result = _call_method(parent.server, parent.nodeid, methodid, to_variant(*args))
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\common\methods.py", line 51, in _call_method
    results = server.call(methodstocall)
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\client\ua_client.py", line 602, in call
    data = self._uasocket.send_request(request)
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\client\ua_client.py", line 81, in send_request
    future = self._send_request(request, callback, timeout, message_type)
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\client\ua_client.py", line 55, in _send_request
    binreq = struct_to_binary(request)
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 261, in struct_to_binary
    packet.append(to_binary(uatype, val))
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 284, in to_binary
    return struct_to_binary(val)
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 256, in struct_to_binary
    packet.append(list_to_binary(uatype[6:], val))
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 296, in list_to_binary
    pack = [to_binary(uatype, el) for el in val]
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 296, in <listcomp>
    pack = [to_binary(uatype, el) for el in val]
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 284, in to_binary
    return struct_to_binary(val)
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 256, in struct_to_binary
    packet.append(list_to_binary(uatype[6:], val))
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 296, in list_to_binary
    pack = [to_binary(uatype, el) for el in val]
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 296, in <listcomp>
    pack = [to_binary(uatype, el) for el in val]
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 274, in to_binary
    return pack_uatype(vtype, val)
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 192, in pack_uatype
    return variant_to_binary(value)
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 373, in variant_to_binary
    b.append(pack_uatype_array(var.VariantType, ua.flatten(var.Value)))
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 224, in pack_uatype_array
    b = [pack_uatype(vtype, val) for val in array]
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 224, in <listcomp>
    b = [pack_uatype(vtype, val) for val in array]
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 192, in pack_uatype
    return variant_to_binary(value)
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 378, in variant_to_binary
    b.append(pack_uatype(var.VariantType, var.Value))
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 188, in pack_uatype
    return extensionobject_to_binary(value)
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 460, in extensionobject_to_binary
    Body = struct_to_binary(obj)
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 261, in struct_to_binary
    packet.append(to_binary(uatype, val))
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 276, in to_binary
    return getattr(Primitives, uatype).pack(val)
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\opcua\ua\ua_binary.py", line 69, in pack
    string = string.encode('utf-8')
AttributeError: 'bytes' object has no attribute 'encode'
schroeder- commented 1 year ago

In asyncua it is working because i implemented it there see: https://github.com/FreeOpcUa/opcua-asyncio/issues/1093 https://github.com/FreeOpcUa/opcua-asyncio/issues/817

hbszjjw commented 1 year ago

thanks, but I still have problems, now I do it like this:

from asyncua.sync import Client, ua

....

            identifier = ua.ScanData()
            codeType = ''
            region = ua.UInt16(3)
            offset = ua.UInt32(0)
            length = ua.UInt32(10)
            password = b''
            arguments = [identifier, codeType, region, offset, length, password]
            out = readPoint11.call_method(readTag, arguments)

it throws exception:

....
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\asyncua\ua\ua_binary.py", line 564, in extensionobject_to_binary
    type_id = ua.extension_object_typeids[obj.__class__.__name__]
KeyError: 'str'

if I do it like this:

            eo = ua.ScanData()
            eo.SwitchField = 0
            identifier = ua.Variant(eo, ua.VariantType.ExtensionObject)
            codeType = ua.Variant('', ua.VariantType.String)
            region = ua.Variant(3, ua.VariantType.UInt16)
            offset = ua.Variant(0, ua.VariantType.UInt32)
            length = ua.Variant(10, ua.VariantType.UInt32)
            password = ua.Variant(b'', ua.VariantType.ByteString)

it throws exception:

...
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\asyncua\ua\uatypes.py", line 372, in check
    raise UaStatusCodeError(self.value)
asyncua.ua.uaerrors._auto.BadArgumentsMissing: "The client did not specify all of the input arguments for the method."(BadArgumentsMissing)
schroeder- commented 1 year ago

Try client.load_data_type_definitions() instead of load_type_definitions()

hbszjjw commented 1 year ago

thanks, the error is the same, and I checked with WireShark, I found the successful call request by UaExpert, the ArraySzie of InputArguments is 6, but for the failed call request, the ArraySzie of InputArguments is 1, and in Variant[0], it has the another level which is the same as the the successful call request, that means it has one more level. I want to share the picture, but git always shows: Something went really wrong, and we can’t process that file.

schroeder- commented 1 year ago

May bad the last parameter should be ByteString: identifier = ua.ScanData() codeType = '' region = ua.UInt16(3) offset = ua.UInt32(0) length = ua.UInt32(10) password = ua.ByteString (b'')

hbszjjw commented 1 year ago

I can't use this style assignment, if I do it as follows:

        identifier = ua.ScanData()
        codeType = ''
        region = ua.UInt16(3)
        offset = ua.UInt32(0)
        length = ua.UInt32(10)
        password = ua.ByteString(b'')

it will throw exception:

......
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\asyncua\ua\ua_binary.py", line 564, in extensionobject_to_binary
    type_id = ua.extension_object_typeids[obj.__class__.__name__]
KeyError: 'str'
schroeder- commented 1 year ago
identifier = ua.Variant(ua.ScanData())
codeType = ua.Variant('')
region = ua.Variant(ua.UInt16(3))
offset = ua.Variant(ua.UInt32(0))
length = ua.Variant(ua.UInt32(10))
password = ua.Variant(ua.ByteString (b''))
readPoint11.call_method(readTag, identifier, codeType , region, offset, length , password )
hbszjjw commented 1 year ago

great, It works now, but it still throw another exception:

  File "<string>", line 7, in __init__
  File "C:\03_Python_Project\SiemensRfidReader\venv\lib\site-packages\asyncua\ua\uatypes.py", line 904, in __post_init__
    raise UaError(
asyncua.ua.uaerrors._base.UaError: Non array Variant of type VariantType.ByteString cannot have value None

and I checked with WireShark again, the InputArguments is ok now, but in EncodingMask of ObjectId and MethodId is 0010 (Numeric of arbitrary length) which it is 0001(Four byte encoded Numeric) with UaExpert.

schroeder- commented 1 year ago

Should not matter, this is more a message length optimistation. How is the response? Can you post either the a complete wireshark trace or content of the response?

schroeder- commented 1 year ago

If you want the same request use this instead, because from string is not setting the length type: readPoint11 = client.get_node(ua.NodeId(5002, 4)) readTag = client.get_node(ua.NodeId(7009, 4))

hbszjjw commented 1 year ago

it seems I can get the result, but something with the device now, when I read, its error LED blinks, but I check with WireShark, the result is the same as UaExpert, thank you very much, I think the main problem is solved.

and I tested with:

readPoint11 = client.get_node(ua.NodeId(5002, 4))
readTag = client.get_node(ua.NodeId(7009, 4))

the last error still have, if this error happens, I can't read output from Python.

hbszjjw commented 1 year ago

thank you very much, the problem is solved because of some setting in the device, now the error disappears.