Completed
Pull Request — master (#494)
by Olivier
04:24
created

uageneratestructs()   A

Complexity

Conditions 1

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 1
c 2
b 0
f 1
dl 0
loc 23
ccs 0
cts 14
cp 0
crap 2
rs 9.0856
1
import logging
2
import sys
3
import argparse
4
from datetime import datetime, timedelta
5
import math
6
import time
7
8
try:
9
    from IPython import embed
10
except ImportError:
11
    import code
12
13
    def embed():
14
        code.interact(local=dict(globals(), **locals()))
15
16
from opcua import ua
17
from opcua import Client
18
from opcua import Server
19
from opcua import Node
20
from opcua import uamethod
21
22
23
def add_minimum_args(parser):
24
    parser.add_argument("-u",
25
                        "--url",
26
                        help="URL of OPC UA server (for example: opc.tcp://example.org:4840)",
27
                        default='opc.tcp://localhost:4840',
28
                        metavar="URL")
29
    parser.add_argument("-v",
30
                        "--verbose",
31
                        dest="loglevel",
32
                        choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
33
                        default='WARNING',
34
                        help="Set log level")
35
    parser.add_argument("--timeout",
36
                        dest="timeout",
37
                        type=int,
38
                        default=1,
39
                        help="Set socket timeout (NOT the diverse UA timeouts)")
40
41
42
def add_common_args(parser, default_node='i=84', require_node=False):
43
    add_minimum_args(parser)
44
    parser.add_argument("-n",
45
                        "--nodeid",
46
                        help="Fully-qualified node ID (for example: i=85). Default: root node",
47
                        default=default_node,
48
                        required=require_node,
49
                        metavar="NODE")
50
    parser.add_argument("-p",
51
                        "--path",
52
                        help="Comma separated browse path to the node starting at NODE (for example: 3:Mybject,3:MyVariable)",
53
                        default='',
54
                        metavar="BROWSEPATH")
55
    parser.add_argument("-i",
56
                        "--namespace",
57
                        help="Default namespace",
58
                        type=int,
59
                        default=0,
60
                        metavar="NAMESPACE")
61
    parser.add_argument("--security",
62
                        help="Security settings, for example: Basic256,SignAndEncrypt,cert.der,pk.pem[,server_cert.der]. Default: None",
63
                        default='')
64
65
66
def _require_nodeid(parser, args):
67
    # check that a nodeid has been given explicitly, a bit hackish...
68
    if args.nodeid == "i=84" and args.path == "":
69
        parser.print_usage()
70
        print("{0}: error: A NodeId or BrowsePath is required".format(parser.prog))
71
        sys.exit(1)
72
73
74
def parse_args(parser, requirenodeid=False):
75
    args = parser.parse_args()
76
    logging.basicConfig(format="%(levelname)s: %(message)s", level=getattr(logging, args.loglevel))
77
    if args.url and '://' not in args.url:
78
        logging.info("Adding default scheme %s to URL %s", ua.OPC_TCP_SCHEME, args.url)
79
        args.url = ua.OPC_TCP_SCHEME + '://' + args.url
80
    if requirenodeid:
81
        _require_nodeid(parser, args)
82
    return args
83
84
85
def get_node(client, args):
86
    node = client.get_node(args.nodeid)
87
    if args.path:
88
        path = args.path.split(",")
89
        if node.nodeid == ua.NodeId(84, 0) and path[0] == "0:Root":
90
            # let user specify root if not node given
91
            path = path[1:]
92
        node = node.get_child(path)
93
    return node
94
95 View Code Duplication
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
96
def uaread():
97
    parser = argparse.ArgumentParser(description="Read attribute of a node, per default reads value of a node")
98
    add_common_args(parser)
99
    parser.add_argument("-a",
100
                        "--attribute",
101
                        dest="attribute",
102
                        type=int,
103
                        default=ua.AttributeIds.Value,
104
                        help="Set attribute to read")
105
    parser.add_argument("-t",
106
                        "--datatype",
107
                        dest="datatype",
108
                        default="python",
109
                        choices=['python', 'variant', 'datavalue'],
110
                        help="Data type to return")
111
112
    args = parse_args(parser, requirenodeid=True)
113
114
    client = Client(args.url, timeout=args.timeout)
115
    client.set_security_string(args.security)
116
    client.connect()
117
    try:
118
        node = get_node(client, args)
119
        attr = node.get_attribute(args.attribute)
120
        if args.datatype == "python":
121
            print(attr.Value.Value)
122
        elif args.datatype == "variant":
123
            print(attr.Value)
124
        else:
125
            print(attr)
126
    finally:
127
        client.disconnect()
128
    sys.exit(0)
129
    print(args)
130
131
132
def _args_to_array(val, array):
133
    if array == "guess":
134
        if "," in val:
135
            array = "true"
136
    if array == "true":
137
        val = val.split(",")
138
    return val
139
140
141
def _arg_to_bool(val):
142
    return val in ("true", "True")
143
144
145
def _arg_to_variant(val, array, ptype, varianttype=None):
146
    val = _args_to_array(val, array)
147
    if isinstance(val, list):
148
        val = [ptype(i) for i in val]
149
    else:
150
        val = ptype(val)
151
    if varianttype:
152
        return ua.Variant(val, varianttype)
153
    else:
154
        return ua.Variant(val)
155
156
157
def _val_to_variant(val, args):
158
    array = args.array
159
    if args.datatype == "guess":
160
        if val in ("true", "True", "false", "False"):
161
            return _arg_to_variant(val, array, _arg_to_bool)
162
        try:
163
            return _arg_to_variant(val, array, int)
164
        except ValueError:
165
            try:
166
                return _arg_to_variant(val, array, float)
167
            except ValueError:
168
                return _arg_to_variant(val, array, str)
169
    elif args.datatype == "bool":
170
        if val in ("1", "True", "true"):
171
            return ua.Variant(True, ua.VariantType.Boolean)
172
        else:
173
            return ua.Variant(False, ua.VariantType.Boolean)
174
    elif args.datatype == "sbyte":
175
        return _arg_to_variant(val, array, int, ua.VariantType.SByte)
176
    elif args.datatype == "byte":
177
        return _arg_to_variant(val, array, int, ua.VariantType.Byte)
178
    #elif args.datatype == "uint8":
179
        #return _arg_to_variant(val, array, int, ua.VariantType.Byte)
180
    elif args.datatype == "uint16":
181
        return _arg_to_variant(val, array, int, ua.VariantType.UInt16)
182
    elif args.datatype == "uint32":
183
        return _arg_to_variant(val, array, int, ua.VariantType.UInt32)
184
    elif args.datatype == "uint64":
185
        return _arg_to_variant(val, array, int, ua.VariantType.UInt64)
186
    #elif args.datatype == "int8":
187
        #return ua.Variant(int(val), ua.VariantType.Int8)
188
    elif args.datatype == "int16":
189
        return _arg_to_variant(val, array, int, ua.VariantType.Int16)
190
    elif args.datatype == "int32":
191
        return _arg_to_variant(val, array, int, ua.VariantType.Int32)
192
    elif args.datatype == "int64":
193
        return _arg_to_variant(val, array, int, ua.VariantType.Int64)
194
    elif args.datatype == "float":
195
        return _arg_to_variant(val, array, float, ua.VariantType.Float)
196
    elif args.datatype == "double":
197
        return _arg_to_variant(val, array, float, ua.VariantType.Double)
198
    elif args.datatype == "string":
199
        return _arg_to_variant(val, array, str, ua.VariantType.String)
200
    elif args.datatype == "datetime":
201
        raise NotImplementedError
202
    elif args.datatype == "Guid":
203
        return _arg_to_variant(val, array, bytes, ua.VariantType.Guid)
204
    elif args.datatype == "ByteString":
205
        return _arg_to_variant(val, array, bytes, ua.VariantType.ByteString)
206
    elif args.datatype == "xml":
207
        return _arg_to_variant(val, array, str, ua.VariantType.XmlElement)
208
    elif args.datatype == "nodeid":
209
        return _arg_to_variant(val, array, ua.NodeId.from_string, ua.VariantType.NodeId)
210
    elif args.datatype == "expandednodeid":
211
        return _arg_to_variant(val, array, ua.ExpandedNodeId.from_string, ua.VariantType.ExpandedNodeId)
212
    elif args.datatype == "statuscode":
213
        return _arg_to_variant(val, array, int, ua.VariantType.StatusCode)
214
    elif args.datatype in ("qualifiedname", "browsename"):
215
        return _arg_to_variant(val, array, ua.QualifiedName.from_string, ua.VariantType.QualifiedName)
216
    elif args.datatype == "LocalizedText":
217
        return _arg_to_variant(val, array, ua.LocalizedText, ua.VariantType.LocalizedText)
218
219
220
def uawrite():
221
    parser = argparse.ArgumentParser(description="Write attribute of a node, per default write value of node")
222
    add_common_args(parser)
223
    parser.add_argument("-a",
224
                        "--attribute",
225
                        dest="attribute",
226
                        type=int,
227
                        default=ua.AttributeIds.Value,
228
                        help="Set attribute to read")
229
    parser.add_argument("-l",
230
                        "--list",
231
                        "--array",
232
                        dest="array",
233
                        default="guess",
234
                        choices=["guess", "true", "false"],
235
                        help="Value is an array")
236
    parser.add_argument("-t",
237
                        "--datatype",
238
                        dest="datatype",
239
                        default="guess",
240
                        choices=["guess", 'byte', 'sbyte', 'nodeid', 'expandednodeid', 'qualifiedname', 'browsename', 'string', 'float', 'double', 'int16', 'int32', "int64", 'uint16', 'uint32', 'uint64', "bool", "string", 'datetime', 'bytestring', 'xmlelement', 'statuscode', 'localizedtext'],
241
                        help="Data type to return")
242
    parser.add_argument("value",
243
                        help="Value to be written",
244
                        metavar="VALUE")
245
    args = parse_args(parser, requirenodeid=True)
246
247
    client = Client(args.url, timeout=args.timeout)
248
    client.set_security_string(args.security)
249
    client.connect()
250
    try:
251
        node = get_node(client, args)
252
        val = _val_to_variant(args.value, args)
253
        node.set_attribute(args.attribute, ua.DataValue(val))
254
    finally:
255
        client.disconnect()
256
    sys.exit(0)
257
    print(args)
258
259 View Code Duplication
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
260
def uals():
261
    parser = argparse.ArgumentParser(description="Browse OPC-UA node and print result")
262
    add_common_args(parser)
263
    parser.add_argument("-l",
264
                        dest="long_format",
265
                        const=3,
266
                        nargs="?",
267
                        type=int,
268
                        help="use a long listing format")
269
    parser.add_argument("-d",
270
                        "--depth",
271
                        default=1,
272
                        type=int,
273
                        help="Browse depth")
274
275
    args = parse_args(parser)
276
    if args.long_format is None:
277
        args.long_format = 1
278
279
    client = Client(args.url, timeout=args.timeout)
280
    client.set_security_string(args.security)
281
    client.connect()
282
    try:
283
        node = get_node(client, args)
284
        print("Browsing node {0} at {1}\n".format(node, args.url))
285
        if args.long_format == 0:
286
            _lsprint_0(node, args.depth - 1)
287
        elif args.long_format == 1:
288
            _lsprint_1(node, args.depth - 1)
289
        else:
290
            _lsprint_long(node, args.depth - 1)
291
    finally:
292
        client.disconnect()
293
    sys.exit(0)
294
    print(args)
295
296
297
def _lsprint_0(node, depth, indent=""):
298
    if not indent:
299
        print("{0:30} {1:25}".format("DisplayName", "NodeId"))
300
        print("")
301
    for desc in node.get_children_descriptions():
302
        print("{0}{1:30} {2:25}".format(indent, desc.DisplayName.to_string(), desc.NodeId.to_string()))
303
        if depth:
304
            _lsprint_0(Node(node.server, desc.NodeId), depth - 1, indent + "  ")
305
306
307
def _lsprint_1(node, depth, indent=""):
308
    if not indent:
309
        print("{0:30} {1:25} {2:25} {3:25}".format("DisplayName", "NodeId", "BrowseName", "Value"))
310
        print("")
311
312
    for desc in node.get_children_descriptions():
313
        if desc.NodeClass == ua.NodeClass.Variable:
314
            val = Node(node.server, desc.NodeId).get_value()
315
            print("{0}{1:30} {2!s:25} {3!s:25}, {4!s:3}".format(indent, desc.DisplayName.to_string(), desc.NodeId.to_string(), desc.BrowseName.to_string(), val))
316
        else:
317
            print("{0}{1:30} {2!s:25} {3!s:25}".format(indent, desc.DisplayName.to_string(), desc.NodeId.to_string(), desc.BrowseName.to_string()))
318
        if depth:
319
            _lsprint_1(Node(node.server, desc.NodeId), depth - 1, indent + "  ")
320
321
322
def _lsprint_long(pnode, depth, indent=""):
323
    if not indent:
324
        print("{0:30} {1:25} {2:25} {3:10} {4:30} {5:25}".format("DisplayName", "NodeId", "BrowseName", "DataType", "Timestamp", "Value"))
325
        print("")
326
    for node in pnode.get_children():
327
        attrs = node.get_attributes([ua.AttributeIds.DisplayName,
328
                                     ua.AttributeIds.BrowseName,
329
                                     ua.AttributeIds.NodeClass,
330
                                     ua.AttributeIds.WriteMask,
331
                                     ua.AttributeIds.UserWriteMask,
332
                                     ua.AttributeIds.DataType,
333
                                     ua.AttributeIds.Value])
334
        name, bname, nclass, mask, umask, dtype, val = [attr.Value.Value for attr in attrs]
335
        update = attrs[-1].ServerTimestamp
336
        if nclass == ua.NodeClass.Variable:
337
            print("{0}{1:30} {2:25} {3:25} {4:10} {5!s:30} {6!s:25}".format(indent, name.to_string(), node.nodeid.to_string(), bname.to_string(), dtype.to_string(), update, val))
338
        else:
339
            print("{0}{1:30} {2:25} {3:25}".format(indent, name.to_string(), bname.to_string(), node.nodeid.to_string()))
340
        if depth:
341
            _lsprint_long(node, depth - 1, indent + "  ")
342
343
344
class SubHandler(object):
345
346
    def datachange_notification(self, node, val, data):
347
        print("New data change event", node, val, data)
348
349
    def event_notification(self, event):
350
        print("New event", event)
351
352
353
def uasubscribe():
354
    parser = argparse.ArgumentParser(description="Subscribe to a node and print results")
355
    add_common_args(parser)
356
    parser.add_argument("-t",
357
                        "--eventtype",
358
                        dest="eventtype",
359
                        default="datachange",
360
                        choices=['datachange', 'event'],
361
                        help="Event type to subscribe to")
362
363
    args = parse_args(parser, requirenodeid=False)
364
    if args.eventtype == "datachange":
365
        _require_nodeid(parser, args)
366
    else:
367
        # FIXME: this is broken, someone may have written i=84 on purpose
368
        if args.nodeid == "i=84" and args.path == "":
369
            args.nodeid = "i=2253"
370
371
    client = Client(args.url, timeout=args.timeout)
372
    client.set_security_string(args.security)
373
    client.connect()
374
    try:
375
        node = get_node(client, args)
376
        handler = SubHandler()
377
        sub = client.create_subscription(500, handler)
378
        if args.eventtype == "datachange":
379
            sub.subscribe_data_change(node)
380
        else:
381
            sub.subscribe_events(node)
382
        print("Type Ctr-C to exit")
383
        while True:
384
            time.sleep(1)
385
    finally:
386
        client.disconnect()
387
    sys.exit(0)
388
    print(args)
389
390
391
def application_to_strings(app):
392
    result = []
393
    result.append(('Application URI', app.ApplicationUri))
394
    optionals = [
395
        ('Product URI', app.ProductUri),
396
        ('Application Name', app.ApplicationName.to_string()),
397
        ('Application Type', str(app.ApplicationType)),
398
        ('Gateway Server URI', app.GatewayServerUri),
399
        ('Discovery Profile URI', app.DiscoveryProfileUri),
400
    ]
401
    for (n, v) in optionals:
402
        if v:
403
            result.append((n, v))
404
    for url in app.DiscoveryUrls:
405
        result.append(('Discovery URL', url))
406
    return result  # ['{}: {}'.format(n, v) for (n, v) in result]
407
408
409
def cert_to_string(der):
410
    if not der:
411
        return '[no certificate]'
412
    try:
413
        from opcua.crypto import uacrypto
414
    except ImportError:
415
        return "{0} bytes".format(len(der))
416
    cert = uacrypto.x509_from_der(der)
417
    return uacrypto.x509_to_string(cert)
418
419
420
def endpoint_to_strings(ep):
421
    result = [('Endpoint URL', ep.EndpointUrl)]
422
    result += application_to_strings(ep.Server)
423
    result += [
424
        ('Server Certificate', cert_to_string(ep.ServerCertificate)),
425
        ('Security Mode', str(ep.SecurityMode)),
426
        ('Security Policy URI', ep.SecurityPolicyUri)]
427
    for tok in ep.UserIdentityTokens:
428
        result += [
429
            ('User policy', tok.PolicyId),
430
            ('  Token type', str(tok.TokenType))]
431
        if tok.IssuedTokenType or tok.IssuerEndpointUrl:
432
            result += [
433
                ('  Issued Token type', tok.IssuedTokenType),
434
                ('  Issuer Endpoint URL', tok.IssuerEndpointUrl)]
435
        if tok.SecurityPolicyUri:
436
            result.append(('  Security Policy URI', tok.SecurityPolicyUri))
437
    result += [
438
        ('Transport Profile URI', ep.TransportProfileUri),
439
        ('Security Level', ep.SecurityLevel)]
440
    return result
441
442
443
def uaclient():
444
    parser = argparse.ArgumentParser(description="Connect to server and start python shell. root and objects nodes are available. Node specificed in command line is available as mynode variable")
445
    add_common_args(parser)
446
    parser.add_argument("-c",
447
                        "--certificate",
448
                        help="set client certificate")
449
    parser.add_argument("-k",
450
                        "--private_key",
451
                        help="set client private key")
452
    args = parse_args(parser)
453
454
    client = Client(args.url, timeout=args.timeout)
455
    client.set_security_string(args.security)
456
    if args.certificate:
457
        client.load_client_certificate(args.certificate)
458
    if args.private_key:
459
        client.load_private_key(args.private_key)
460
    client.connect()
461
    try:
462
        root = client.get_root_node()
463
        objects = client.get_objects_node()
464
        mynode = get_node(client, args)
465
        embed()
466
    finally:
467
        client.disconnect()
468
    sys.exit(0)
469
470
471
def uaserver():
472
    parser = argparse.ArgumentParser(description="Run an example OPC-UA server. By importing xml definition and using uawrite command line, it is even possible to expose real data using this server")
473
    # we setup a server, this is a bit different from other tool so we do not reuse common arguments
474
    parser.add_argument("-u",
475
                        "--url",
476
                        help="URL of OPC UA server, default is opc.tcp://0.0.0.0:4840",
477
                        default='opc.tcp://0.0.0.0:4840',
478
                        metavar="URL")
479
    parser.add_argument("-v",
480
                        "--verbose",
481
                        dest="loglevel",
482
                        choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
483
                        default='WARNING',
484
                        help="Set log level")
485
    parser.add_argument("-x",
486
                        "--xml",
487
                        metavar="XML_FILE",
488
                        help="Populate address space with nodes defined in XML")
489
    parser.add_argument("-p",
490
                        "--populate",
491
                        action="store_true",
492
                        help="Populate address space with some sample nodes")
493
    parser.add_argument("-c",
494
                        "--disable-clock",
495
                        action="store_true",
496
                        help="Disable clock, to avoid seeing many write if debugging an application")
497
    parser.add_argument("-s",
498
                        "--shell",
499
                        action="store_true",
500
                        help="Start python shell instead of randomly changing node values")
501
    parser.add_argument("--certificate",
502
                        help="set server certificate")
503
    parser.add_argument("--private_key",
504
                        help="set server private key")
505
    args = parser.parse_args()
506
    logging.basicConfig(format="%(levelname)s: %(message)s", level=getattr(logging, args.loglevel))
507
508
    server = Server()
509
    server.set_endpoint(args.url)
510
    if args.certificate:
511
        server.load_certificate(args.certificate)
512
    if args.private_key:
513
        server.load_private_key(args.private_key)
514
    server.disable_clock(args.disable_clock)
515
    server.set_server_name("FreeOpcUa Example Server")
516
    if args.xml:
517
        server.import_xml(args.xml)
518
    if args.populate:
519
        @uamethod
520
        def multiply(parent, x, y):
521
            print("multiply method call with parameters: ", x, y)
522
            return x * y
523
524
        uri = "http://examples.freeopcua.github.io"
525
        idx = server.register_namespace(uri)
526
        objects = server.get_objects_node()
527
        myobj = objects.add_object(idx, "MyObject")
528
        mywritablevar = myobj.add_variable(idx, "MyWritableVariable", 6.7)
529
        mywritablevar.set_writable()    # Set MyVariable to be writable by clients
530
        myvar = myobj.add_variable(idx, "MyVariable", 6.7)
531
        myarrayvar = myobj.add_variable(idx, "MyVarArray", [6.7, 7.9])
532
        myprop = myobj.add_property(idx, "MyProperty", "I am a property")
533
        mymethod = myobj.add_method(idx, "MyMethod", multiply, [ua.VariantType.Double, ua.VariantType.Int64], [ua.VariantType.Double])
534
535
    server.start()
536
    try:
537
        if args.shell:
538
            embed()
539
        elif args.populate:
540
            count = 0
541
            while True:
542
                time.sleep(1)
543
                myvar.set_value(math.sin(count / 10))
544
                myarrayvar.set_value([math.sin(count / 10), math.sin(count / 100)])
545
                count += 1
546
        else:
547
            while True:
548
                time.sleep(1)
549
    finally:
550
        server.stop()
551
    sys.exit(0)
552
553
554
def uadiscover():
555
    parser = argparse.ArgumentParser(description="Performs OPC UA discovery and prints information on servers and endpoints.")
556
    add_minimum_args(parser)
557
    parser.add_argument("-n",
558
                        "--network",
559
                        action="store_true",
560
                        help="Also send a FindServersOnNetwork request to server")
561
    #parser.add_argument("-s",
562
                        #"--servers",
563
                        #action="store_false",
564
                        #help="send a FindServers request to server")
565
    #parser.add_argument("-e",
566
                        #"--endpoints",
567
                        #action="store_false",
568
                        #help="send a GetEndpoints request to server")
569
    args = parse_args(parser)
570
571
    client = Client(args.url, timeout=args.timeout)
572
573
    if args.network:
574
        print("Performing discovery at {0}\n".format(args.url))
575
        for i, server in enumerate(client.connect_and_find_servers_on_network(), start=1):
576
            print('Server {0}:'.format(i))
577
            #for (n, v) in application_to_strings(server):
578
                #print('  {}: {}'.format(n, v))
579
            print('')
580
581
    print("Performing discovery at {0}\n".format(args.url))
582
    for i, server in enumerate(client.connect_and_find_servers(), start=1):
583
        print('Server {0}:'.format(i))
584
        for (n, v) in application_to_strings(server):
585
            print('  {0}: {1}'.format(n, v))
586
        print('')
587
588
    for i, ep in enumerate(client.connect_and_get_server_endpoints(), start=1):
589
        print('Endpoint {0}:'.format(i))
590
        for (n, v) in endpoint_to_strings(ep):
591
            print('  {0}: {1}'.format(n, v))
592
        print('')
593
594
    sys.exit(0)
595
596
597
def print_history(o):
598
    if isinstance(o, ua.HistoryData):
599
        print("{0:30} {1:10} {2}".format('Source timestamp', 'Status', 'Value'))
600
        for d in o.DataValues:
601
            print("{0:30} {1:10} {2}".format(str(d.SourceTimestamp), d.StatusCode.name, d.Value))
602
603
604
def str_to_datetime(s, default=None):
605
    if not s:
606
        if default is not None:
607
            return default
608
        return datetime.utcnow()
609
    # FIXME: try different datetime formats
610
    for fmt in ["%Y-%m-%d", "%Y-%m-%d %H:%M", "%Y-%m-%d %H:%M:%S"]:
611
        try:
612
            return datetime.strptime(s, fmt)
613
        except ValueError:
614
            pass
615
616
617
def uahistoryread():
618
    parser = argparse.ArgumentParser(description="Read history of a node")
619
    add_common_args(parser)
620
    parser.add_argument("--starttime",
621
                        default=None,
622
                        help="Start time, formatted as YYYY-MM-DD [HH:MM[:SS]]. Default: current time - one day")
623
    parser.add_argument("--endtime",
624
                        default=None,
625
                        help="End time, formatted as YYYY-MM-DD [HH:MM[:SS]]. Default: current time")
626
    parser.add_argument("-e",
627
                        "--events",
628
                        action="store_true",
629
                        help="Read event history instead of data change history")
630
    parser.add_argument("-l",
631
                        "--limit",
632
                        type=int,
633
                        default=10,
634
                        help="Maximum number of notfication to return")
635
636
    args = parse_args(parser, requirenodeid=True)
637
638
    client = Client(args.url, timeout=args.timeout)
639
    client.set_security_string(args.security)
640
    client.connect()
641
    try:
642
        node = get_node(client, args)
643
        starttime = str_to_datetime(args.starttime, datetime.utcnow() - timedelta(days=1))
644
        endtime = str_to_datetime(args.endtime, datetime.utcnow())
645
        print("Reading raw history of node {0} at {1}; start at {2}, end at {3}\n".format(node, args.url, starttime, endtime))
646
        if args.events:
647
            evs = node.read_event_history(starttime, endtime, numvalues=args.limit)
648
            for ev in evs:
649
                print(ev)
650
        else:
651
            print_history(node.read_raw_history(starttime, endtime, numvalues=args.limit))
652
    finally:
653
        client.disconnect()
654
    sys.exit(0)
655
656
657
def uacall():
658
    parser = argparse.ArgumentParser(description="Call method of a node")
659
    add_common_args(parser)
660
    parser.add_argument("-m",
661
                        "--method",
662
                        dest="method",
663
                        type=int,
664
                        default=None,
665
                        help="Set method to call. If not given then (single) method of the selected node is used.")
666
    parser.add_argument("-l",
667
                        "--list",
668
                        "--array",
669
                        dest="array",
670
                        default="guess",
671
                        choices=["guess", "true", "false"],
672
                        help="Value is an array")
673
    parser.add_argument("-t",
674
                        "--datatype",
675
                        dest="datatype",
676
                        default="guess",
677
                        choices=["guess", 'byte', 'sbyte', 'nodeid', 'expandednodeid', 'qualifiedname', 'browsename', 'string', 'float', 'double', 'int16', 'int32', "int64", 'uint16', 'uint32', 'uint64', "bool", "string", 'datetime', 'bytestring', 'xmlelement', 'statuscode', 'localizedtext'],
678
                        help="Data type to return")
679
    parser.add_argument("value",
680
                        help="Value to use for call to method, if any",
681
                        nargs="?",
682
                        metavar="VALUE")
683
684
    args = parse_args(parser, requirenodeid=True)
685
686
    client = Client(args.url, timeout=args.timeout)
687
    client.set_security_string(args.security)
688
    client.connect()
689
    try:
690
        node = get_node(client, args)
691
        # val must be a tuple in order to enable method calls without arguments
692
        if ( args.value is None ):
693
            val = () #empty tuple
694
        else:
695
            val = (_val_to_variant(args.value, args),) # tuple with one element
696
697
        # determine method to call: Either explicitly given or automatically select the method of the selected node.
698
        methods = node.get_methods()
699
        method_id = None
700
        #print( "methods=%s" % (methods) )
701
702
        if ( args.method is None ):
703
            if ( len( methods ) == 0 ):
704
                raise ValueError( "No methods in selected node and no method given" )
705
            elif ( len( methods ) == 1 ):
706
                method_id = methods[0]
707
            else:
708
                raise ValueError( "Selected node has {0:d} methods but no method given. Provide one of {1!s}".format(*(methods)) )
709
        else:
710
            for m in methods:
711
                if ( m.nodeid.Identifier == args.method ):
712
                    method_id = m.nodeid
713
                    break
714
715
        if ( method_id is None):
716
            # last resort:
717
            method_id = ua.NodeId( identifier=args.method )#, namespaceidx=? )#, nodeidtype=?): )
718
719
        #print( "method_id=%s\nval=%s" % (method_id,val) )
720
721
        result_variants = node.call_method( method_id, *val )
722
        print( "resulting result_variants={0!s}".format(result_variants) )
723
    finally:
724
        client.disconnect()
725
    sys.exit(0)
726
    print(args)
727
728
729
def uageneratestructs():
730
    parser = argparse.ArgumentParser(description="Generate a Python module from the xml structure definition (.bsd)")
731
    add_common_args(parser, require_node=True)
732
    parser.add_argument("-o",
733
                        "--output",
734
                        dest="output_path",
735
                        required=True,
736
                        type=str,
737
                        default=None,
738
                        help="The python file to be generated.",
739
                        )
740
    args = parse_args(parser, requirenodeid=True)
741
742
    client = Client(args.url, timeout=args.timeout)
743
    client.set_security_string(args.security)
744
    client.connect()
745
    try:
746
        node = get_node(client, args)
747
        generators, _ = client.load_type_definitions([node])
748
        generators[0].save_to_file(args.output_path, True)
749
    finally:
750
        client.disconnect()
751
    sys.exit(0)
752