Commit 5318bda8 authored by SCHUNK-Osswald's avatar SCHUNK-Osswald Committed by ORD

Added command line support for OPC-UA method calls according to #321 (#325)

* - added tools/uacall script

* As suggested by oroulet corrected misunderstandings in signature of
Node.call_method(). No parent needed, just use self.

* Old folks, knowing Python2.x only, keep forgetting that print should
have been a function in the first place...
parent 0141aca8
......@@ -18,3 +18,6 @@ dist
newdocs
examples/history.db
*.sql
/.project
/.pydevproject
/.settings/
......@@ -13,7 +13,7 @@ OPC UA binary protocol implementation is quasi complete and has been tested agai
Most low level code is autogenerated from xml specification, thus adding missing functionnality to client or server is often trivial.
Using Python > 3.4 the dependencies are cryptography and dateutil. If using python 2.7 or pypy < 3 you also need to install enum34, trollius(asyncio), and futures(concurrent.futures), with pip for example.
Using Python > 3.4 the dependencies are cryptography and dateutil. If using python 2.7 or pypy < 3 you also need to install enum34, trollius(asyncio), and futures(concurrent.futures), with pip for example.
coveryage.py reports a test coverage of over 90% of code, most of non-tested code is autogenerate code that is not used yet.
......@@ -32,6 +32,7 @@ A set of command line tools also available: https://github.com/FreeOpcUa/python-
* uahistoryread
* uaread (read attribute of a node)
* uawrite (write attribute of a node)
* uacall (call method of a node)
* uasubscribe (subscribe to a node and print datachange events)
* uaclient (connect to server and start python shell)
* uaserver (starts a demo OPC UA server)
......@@ -52,11 +53,11 @@ Client: what works:
* history read
* login with certificate
* communication encryption
* removing nodes
* removing nodes
Tested servers: freeopcua C++, freeopcua Python, prosys, kepware, beckoff
Client: what is not implemented yet
Client: what is not implemented yet
* localized text feature
* adding some missing modify methods
* XML protocol
......@@ -74,7 +75,7 @@ Server: what works:
* basic user implementation (one existing user called admin, which can be disabled, all others are read only)
* encryption
* certificate handling
* removing nodes
* removing nodes
* history support for data change and events
Tested clients: freeopcua C++, freeopcua Python, uaexpert, prosys, quickopc
......@@ -101,7 +102,7 @@ All protocol code is under opcua directory
- opcua/common contains high level objects and methods used both in server and client
- opcua/client contains client specific code
- opcua/server contains server specific code
- opcua/utils contains some utilities function and classes
- opcua/utils contains some utilities function and classes
- opcua/tools contains code for command lines tools
- schemas contains the XML and text files from specification and the python scripts used to autogenerate code
- tests contains tests
......@@ -115,7 +116,7 @@ python tests.py
## Coverage
coverage run tests.py
coverage html
firefox htmlcov/index.html
coverage run tests.py
coverage html
firefox htmlcov/index.html
......@@ -5,7 +5,7 @@ and browse address space
from opcua import ua
from opcua.common import events
import opcua.common
import opcua.common
class Node(object):
......@@ -525,30 +525,30 @@ class Node(object):
def add_folder(self, nodeid, bname):
return opcua.common.manage_nodes.create_folder(self, nodeid, bname)
def add_object(self, nodeid, bname, objecttype=None):
return opcua.common.manage_nodes.create_object(self, nodeid, bname, objecttype)
def add_variable(self, nodeid, bname, val, varianttype=None, datatype=None):
return opcua.common.manage_nodes.create_variable(self, nodeid, bname, val, varianttype, datatype)
def add_object_type(self, nodeid, bname):
return opcua.common.manage_nodes.create_object_type(self, nodeid, bname)
def add_variable_type(self, nodeid, bname, datatype):
return opcua.common.manage_nodes.create_variable_type(self, nodeid, bname, datatype)
def add_data_type(self, nodeid, bname, description=None):
return opcua.common.manage_nodes.create_data_type(self, nodeid, bname, description=None)
def add_property(self, nodeid, bname, val, varianttype=None, datatype=None):
return opcua.common.manage_nodes.create_property(self, nodeid, bname, val, varianttype, datatype)
def add_method(self, *args):
return opcua.common.manage_nodes.create_method(self, *args)
def add_reference_type(self, parent, nodeid, bname):
return opcua.common.manage_nodes.create_reference_type(parent, nodeid, bname)
def call_method(parent, methodid, *args):
return opcua.common.methods.call_method(parent, methodid, *args)
def call_method(self, methodid, *args):
return opcua.common.methods.call_method(self, methodid, *args)
......@@ -11,7 +11,7 @@ except ImportError:
import code
def embed():
code.interact(local=dict(globals(), **locals()))
code.interact(local=dict(globals(), **locals()))
from opcua import ua
from opcua import Client
......@@ -236,7 +236,7 @@ def uawrite():
"--datatype",
dest="datatype",
default="guess",
choices=["guess", 'byte', 'sbyte', 'nodeid', 'expandednodeid', 'qualifiedname', 'browsename', 'string', 'float', 'double', 'int16', 'int32', "int64", 'uint16', 'uint32', 'uint64', "bool", "string", 'datetime', 'bytestring', 'xmlelement', 'statuscode', 'localizedtext'],
choices=["guess", 'byte', 'sbyte', 'nodeid', 'expandednodeid', 'qualifiedname', 'browsename', 'string', 'float', 'double', 'int16', 'int32', "int64", 'uint16', 'uint32', 'uint64', "bool", "string", 'datetime', 'bytestring', 'xmlelement', 'statuscode', 'localizedtext'],
help="Data type to return")
parser.add_argument("value",
help="Value to be written",
......@@ -323,7 +323,7 @@ def _lsprint_long(pnode, depth, indent=""):
print("{:30} {:25} {:25} {:10} {:30} {:25}".format("DisplayName", "NodeId", "BrowseName", "DataType", "Timestamp", "Value"))
print("")
for node in pnode.get_children():
attrs = node.get_attributes([ua.AttributeIds.DisplayName,
attrs = node.get_attributes([ua.AttributeIds.DisplayName,
ua.AttributeIds.BrowseName,
ua.AttributeIds.NodeClass,
ua.AttributeIds.WriteMask,
......@@ -566,7 +566,7 @@ def uadiscover():
#action="store_false",
#help="send a GetEndpoints request to server")
args = parse_args(parser)
client = Client(args.url, timeout=args.timeout)
if args.network:
......@@ -651,3 +651,75 @@ def uahistoryread():
finally:
client.disconnect()
sys.exit(0)
def uacall():
parser = argparse.ArgumentParser(description="Call method of a node")
add_common_args(parser)
parser.add_argument("-m",
"--method",
dest="method",
type=int,
default=None,
help="Set method to call. If not given then (single) method of the selected node is used.")
parser.add_argument("-l",
"--list",
"--array",
dest="array",
default="guess",
choices=["guess", "true", "false"],
help="Value is an array")
parser.add_argument("-t",
"--datatype",
dest="datatype",
default="guess",
choices=["guess", 'byte', 'sbyte', 'nodeid', 'expandednodeid', 'qualifiedname', 'browsename', 'string', 'float', 'double', 'int16', 'int32', "int64", 'uint16', 'uint32', 'uint64', "bool", "string", 'datetime', 'bytestring', 'xmlelement', 'statuscode', 'localizedtext'],
help="Data type to return")
parser.add_argument("value",
help="Value to use for call to method, if any",
nargs="?",
metavar="VALUE")
args = parse_args(parser, requirenodeid=True)
client = Client(args.url, timeout=args.timeout)
client.set_security_string(args.security)
client.connect()
try:
node = get_node(client, args)
# val must be a tuple in order to enable method calls without arguments
if ( args.value is None ):
val = () #empty tuple
else:
val = (_val_to_variant(args.value, args),) # tuple with one element
# determine method to call: Either explicitly given or automatically select the method of the selected node.
methods = node.get_methods()
method_id = None
#print( "methods=%s" % (methods) )
if ( args.method is None ):
if ( len( methods ) == 0 ):
raise ValueError( "No methods in selected node and no method given" )
elif ( len( methods ) == 1 ):
method_id = methods[0]
else:
raise ValueError( "Selected node has %d methods but no method given. Provide one of %s" % (methods) )
else:
for m in methods:
if ( m.nodeid.Identifier == args.method ):
method_id = m.nodeid
break
if ( method_id is None):
# last resort:
method_id = ua.NodeId( identifier=args.method )#, namespaceidx=? )#, nodeidtype=?): )
#print( "method_id=%s\nval=%s" % (method_id,val) )
result_variants = node.call_method( method_id, *val )
print( "resulting result_variants=%s" % result_variants )
finally:
client.disconnect()
sys.exit(0)
print(args)
......@@ -7,7 +7,7 @@ if sys.version_info[0] < 3:
else:
install_requires = ["python-dateutil"]
setup(name="freeopcua",
setup(name="freeopcua",
version="0.10.17",
description="Pure Python OPC-UA client and server library",
author="Olivier Roulet-Dubonnet",
......@@ -29,7 +29,7 @@ setup(name="freeopcua",
"License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)",
"Topic :: Software Development :: Libraries :: Python Modules",
],
entry_points={'console_scripts':
entry_points={'console_scripts':
[
'uaread = opcua.tools:uaread',
'uals = opcua.tools:uals',
......@@ -39,7 +39,8 @@ setup(name="freeopcua",
'uahistoryread = opcua.tools:uahistoryread',
'uaclient = opcua.tools:uaclient',
'uaserver = opcua.tools:uaserver',
'uadiscover = opcua.tools:uadiscover'
'uadiscover = opcua.tools:uadiscover',
'uacall = opcua.tools:uacall',
]
}
)
......
/tests_spaw.py
/export.xml
#!/usr/bin/env python
import sys
import os
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from opcua.tools import uacall
if __name__ == "__main__":
uacall()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment