Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
O
opcua-asyncio
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Nikola Balog
opcua-asyncio
Commits
b698bc87
Commit
b698bc87
authored
Sep 12, 2017
by
oroulet
Committed by
oroulet
Sep 15, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
move all custom structure code to one file. ensure supported python class names
parent
c2f348d9
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
66 additions
and
65 deletions
+66
-65
opcua/client/client.py
opcua/client/client.py
+5
-45
opcua/common/structures.py
opcua/common/structures.py
+60
-19
tests/tests_unit.py
tests/tests_unit.py
+1
-1
No files found.
opcua/client/client.py
View file @
b698bc87
...
...
@@ -16,7 +16,7 @@ from opcua.common.subscription import Subscription
from
opcua.common
import
utils
from
opcua.crypto
import
security_policies
from
opcua.common.shortcuts
import
Shortcuts
from
opcua.common.structures
_generator
import
StructGenerator
from
opcua.common.structures
import
load_type_definitions
use_crypto
=
True
try
:
from
opcua.crypto
import
uacrypto
...
...
@@ -110,6 +110,7 @@ class Client(object):
self
.
uaclient
=
UaClient
(
timeout
)
self
.
user_certificate
=
None
self
.
user_private_key
=
None
self
.
_server_nonce
=
None
self
.
_session_counter
=
1
self
.
keepalive
=
None
self
.
nodes
=
Shortcuts
(
self
.
uaclient
)
...
...
@@ -131,8 +132,7 @@ class Client(object):
ep
.
SecurityMode
==
security_mode
and
ep
.
SecurityPolicyUri
==
policy_uri
):
return
ep
raise
ua
.
UaError
(
"No matching endpoints: {0}, {1}"
.
format
(
security_mode
,
policy_uri
))
raise
ua
.
UaError
(
"No matching endpoints: {0}, {1}"
.
format
(
security_mode
,
policy_uri
))
def
set_user
(
self
,
username
):
"""
...
...
@@ -504,7 +504,7 @@ class Client(object):
"""
if
isinstance
(
period
,
ua
.
CreateSubscriptionParameters
):
return
Subscription
(
self
.
uaclient
,
period
,
handler
)
return
Subscription
(
self
.
uaclient
,
period
,
handler
)
params
=
ua
.
CreateSubscriptionParameters
()
params
.
RequestedPublishingInterval
=
period
params
.
RequestedLifetimeCount
=
10000
...
...
@@ -554,46 +554,6 @@ class Client(object):
return
len
(
uries
)
-
1
def
load_type_definitions
(
self
,
nodes
=
None
):
"""
Download xml from given variable node defining custom structures.
If no no node is given, attemps to import variables from all nodes under
"0:OPC Binary"
the code is generated and imported on the fly. If you know the structures
are not going to be modified it might be interresting to copy the generated files
and include them in you code
"""
if
nodes
is
None
:
nodes
=
[]
for
desc
in
self
.
nodes
.
opc_binary
.
get_children_descriptions
():
if
desc
.
BrowseName
!=
ua
.
QualifiedName
(
"Opc.Ua"
):
nodes
.
append
(
self
.
get_node
(
desc
.
NodeId
))
self
.
logger
.
info
(
"Importing structures from nodes: %s"
,
nodes
)
structs_dict
=
{}
for
node
in
nodes
:
xml
=
node
.
get_value
()
xml
=
xml
.
decode
(
"utf-8"
)
generator
=
StructGenerator
()
generator
.
make_model_from_string
(
xml
)
# generate and execute new code on the fly
d
=
generator
.
get_python_classes
(
structs_dict
)
# same but using a file that is imported. This can be usefull for debugging library
#name = node.get_browse_name().Name
# Make sure structure names do not contain charaters that cannot be used in Python class file names
#name = name.replace(".", "")
#name = name.replace("'", "")
#name = name.replace('"', '')
#name = "structures_" + node.get_browse_name().Name
#generator.save_and_import(name + ".py", append_to=structs_dict)
# register classes
# every children of our node should represent a class
for
ndesc
in
node
.
get_children_descriptions
():
ndesc_node
=
self
.
get_node
(
ndesc
.
NodeId
)
ref_desc_list
=
ndesc_node
.
get_references
(
refs
=
ua
.
ObjectIds
.
HasDescription
,
direction
=
ua
.
BrowseDirection
.
Inverse
)
if
ref_desc_list
:
#some server put extra things here
name
=
ndesc
.
BrowseName
.
Name
nodeid
=
ref_desc_list
[
0
].
NodeId
ua
.
register_extension_object
(
name
,
nodeid
,
structs_dict
[
name
])
return
load_type_definitions
(
self
,
nodes
)
opcua/common/structures
_generator
.py
→
opcua/common/structures.py
View file @
b698bc87
"""
parse simple structures from an xml tree
Support for custom structures in client and server
We only support a subset of features but should be enough
for custom structures
"""
import
os
import
importlib
import
re
# The next two imports are for generated code
from
datetime
import
datetime
import
uuid
from
lxml
import
objectify
from
opcua.ua.ua_binary
import
Primitives
from
opcua
import
ua
# The next two imports are for generated code
from
datetime
import
datetime
import
uuid
def
get_default_value
(
uatype
):
...
...
@@ -151,10 +152,11 @@ class StructGenerator(object):
if
name
.
startswith
(
"NoOf"
):
array
=
True
continue
field
=
Field
(
name
)
field
=
Field
(
_clean_name
(
name
)
)
field
.
uatype
=
xmlfield
.
get
(
"TypeName"
)
if
":"
in
field
.
uatype
:
field
.
uatype
=
field
.
uatype
.
split
(
":"
)[
1
]
field
.
uatype
=
_clean_name
(
field
.
uatype
)
field
.
value
=
get_default_value
(
field
.
uatype
)
if
array
:
field
.
array
=
True
...
...
@@ -188,11 +190,11 @@ class StructGenerator(object):
if
env
is
None
:
env
=
{}
# Add the required libraries to dict
if
not
"ua"
in
env
:
if
"ua"
not
in
env
:
env
[
'ua'
]
=
ua
if
not
"datetime"
in
env
:
if
"datetime"
not
in
env
:
env
[
'datetime'
]
=
datetime
if
not
"uuid"
in
env
:
if
"uuid"
not
in
env
:
env
[
'uuid'
]
=
uuid
# generate classes one by one and add them to dict
for
struct
in
self
.
model
:
...
...
@@ -233,17 +235,56 @@ from opcua import ua
if
__name__
==
"__main__"
:
import
sys
from
IPython
import
embed
sys
.
path
.
insert
(
0
,
"."
)
# necessary for import in current dir
def
load_type_definitions
(
server
,
nodes
=
None
):
"""
Download xml from given variable node defining custom structures.
If no no node is given, attemps to import variables from all nodes under
"0:OPC Binary"
the code is generated and imported on the fly. If you know the structures
are not going to be modified it might be interresting to copy the generated files
and include them in you code
"""
if
nodes
is
None
:
nodes
=
[]
for
desc
in
server
.
nodes
.
opc_binary
.
get_children_descriptions
():
if
desc
.
BrowseName
!=
ua
.
QualifiedName
(
"Opc.Ua"
):
nodes
.
append
(
server
.
get_node
(
desc
.
NodeId
))
structs_dict
=
{}
for
node
in
nodes
:
xml
=
node
.
get_value
()
xml
=
xml
.
decode
(
"utf-8"
)
generator
=
StructGenerator
()
generator
.
make_model_from_string
(
xml
)
# generate and execute new code on the fly
generator
.
get_python_classes
(
structs_dict
)
# same but using a file that is imported. This can be usefull for debugging library
#name = node.get_browse_name().Name
# Make sure structure names do not contain charaters that cannot be used in Python class file names
#name = _clean_name(name)
#name = "structures_" + node.get_browse_name().Name
#generator.save_and_import(name + ".py", append_to=structs_dict)
# register classes
# every children of our node should represent a class
for
ndesc
in
node
.
get_children_descriptions
():
ndesc_node
=
server
.
get_node
(
ndesc
.
NodeId
)
ref_desc_list
=
ndesc_node
.
get_references
(
refs
=
ua
.
ObjectIds
.
HasDescription
,
direction
=
ua
.
BrowseDirection
.
Inverse
)
if
ref_desc_list
:
#some server put extra things here
name
=
_clean_name
(
ndesc
.
BrowseName
.
Name
)
if
not
name
in
structs_dict
:
print
(
"Error {} is found as child of binary definition node but is not found in xml"
.
format
(
name
))
continue
nodeid
=
ref_desc_list
[
0
].
NodeId
ua
.
register_extension_object
(
name
,
nodeid
,
structs_dict
[
name
])
#xmlpath = "schemas/Opc.Ua.Types.bsd"
xmlpath
=
"schemas/example.bsd"
c
=
StructGenerator
(
xmlpath
,
"structures.py"
)
c
.
run
()
import
structures
as
s
def
_clean_name
(
name
):
"""
Remove characters that might be present in OPC UA structures
but cannot be part of of Python class names
"""
name
=
re
.
sub
(
r'\
W+
', '
_
', name)
name = re.sub(r'
^
[
0
-
9
]
+
', r'
_
\
g
<
0
>
', name)
#sts = c.get_structures()
embed
()
return name
tests/tests_unit.py
View file @
b698bc87
...
...
@@ -16,7 +16,7 @@ from opcua.common.event_objects import BaseEvent
from
opcua.common.ua_utils
import
string_to_variant
,
variant_to_string
,
string_to_val
,
val_to_string
from
opcua.common.xmlimporter
import
XmlImporter
from
opcua.ua.uatypes
import
_MaskEnum
from
opcua.common.structures
_generator
import
StructGenerator
from
opcua.common.structures
import
StructGenerator
class
TestUnit
(
unittest
.
TestCase
):
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment