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
37f69181
Commit
37f69181
authored
Apr 26, 2015
by
Olivier R-D
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
autopep8
parent
e6300bec
Changes
21
Show whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
508 additions
and
445 deletions
+508
-445
client_to_prosys.py
client_to_prosys.py
+5
-5
example-client.py
example-client.py
+12
-9
example-server.py
example-server.py
+16
-11
opcua/__init__.py
opcua/__init__.py
+2
-3
opcua/address_space.py
opcua/address_space.py
+31
-30
opcua/binary_client.py
opcua/binary_client.py
+14
-17
opcua/binary_server.py
opcua/binary_server.py
+7
-5
opcua/client.py
opcua/client.py
+15
-16
opcua/event.py
opcua/event.py
+7
-7
opcua/internal_server.py
opcua/internal_server.py
+15
-12
opcua/node.py
opcua/node.py
+40
-38
opcua/server.py
opcua/server.py
+6
-10
opcua/standard_address_space.py
opcua/standard_address_space.py
+1
-2
opcua/subscription.py
opcua/subscription.py
+11
-10
opcua/subscription_service.py
opcua/subscription_service.py
+21
-25
opcua/uaprocessor.py
opcua/uaprocessor.py
+45
-43
opcua/uaprotocol.py
opcua/uaprotocol.py
+0
-3
opcua/uaprotocol_hand.py
opcua/uaprotocol_hand.py
+36
-20
opcua/uatypes.py
opcua/uatypes.py
+125
-79
opcua/utils.py
opcua/utils.py
+4
-4
tests.py
tests.py
+95
-96
No files found.
client_to_prosys.py
View file @
37f69181
...
@@ -3,10 +3,13 @@ import logging
...
@@ -3,10 +3,13 @@ import logging
from
opcua
import
Client
from
opcua
import
Client
from
opcua
import
uaprotocol
as
ua
from
opcua
import
uaprotocol
as
ua
class
SubHandler
(
object
):
class
SubHandler
(
object
):
"""
"""
Client to subscription. It will receive events from server
Client to subscription. It will receive events from server
"""
"""
def
data_change
(
self
,
handle
,
node
,
val
,
attr
):
def
data_change
(
self
,
handle
,
node
,
val
,
attr
):
print
(
"Python: New data change event"
,
handle
,
node
,
val
,
attr
)
print
(
"Python: New data change event"
,
handle
,
node
,
val
,
attr
)
...
@@ -14,7 +17,6 @@ class SubHandler(object):
...
@@ -14,7 +17,6 @@ class SubHandler(object):
print
(
"Python: New event"
,
handle
,
event
)
print
(
"Python: New event"
,
handle
,
event
)
if
__name__
==
"__main__"
:
if
__name__
==
"__main__"
:
from
IPython
import
embed
from
IPython
import
embed
logging
.
basicConfig
(
level
=
logging
.
WARN
)
logging
.
basicConfig
(
level
=
logging
.
WARN
)
...
@@ -42,7 +44,6 @@ if __name__ == "__main__":
...
@@ -42,7 +44,6 @@ if __name__ == "__main__":
myfloat
.
set_value
(
ua
.
Variant
(
1.234
,
ua
.
VariantType
.
Float
))
myfloat
.
set_value
(
ua
.
Variant
(
1.234
,
ua
.
VariantType
.
Float
))
print
(
"reading float value: "
,
myfloat
.
get_value
())
print
(
"reading float value: "
,
myfloat
.
get_value
())
handler
=
SubHandler
()
handler
=
SubHandler
()
sub
=
client
.
create_subscription
(
500
,
handler
)
sub
=
client
.
create_subscription
(
500
,
handler
)
sub
.
subscribe_data_change
(
var
)
sub
.
subscribe_data_change
(
var
)
...
@@ -52,7 +53,6 @@ if __name__ == "__main__":
...
@@ -52,7 +53,6 @@ if __name__ == "__main__":
result
=
device
.
call_method
(
method
,
ua
.
Variant
(
"sin"
),
ua
.
Variant
(
180
,
ua
.
VariantType
.
Double
))
result
=
device
.
call_method
(
method
,
ua
.
Variant
(
"sin"
),
ua
.
Variant
(
180
,
ua
.
VariantType
.
Double
))
print
(
"Mehtod result is: "
,
result
)
print
(
"Mehtod result is: "
,
result
)
embed
()
embed
()
client
.
close_session
()
client
.
close_session
()
finally
:
finally
:
...
...
example-client.py
View file @
37f69181
...
@@ -5,6 +5,7 @@ try:
...
@@ -5,6 +5,7 @@ try:
from
IPython
import
embed
from
IPython
import
embed
except
ImportError
:
except
ImportError
:
import
code
import
code
def
embed
():
def
embed
():
vars
=
globals
()
vars
=
globals
()
vars
.
update
(
locals
())
vars
.
update
(
locals
())
...
@@ -15,10 +16,13 @@ except ImportError:
...
@@ -15,10 +16,13 @@ except ImportError:
from
opcua
import
Client
from
opcua
import
Client
from
opcua
import
uaprotocol
as
ua
from
opcua
import
uaprotocol
as
ua
class
SubHandler
(
object
):
class
SubHandler
(
object
):
"""
"""
Client to subscription. It will receive events from server
Client to subscription. It will receive events from server
"""
"""
def
data_change
(
self
,
handle
,
node
,
val
,
attr
):
def
data_change
(
self
,
handle
,
node
,
val
,
attr
):
print
(
"Python: New data change event"
,
handle
,
node
,
val
,
attr
)
print
(
"Python: New data change event"
,
handle
,
node
,
val
,
attr
)
...
@@ -26,11 +30,10 @@ class SubHandler(object):
...
@@ -26,11 +30,10 @@ class SubHandler(object):
print
(
"Python: New event"
,
handle
,
event
)
print
(
"Python: New event"
,
handle
,
event
)
if
__name__
==
"__main__"
:
if
__name__
==
"__main__"
:
logging
.
basicConfig
(
level
=
logging
.
WARN
)
logging
.
basicConfig
(
level
=
logging
.
WARN
)
#logger = logging.getLogger("KeepAlive")
#logger = logging.getLogger("KeepAlive")
#logger.setLevel(logging.DEBUG)
#
logger.setLevel(logging.DEBUG)
client
=
Client
(
"opc.tcp://localhost:4841/freeopcua/server/"
)
client
=
Client
(
"opc.tcp://localhost:4841/freeopcua/server/"
)
try
:
try
:
client
.
connect
()
client
.
connect
()
...
@@ -39,8 +42,8 @@ if __name__ == "__main__":
...
@@ -39,8 +42,8 @@ if __name__ == "__main__":
print
(
root
.
get_children
())
print
(
root
.
get_children
())
print
(
root
.
get_browse_name
())
print
(
root
.
get_browse_name
())
#var = client.get_node(ua.NodeId(1002, 2))
#var = client.get_node(ua.NodeId(1002, 2))
#print(var)
#
print(var)
#print(var.get_value())
#
print(var.get_value())
#var.set_value(ua.Variant([23], ua.VariantType.Int64))
#var.set_value(ua.Variant([23], ua.VariantType.Int64))
state
=
root
.
get_child
([
"0:Objects"
,
"0:Server"
])
state
=
root
.
get_child
([
"0:Objects"
,
"0:Server"
])
print
(
state
)
print
(
state
)
...
@@ -52,10 +55,10 @@ if __name__ == "__main__":
...
@@ -52,10 +55,10 @@ if __name__ == "__main__":
handle
=
sub
.
subscribe_data_change
(
myvar
)
handle
=
sub
.
subscribe_data_change
(
myvar
)
time
.
sleep
(
0.1
)
time
.
sleep
(
0.1
)
sub
.
subscribe_events
()
sub
.
subscribe_events
()
#sub.unsubscribe(handle)
#
sub.unsubscribe(handle)
#sub.delete()
#
sub.delete()
#calling a method on server
#
calling a method on server
res
=
obj
.
call_method
(
"2:multiply"
,
3
,
"klk"
)
res
=
obj
.
call_method
(
"2:multiply"
,
3
,
"klk"
)
print
(
"method result is: "
,
res
)
print
(
"method result is: "
,
res
)
...
...
example-server.py
View file @
37f69181
...
@@ -5,6 +5,7 @@ try:
...
@@ -5,6 +5,7 @@ try:
from
IPython
import
embed
from
IPython
import
embed
except
ImportError
:
except
ImportError
:
import
code
import
code
def
embed
():
def
embed
():
vars
=
globals
()
vars
=
globals
()
vars
.
update
(
locals
())
vars
.
update
(
locals
())
...
@@ -16,36 +17,41 @@ from opcua import ua, uamethod, Server, Event, ObjectIds
...
@@ -16,36 +17,41 @@ from opcua import ua, uamethod, Server, Event, ObjectIds
class
SubHandler
(
object
):
class
SubHandler
(
object
):
"""
"""
Client to subscription. It will receive events from server
Client to subscription. It will receive events from server
"""
"""
def
data_change
(
self
,
handle
,
node
,
val
,
attr
):
def
data_change
(
self
,
handle
,
node
,
val
,
attr
):
print
(
"Python: New data change event"
,
handle
,
node
,
val
,
attr
)
print
(
"Python: New data change event"
,
handle
,
node
,
val
,
attr
)
def
event
(
self
,
handle
,
event
):
def
event
(
self
,
handle
,
event
):
print
(
"Python: New event"
,
handle
,
event
)
print
(
"Python: New event"
,
handle
,
event
)
#method to be exposed through server
# method to be exposed through server
def
func
(
parent
,
variant
):
def
func
(
parent
,
variant
):
return
[
variant
.
Value
*
2
]
return
[
variant
.
Value
*
2
]
#method to be exposed through server
#
method to be exposed through server
# uses a decorator to automatically convert to and from variants
# uses a decorator to automatically convert to and from variants
@
uamethod
@
uamethod
def
multiply
(
parent
,
x
,
y
):
def
multiply
(
parent
,
x
,
y
):
print
(
"multiply method call with parameters: "
,
x
,
y
)
print
(
"multiply method call with parameters: "
,
x
,
y
)
return
x
*
y
return
x
*
y
if
__name__
==
"__main__"
:
if
__name__
==
"__main__"
:
#optional setup logging
#
optional setup logging
logging
.
basicConfig
(
level
=
logging
.
WARN
)
logging
.
basicConfig
(
level
=
logging
.
WARN
)
#logger = logging.getLogger("opcua.address_space")
#logger = logging.getLogger("opcua.address_space")
#logger = logging.getLogger("opcua.internal_server")
#logger = logging.getLogger("opcua.internal_server")
#logger.setLevel(logging.DEBUG)
#
logger.setLevel(logging.DEBUG)
#logger = logging.getLogger("opcua.subscription_server")
#logger = logging.getLogger("opcua.subscription_server")
#logger.setLevel(logging.DEBUG)
# logger.setLevel(logging.DEBUG)
# now setup our server
# now setup our server
server
=
Server
()
server
=
Server
()
...
@@ -79,7 +85,7 @@ if __name__ == "__main__":
...
@@ -79,7 +85,7 @@ if __name__ == "__main__":
print
(
"Available loggers are: "
,
logging
.
Logger
.
manager
.
loggerDict
.
keys
())
print
(
"Available loggers are: "
,
logging
.
Logger
.
manager
.
loggerDict
.
keys
())
try
:
try
:
handler
=
SubHandler
()
handler
=
SubHandler
()
#enable following if you want to subscribe to nodes on server side
#
enable following if you want to subscribe to nodes on server side
sub
=
server
.
create_subscription
(
500
,
handler
)
sub
=
server
.
create_subscription
(
500
,
handler
)
handle
=
sub
.
subscribe_data_change
(
myvar
)
handle
=
sub
.
subscribe_data_change
(
myvar
)
# trigger event, all subscribed clients wil receive it
# trigger event, all subscribed clients wil receive it
...
@@ -88,4 +94,3 @@ if __name__ == "__main__":
...
@@ -88,4 +94,3 @@ if __name__ == "__main__":
embed
()
embed
()
finally
:
finally
:
server
.
stop
()
server
.
stop
()
opcua/__init__.py
View file @
37f69181
...
@@ -23,10 +23,9 @@ def uamethod(func):
...
@@ -23,10 +23,9 @@ def uamethod(func):
return
to_variant
(
result
)
return
to_variant
(
result
)
return
wrapper
return
wrapper
def
to_variant
(
*
args
):
def
to_variant
(
*
args
):
uaargs
=
[]
uaargs
=
[]
for
arg
in
args
:
for
arg
in
args
:
uaargs
.
append
(
ua
.
Variant
(
arg
))
uaargs
.
append
(
ua
.
Variant
(
arg
))
return
uaargs
return
uaargs
opcua/address_space.py
View file @
37f69181
...
@@ -4,7 +4,9 @@ import pickle
...
@@ -4,7 +4,9 @@ import pickle
from
opcua
import
ua
from
opcua
import
ua
class
AttributeValue
(
object
):
class
AttributeValue
(
object
):
def
__init__
(
self
,
value
):
def
__init__
(
self
,
value
):
self
.
value
=
value
self
.
value
=
value
self
.
value_callback
=
None
self
.
value_callback
=
None
...
@@ -14,7 +16,9 @@ class AttributeValue(object):
...
@@ -14,7 +16,9 @@ class AttributeValue(object):
return
"AttributeValue({})"
.
format
(
self
.
value
)
return
"AttributeValue({})"
.
format
(
self
.
value
)
__repr__
=
__str__
__repr__
=
__str__
class
NodeData
(
object
):
class
NodeData
(
object
):
def
__init__
(
self
,
nodeid
):
def
__init__
(
self
,
nodeid
):
self
.
nodeid
=
nodeid
self
.
nodeid
=
nodeid
self
.
attributes
=
{}
self
.
attributes
=
{}
...
@@ -27,6 +31,7 @@ class NodeData(object):
...
@@ -27,6 +31,7 @@ class NodeData(object):
class
AttributeService
(
object
):
class
AttributeService
(
object
):
def
__init__
(
self
,
aspace
):
def
__init__
(
self
,
aspace
):
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
_aspace
=
aspace
self
.
_aspace
=
aspace
...
@@ -45,7 +50,9 @@ class AttributeService(object):
...
@@ -45,7 +50,9 @@ class AttributeService(object):
res
.
append
(
self
.
_aspace
.
set_attribute_value
(
writevalue
.
NodeId
,
writevalue
.
AttributeId
,
writevalue
.
Value
))
res
.
append
(
self
.
_aspace
.
set_attribute_value
(
writevalue
.
NodeId
,
writevalue
.
AttributeId
,
writevalue
.
Value
))
return
res
return
res
class
ViewService
(
object
):
class
ViewService
(
object
):
def
__init__
(
self
,
aspace
):
def
__init__
(
self
,
aspace
):
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
_aspace
=
aspace
self
.
_aspace
=
aspace
...
@@ -140,7 +147,7 @@ class ViewService(object):
...
@@ -140,7 +147,7 @@ class ViewService(object):
with
self
.
_aspace
.
lock
:
with
self
.
_aspace
.
lock
:
nodedata
=
self
.
_aspace
.
nodes
[
nodeid
]
nodedata
=
self
.
_aspace
.
nodes
[
nodeid
]
for
ref
in
nodedata
.
references
:
for
ref
in
nodedata
.
references
:
#FIXME: here we should check other arguments!!
#
FIXME: here we should check other arguments!!
if
ref
.
BrowseName
==
el
.
TargetName
:
if
ref
.
BrowseName
==
el
.
TargetName
:
return
ref
.
NodeId
return
ref
.
NodeId
self
.
logger
.
info
(
"element %s was not found in node %s"
,
el
,
nodeid
)
self
.
logger
.
info
(
"element %s was not found in node %s"
,
el
,
nodeid
)
...
@@ -148,6 +155,7 @@ class ViewService(object):
...
@@ -148,6 +155,7 @@ class ViewService(object):
class
NodeManagementService
(
object
):
class
NodeManagementService
(
object
):
def
__init__
(
self
,
aspace
):
def
__init__
(
self
,
aspace
):
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
_aspace
=
aspace
self
.
_aspace
=
aspace
...
@@ -167,14 +175,14 @@ class NodeManagementService(object):
...
@@ -167,14 +175,14 @@ class NodeManagementService(object):
result
.
StatusCode
=
ua
.
StatusCode
(
ua
.
StatusCodes
.
BadNodeIdExists
)
result
.
StatusCode
=
ua
.
StatusCode
(
ua
.
StatusCodes
.
BadNodeIdExists
)
return
result
return
result
nodedata
=
NodeData
(
item
.
RequestedNewNodeId
)
nodedata
=
NodeData
(
item
.
RequestedNewNodeId
)
#add common attrs
#
add common attrs
nodedata
.
attributes
[
ua
.
AttributeIds
.
NodeId
]
=
AttributeValue
(
ua
.
DataValue
(
ua
.
Variant
(
item
.
RequestedNewNodeId
,
ua
.
VariantType
.
NodeId
)))
nodedata
.
attributes
[
ua
.
AttributeIds
.
NodeId
]
=
AttributeValue
(
ua
.
DataValue
(
ua
.
Variant
(
item
.
RequestedNewNodeId
,
ua
.
VariantType
.
NodeId
)))
nodedata
.
attributes
[
ua
.
AttributeIds
.
BrowseName
]
=
AttributeValue
(
ua
.
DataValue
(
ua
.
Variant
(
item
.
BrowseName
,
ua
.
VariantType
.
QualifiedName
)))
nodedata
.
attributes
[
ua
.
AttributeIds
.
BrowseName
]
=
AttributeValue
(
ua
.
DataValue
(
ua
.
Variant
(
item
.
BrowseName
,
ua
.
VariantType
.
QualifiedName
)))
nodedata
.
attributes
[
ua
.
AttributeIds
.
NodeClass
]
=
AttributeValue
(
ua
.
DataValue
(
ua
.
Variant
(
item
.
NodeClass
,
ua
.
VariantType
.
Int32
)))
nodedata
.
attributes
[
ua
.
AttributeIds
.
NodeClass
]
=
AttributeValue
(
ua
.
DataValue
(
ua
.
Variant
(
item
.
NodeClass
,
ua
.
VariantType
.
Int32
)))
#add requested attrs
#
add requested attrs
self
.
_add_nodeattributes
(
item
.
NodeAttributes
,
nodedata
)
self
.
_add_nodeattributes
(
item
.
NodeAttributes
,
nodedata
)
#add parent
#
add parent
if
item
.
ParentNodeId
==
ua
.
NodeId
():
if
item
.
ParentNodeId
==
ua
.
NodeId
():
#self.logger.warning("add_node: creating node %s without parent", item.RequestedNewNodeId)
#self.logger.warning("add_node: creating node %s without parent", item.RequestedNewNodeId)
pass
pass
...
@@ -193,10 +201,10 @@ class NodeManagementService(object):
...
@@ -193,10 +201,10 @@ class NodeManagementService(object):
desc
.
IsForward
=
True
desc
.
IsForward
=
True
self
.
_aspace
.
nodes
[
item
.
ParentNodeId
].
references
.
append
(
desc
)
self
.
_aspace
.
nodes
[
item
.
ParentNodeId
].
references
.
append
(
desc
)
#now add our node to db
#
now add our node to db
self
.
_aspace
.
nodes
[
item
.
RequestedNewNodeId
]
=
nodedata
self
.
_aspace
.
nodes
[
item
.
RequestedNewNodeId
]
=
nodedata
#add type definition
#
add type definition
if
item
.
TypeDefinition
!=
ua
.
NodeId
():
if
item
.
TypeDefinition
!=
ua
.
NodeId
():
addref
=
ua
.
AddReferencesItem
()
addref
=
ua
.
AddReferencesItem
()
addref
.
SourceNodeId
=
item
.
RequestedNewNodeId
addref
.
SourceNodeId
=
item
.
RequestedNewNodeId
...
@@ -270,6 +278,7 @@ class NodeManagementService(object):
...
@@ -270,6 +278,7 @@ class NodeManagementService(object):
class
MethodService
(
object
):
class
MethodService
(
object
):
def
__init__
(
self
,
aspace
):
def
__init__
(
self
,
aspace
):
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
_aspace
=
aspace
self
.
_aspace
=
aspace
...
@@ -301,16 +310,18 @@ class MethodService(object):
...
@@ -301,16 +310,18 @@ class MethodService(object):
class
AddressSpace
(
object
):
class
AddressSpace
(
object
):
"""
"""
The address space object stores all the nodes og the OPC-UA server
The address space object stores all the nodes og the OPC-UA server
and helper methods.
and helper methods.
It is NOT threadsafe, lock the lock object before reading of modifying
It is NOT threadsafe, lock the lock object before reading of modifying
a node
a node
"""
"""
def
__init__
(
self
):
def
__init__
(
self
):
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
nodes
=
{}
self
.
nodes
=
{}
self
.
lock
=
RLock
()
#
FIXME: should use multiple reader, one writter pattern
self
.
lock
=
RLock
()
#
FIXME: should use multiple reader, one writter pattern
self
.
_datachange_callback_counter
=
200
self
.
_datachange_callback_counter
=
200
self
.
_handle_to_attribute_map
=
{}
self
.
_handle_to_attribute_map
=
{}
...
@@ -386,13 +397,3 @@ class AddressSpace(object):
...
@@ -386,13 +397,3 @@ class AddressSpace(object):
with
self
.
lock
:
with
self
.
lock
:
node
=
self
.
nodes
[
methodid
]
node
=
self
.
nodes
[
methodid
]
node
.
call
=
callback
node
.
call
=
callback
opcua/binary_client.py
View file @
37f69181
...
@@ -12,6 +12,7 @@ import opcua.utils as utils
...
@@ -12,6 +12,7 @@ import opcua.utils as utils
class
BinaryClient
(
object
):
class
BinaryClient
(
object
):
"""
"""
low level OPC-UA client.
low level OPC-UA client.
implement all(well..one day) methods defined in opcua spec
implement all(well..one day) methods defined in opcua spec
...
@@ -19,6 +20,7 @@ class BinaryClient(object):
...
@@ -19,6 +20,7 @@ class BinaryClient(object):
in python most of the structures are defined in
in python most of the structures are defined in
uaprotocol_auto.py and uaprotocol_hand.py
uaprotocol_auto.py and uaprotocol_hand.py
"""
"""
def
__init__
(
self
):
def
__init__
(
self
):
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
_socket
=
None
self
.
_socket
=
None
...
@@ -43,9 +45,9 @@ class BinaryClient(object):
...
@@ -43,9 +45,9 @@ class BinaryClient(object):
self
.
_thread
.
start
()
self
.
_thread
.
start
()
def
_send_request
(
self
,
request
,
callback
=
None
,
timeout
=
1000
):
def
_send_request
(
self
,
request
,
callback
=
None
,
timeout
=
1000
):
#HACK to make sure we can convert our request to binary before increasing request counter etc ...
#
HACK to make sure we can convert our request to binary before increasing request counter etc ...
request
.
to_binary
()
request
.
to_binary
()
#END HACK
#
END HACK
with
self
.
_lock
:
with
self
.
_lock
:
request
.
RequestHeader
=
self
.
_create_request_header
(
timeout
)
request
.
RequestHeader
=
self
.
_create_request_header
(
timeout
)
hdr
=
ua
.
Header
(
ua
.
MessageType
.
SecureMessage
,
ua
.
ChunkType
.
Single
,
self
.
_security_token
.
ChannelId
)
hdr
=
ua
.
Header
(
ua
.
MessageType
.
SecureMessage
,
ua
.
ChunkType
.
Single
,
self
.
_security_token
.
ChannelId
)
...
@@ -62,7 +64,7 @@ class BinaryClient(object):
...
@@ -62,7 +64,7 @@ class BinaryClient(object):
return
data
return
data
def
_check_answer
(
self
,
data
,
context
):
def
_check_answer
(
self
,
data
,
context
):
data
=
data
.
copy
(
50
)
#
FIXME check max length nodeid + responseheader
data
=
data
.
copy
(
50
)
#
FIXME check max length nodeid + responseheader
typeid
=
ua
.
NodeId
.
from_binary
(
data
)
typeid
=
ua
.
NodeId
.
from_binary
(
data
)
if
typeid
==
ua
.
FourByteNodeId
(
ua
.
ObjectIds
.
ServiceFault_Encoding_DefaultBinary
):
if
typeid
==
ua
.
FourByteNodeId
(
ua
.
ObjectIds
.
ServiceFault_Encoding_DefaultBinary
):
self
.
logger
.
warning
(
"ServiceFault from server received %s"
,
context
)
self
.
logger
.
warning
(
"ServiceFault from server received %s"
,
context
)
...
@@ -159,14 +161,13 @@ class BinaryClient(object):
...
@@ -159,14 +161,13 @@ class BinaryClient(object):
hdr
.
RequestId
=
self
.
_request_id
hdr
.
RequestId
=
self
.
_request_id
return
hdr
return
hdr
def
connect_socket
(
self
,
host
,
port
):
def
connect_socket
(
self
,
host
,
port
):
"""
"""
connect to server socket and start receiving thread
connect to server socket and start receiving thread
"""
"""
self
.
logger
.
info
(
"opening connection"
)
self
.
logger
.
info
(
"opening connection"
)
self
.
_socket
=
socket
.
create_connection
((
host
,
port
))
self
.
_socket
=
socket
.
create_connection
((
host
,
port
))
self
.
_socket
.
setsockopt
(
socket
.
IPPROTO_TCP
,
socket
.
TCP_NODELAY
,
1
)
#
nodelay ncessary to avoid packing in one frame, some servers do not like it
self
.
_socket
.
setsockopt
(
socket
.
IPPROTO_TCP
,
socket
.
TCP_NODELAY
,
1
)
#
nodelay ncessary to avoid packing in one frame, some servers do not like it
self
.
start
()
self
.
start
()
def
disconnect_socket
(
self
):
def
disconnect_socket
(
self
):
...
@@ -229,7 +230,7 @@ class BinaryClient(object):
...
@@ -229,7 +230,7 @@ class BinaryClient(object):
request
.
DeleteSubscriptions
=
deletesubscriptions
request
.
DeleteSubscriptions
=
deletesubscriptions
data
=
self
.
_send_request
(
request
)
data
=
self
.
_send_request
(
request
)
ua
.
CloseSessionResponse
.
from_binary
(
data
)
ua
.
CloseSessionResponse
.
from_binary
(
data
)
#response.ResponseHeader.ServiceResult.check() #disabled, it seems we sent wrong session Id, but where is the sessionId supposed to be sent???
#
response.ResponseHeader.ServiceResult.check() #disabled, it seems we sent wrong session Id, but where is the sessionId supposed to be sent???
def
browse
(
self
,
parameters
):
def
browse
(
self
,
parameters
):
self
.
logger
.
info
(
"browse"
)
self
.
logger
.
info
(
"browse"
)
...
@@ -281,7 +282,7 @@ class BinaryClient(object):
...
@@ -281,7 +282,7 @@ class BinaryClient(object):
seqhdr
=
self
.
_create_sequence_header
()
seqhdr
=
self
.
_create_sequence_header
()
self
.
_write_socket
(
hdr
,
symhdr
,
seqhdr
,
request
)
self
.
_write_socket
(
hdr
,
symhdr
,
seqhdr
,
request
)
#some servers send a response here, most do not ... so we ignore
#
some servers send a response here, most do not ... so we ignore
def
translate_browsepaths_to_nodeids
(
self
,
browsepaths
):
def
translate_browsepaths_to_nodeids
(
self
,
browsepaths
):
self
.
logger
.
info
(
"translate_browsepath_to_nodeid"
)
self
.
logger
.
info
(
"translate_browsepath_to_nodeid"
)
...
@@ -326,10 +327,9 @@ class BinaryClient(object):
...
@@ -326,10 +327,9 @@ class BinaryClient(object):
response
=
ua
.
PublishResponse
.
from_binary
(
future
.
result
())
response
=
ua
.
PublishResponse
.
from_binary
(
future
.
result
())
try
:
try
:
self
.
_publishcallbacks
[
response
.
Parameters
.
SubscriptionId
](
response
.
Parameters
)
self
.
_publishcallbacks
[
response
.
Parameters
.
SubscriptionId
](
response
.
Parameters
)
except
Exception
as
ex
:
#
we call client code, catch everything!
except
Exception
as
ex
:
#
we call client code, catch everything!
self
.
logger
.
exception
(
"Exception while calling user callback"
)
self
.
logger
.
exception
(
"Exception while calling user callback"
)
def
create_monitored_items
(
self
,
params
):
def
create_monitored_items
(
self
,
params
):
self
.
logger
.
info
(
"create_monitored_items"
)
self
.
logger
.
info
(
"create_monitored_items"
)
request
=
ua
.
CreateMonitoredItemsRequest
()
request
=
ua
.
CreateMonitoredItemsRequest
()
...
@@ -364,6 +364,3 @@ class BinaryClient(object):
...
@@ -364,6 +364,3 @@ class BinaryClient(object):
response
=
ua
.
CallResponse
.
from_binary
(
data
)
response
=
ua
.
CallResponse
.
from_binary
(
data
)
response
.
ResponseHeader
.
ServiceResult
.
check
()
response
.
ResponseHeader
.
ServiceResult
.
check
()
return
response
.
Results
return
response
.
Results
opcua/binary_server.py
View file @
37f69181
...
@@ -14,10 +14,13 @@ from opcua.uaprocessor import UAProcessor
...
@@ -14,10 +14,13 @@ from opcua.uaprocessor import UAProcessor
logger
=
logging
.
getLogger
(
__name__
)
logger
=
logging
.
getLogger
(
__name__
)
class
BinaryServer
(
Thread
):
class
BinaryServer
(
Thread
):
"""
"""
Socket server forwarding request to internal server
Socket server forwarding request to internal server
"""
"""
def
__init__
(
self
,
internal_server
,
hostname
,
port
):
def
__init__
(
self
,
internal_server
,
hostname
,
port
):
Thread
.
__init__
(
self
)
Thread
.
__init__
(
self
)
self
.
socket_server
=
None
self
.
socket_server
=
None
...
@@ -33,10 +36,10 @@ class BinaryServer(Thread):
...
@@ -33,10 +36,10 @@ class BinaryServer(Thread):
def
run
(
self
):
def
run
(
self
):
logger
.
warning
(
"Listening on %s:%s"
,
self
.
hostname
,
self
.
port
)
logger
.
warning
(
"Listening on %s:%s"
,
self
.
hostname
,
self
.
port
)
socketserver
.
TCPServer
.
allow_reuse_address
=
True
#
get rid of address already in used warning
socketserver
.
TCPServer
.
allow_reuse_address
=
True
#
get rid of address already in used warning
self
.
socket_server
=
ThreadingTCPServer
((
self
.
hostname
,
self
.
port
),
UAHandler
)
self
.
socket_server
=
ThreadingTCPServer
((
self
.
hostname
,
self
.
port
),
UAHandler
)
#self.socket_server.daemon_threads = True # this will force a shutdown of all threads, maybe too hard
#
self.socket_server.daemon_threads = True # this will force a shutdown of all threads, maybe too hard
self
.
socket_server
.
internal_server
=
self
.
iserver
#
allow handler to acces server properties
self
.
socket_server
.
internal_server
=
self
.
iserver
#
allow handler to acces server properties
with
self
.
_cond
:
with
self
.
_cond
:
self
.
_cond
.
notify_all
()
self
.
_cond
.
notify_all
()
self
.
socket_server
.
serve_forever
()
self
.
socket_server
.
serve_forever
()
...
@@ -47,6 +50,7 @@ class BinaryServer(Thread):
...
@@ -47,6 +50,7 @@ class BinaryServer(Thread):
class
UAHandler
(
socketserver
.
BaseRequestHandler
):
class
UAHandler
(
socketserver
.
BaseRequestHandler
):
"""
"""
The RequestHandler class for our server.
The RequestHandler class for our server.
...
@@ -65,5 +69,3 @@ class UAHandler(socketserver.BaseRequestHandler):
...
@@ -65,5 +69,3 @@ class UAHandler(socketserver.BaseRequestHandler):
class
ThreadingTCPServer
(
socketserver
.
ThreadingMixIn
,
socketserver
.
TCPServer
):
class
ThreadingTCPServer
(
socketserver
.
ThreadingMixIn
,
socketserver
.
TCPServer
):
pass
pass
opcua/client.py
View file @
37f69181
from
__future__
import
division
#
support for python2
from
__future__
import
division
#
support for python2
from
threading
import
Thread
,
Condition
from
threading
import
Thread
,
Condition
import
logging
import
logging
try
:
try
:
from
urllib.parse
import
urlparse
from
urllib.parse
import
urlparse
except
ImportError
:
#
support for python2
except
ImportError
:
#
support for python2
from
urlparse
import
urlparse
from
urlparse
import
urlparse
from
opcua
import
uaprotocol
as
ua
from
opcua
import
uaprotocol
as
ua
...
@@ -12,10 +12,12 @@ from opcua import utils
...
@@ -12,10 +12,12 @@ from opcua import utils
class
KeepAlive
(
Thread
):
class
KeepAlive
(
Thread
):
"""
"""
Used by Client to keep session opened.
Used by Client to keep session opened.
OPCUA defines timeout both for sessions and secure channel
OPCUA defines timeout both for sessions and secure channel
"""
"""
def
__init__
(
self
,
client
,
timeout
):
def
__init__
(
self
,
client
,
timeout
):
Thread
.
__init__
(
self
)
Thread
.
__init__
(
self
)
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
logger
=
logging
.
getLogger
(
__name__
)
...
@@ -31,7 +33,7 @@ class KeepAlive(Thread):
...
@@ -31,7 +33,7 @@ class KeepAlive(Thread):
server_state
=
self
.
client
.
get_node
(
ua
.
FourByteNodeId
(
ua
.
ObjectIds
.
Server_ServerStatus_State
))
server_state
=
self
.
client
.
get_node
(
ua
.
FourByteNodeId
(
ua
.
ObjectIds
.
Server_ServerStatus_State
))
while
not
self
.
_dostop
:
while
not
self
.
_dostop
:
with
self
.
_cond
:
with
self
.
_cond
:
self
.
_cond
.
wait
(
self
.
timeout
/
1000
)
self
.
_cond
.
wait
(
self
.
timeout
/
1000
)
if
self
.
_dostop
:
if
self
.
_dostop
:
break
break
self
.
logger
.
debug
(
"renewing channel"
)
self
.
logger
.
debug
(
"renewing channel"
)
...
@@ -48,6 +50,7 @@ class KeepAlive(Thread):
...
@@ -48,6 +50,7 @@ class KeepAlive(Thread):
class
Client
(
object
):
class
Client
(
object
):
"""
"""
High level client to connect to an OPC-UA server.
High level client to connect to an OPC-UA server.
This class makes it easy to connect and browse address space.
This class makes it easy to connect and browse address space.
...
@@ -57,6 +60,7 @@ class Client(object):
...
@@ -57,6 +60,7 @@ class Client(object):
which offers a raw OPC-UA interface.
which offers a raw OPC-UA interface.
"""
"""
def
__init__
(
self
,
url
):
def
__init__
(
self
,
url
):
"""
"""
used url argument to connect to server.
used url argument to connect to server.
...
@@ -124,7 +128,7 @@ class Client(object):
...
@@ -124,7 +128,7 @@ class Client(object):
Send OPC-UA hello to server
Send OPC-UA hello to server
"""
"""
ack
=
self
.
bclient
.
send_hello
(
self
.
server_url
.
geturl
())
ack
=
self
.
bclient
.
send_hello
(
self
.
server_url
.
geturl
())
#FIXME check ack
#
FIXME check ack
def
open_secure_channel
(
self
,
renew
=
False
):
def
open_secure_channel
(
self
,
renew
=
False
):
"""
"""
...
@@ -165,10 +169,10 @@ class Client(object):
...
@@ -165,10 +169,10 @@ class Client(object):
params
.
EndpointUrl
=
self
.
server_url
.
geturl
()
params
.
EndpointUrl
=
self
.
server_url
.
geturl
()
params
.
SessionName
=
self
.
description
+
" Session"
+
str
(
self
.
_session_counter
)
params
.
SessionName
=
self
.
description
+
" Session"
+
str
(
self
.
_session_counter
)
params
.
RequestedSessionTimeout
=
3600000
params
.
RequestedSessionTimeout
=
3600000
params
.
MaxResponseMessageSize
=
0
#
means not max size
params
.
MaxResponseMessageSize
=
0
#
means not max size
response
=
self
.
bclient
.
create_session
(
params
)
response
=
self
.
bclient
.
create_session
(
params
)
self
.
session_timeout
=
response
.
RevisedSessionTimeout
self
.
session_timeout
=
response
.
RevisedSessionTimeout
self
.
keepalive
=
KeepAlive
(
self
,
min
(
self
.
session_timeout
,
self
.
secure_channel_timeout
)
*
0.7
)
#
0.7 is from spec
self
.
keepalive
=
KeepAlive
(
self
,
min
(
self
.
session_timeout
,
self
.
secure_channel_timeout
)
*
0.7
)
#
0.7 is from spec
self
.
keepalive
.
start
()
self
.
keepalive
.
start
()
return
response
return
response
...
@@ -224,8 +228,3 @@ class Client(object):
...
@@ -224,8 +228,3 @@ class Client(object):
def
get_namespace_index
(
self
,
uri
):
def
get_namespace_index
(
self
,
uri
):
uries
=
self
.
get_namespace_array
()
uries
=
self
.
get_namespace_array
()
return
uries
.
index
(
uri
)
return
uries
.
index
(
uri
)
opcua/event.py
View file @
37f69181
...
@@ -9,6 +9,7 @@ import uuid
...
@@ -9,6 +9,7 @@ import uuid
class
Event
(
object
):
class
Event
(
object
):
"""
"""
Create an event based on an event type. Per default is BaseEventType used.
Create an event based on an event type. Per default is BaseEventType used.
arguments are:
arguments are:
...
@@ -16,6 +17,7 @@ class Event(object):
...
@@ -16,6 +17,7 @@ class Event(object):
source: The emiting source for the node, either an objectId, NodeId or a Node
source: The emiting source for the node, either an objectId, NodeId or a Node
etype: The event type, either an objectId, a NodeId or a Node object
etype: The event type, either an objectId, a NodeId or a Node object
"""
"""
def
__init__
(
self
,
isession
,
etype
=
ObjectIds
.
BaseEventType
,
source
=
ObjectIds
.
Server
):
def
__init__
(
self
,
isession
,
etype
=
ObjectIds
.
BaseEventType
,
source
=
ObjectIds
.
Server
):
self
.
isession
=
isession
self
.
isession
=
isession
...
@@ -34,7 +36,7 @@ class Event(object):
...
@@ -34,7 +36,7 @@ class Event(object):
else
:
else
:
self
.
SourceNode
=
ua
.
NodeId
(
source
)
self
.
SourceNode
=
ua
.
NodeId
(
source
)
#set some default values for attributes from BaseEventType, thus that all event must have
#
set some default values for attributes from BaseEventType, thus that all event must have
self
.
EventId
=
uuid
.
uuid4
().
bytes
self
.
EventId
=
uuid
.
uuid4
().
bytes
self
.
EventType
=
self
.
node
.
nodeid
self
.
EventType
=
self
.
node
.
nodeid
self
.
LocaleTime
=
datetime
.
now
()
self
.
LocaleTime
=
datetime
.
now
()
...
@@ -44,7 +46,7 @@ class Event(object):
...
@@ -44,7 +46,7 @@ class Event(object):
self
.
Severity
=
ua
.
Variant
(
1
,
ua
.
VariantType
.
UInt16
)
self
.
Severity
=
ua
.
Variant
(
1
,
ua
.
VariantType
.
UInt16
)
self
.
SourceName
=
"Server"
self
.
SourceName
=
"Server"
#og set some node attributed we also are expected to have
#
og set some node attributed we also are expected to have
self
.
BrowseName
=
self
.
node
.
get_browse_name
()
self
.
BrowseName
=
self
.
node
.
get_browse_name
()
self
.
DisplayName
=
self
.
node
.
get_display_name
()
self
.
DisplayName
=
self
.
node
.
get_display_name
()
self
.
NodeId
=
self
.
node
.
nodeid
self
.
NodeId
=
self
.
node
.
nodeid
...
@@ -63,5 +65,3 @@ class Event(object):
...
@@ -63,5 +65,3 @@ class Event(object):
for
desc
in
references
:
for
desc
in
references
:
node
=
Node
(
self
.
isession
,
desc
.
NodeId
)
node
=
Node
(
self
.
isession
,
desc
.
NodeId
)
setattr
(
self
,
desc
.
BrowseName
.
Name
,
node
.
get_value
())
setattr
(
self
,
desc
.
BrowseName
.
Name
,
node
.
get_value
())
opcua/internal_server.py
View file @
37f69181
...
@@ -10,7 +10,7 @@ from threading import Thread
...
@@ -10,7 +10,7 @@ from threading import Thread
from
enum
import
Enum
from
enum
import
Enum
import
functools
import
functools
try
:
try
:
#we prefer to use bundles asyncio version, otherwise fallback to trollius
#
we prefer to use bundles asyncio version, otherwise fallback to trollius
import
asyncio
import
asyncio
except
ImportError
:
except
ImportError
:
import
trollius
as
asyncio
import
trollius
as
asyncio
...
@@ -30,6 +30,7 @@ from opcua import standard_address_space
...
@@ -30,6 +30,7 @@ from opcua import standard_address_space
class
ThreadLoop
(
Thread
):
class
ThreadLoop
(
Thread
):
def
__init__
(
self
):
def
__init__
(
self
):
Thread
.
__init__
(
self
)
Thread
.
__init__
(
self
)
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
logger
=
logging
.
getLogger
(
__name__
)
...
@@ -69,7 +70,9 @@ class SessionState(Enum):
...
@@ -69,7 +70,9 @@ class SessionState(Enum):
Activated
=
1
Activated
=
1
Closed
=
2
Closed
=
2
class
InternalServer
(
object
):
class
InternalServer
(
object
):
def
__init__
(
self
):
def
__init__
(
self
):
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
endpoints
=
[]
self
.
endpoints
=
[]
...
@@ -81,7 +84,7 @@ class InternalServer(object):
...
@@ -81,7 +84,7 @@ class InternalServer(object):
self
.
method_service
=
MethodService
(
self
.
aspace
)
self
.
method_service
=
MethodService
(
self
.
aspace
)
self
.
node_mgt_service
=
NodeManagementService
(
self
.
aspace
)
self
.
node_mgt_service
=
NodeManagementService
(
self
.
aspace
)
standard_address_space
.
fill_address_space
(
self
.
node_mgt_service
)
standard_address_space
.
fill_address_space
(
self
.
node_mgt_service
)
#standard_address_space.fill_address_space_from_disk(self.aspace)
#
standard_address_space.fill_address_space_from_disk(self.aspace)
self
.
loop
=
ThreadLoop
()
self
.
loop
=
ThreadLoop
()
self
.
subcsription_service
=
SubscriptionService
(
self
.
loop
,
self
.
aspace
)
self
.
subcsription_service
=
SubscriptionService
(
self
.
loop
,
self
.
aspace
)
...
@@ -124,15 +127,17 @@ class InternalServer(object):
...
@@ -124,15 +127,17 @@ class InternalServer(object):
def
get_endpoints
(
self
,
params
=
None
):
def
get_endpoints
(
self
,
params
=
None
):
self
.
logger
.
info
(
"get endpoint"
)
self
.
logger
.
info
(
"get endpoint"
)
#FIXME check params
#
FIXME check params
return
self
.
endpoints
[:]
return
self
.
endpoints
[:]
def
create_session
(
self
,
name
):
def
create_session
(
self
,
name
):
return
InternalSession
(
self
,
self
.
aspace
,
self
.
subcsription_service
,
name
)
return
InternalSession
(
self
,
self
.
aspace
,
self
.
subcsription_service
,
name
)
class
InternalSession
(
object
):
class
InternalSession
(
object
):
_counter
=
10
_counter
=
10
_auth_counter
=
1000
_auth_counter
=
1000
def
__init__
(
self
,
internal_server
,
aspace
,
submgr
,
name
):
def
__init__
(
self
,
internal_server
,
aspace
,
submgr
,
name
):
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
iserver
=
internal_server
self
.
iserver
=
internal_server
...
@@ -235,5 +240,3 @@ class InternalSession(object):
...
@@ -235,5 +240,3 @@ class InternalSession(object):
if
acks
is
None
:
if
acks
is
None
:
acks
=
[]
acks
=
[]
return
self
.
subscription_service
.
publish
(
acks
)
return
self
.
subscription_service
.
publish
(
acks
)
opcua/node.py
View file @
37f69181
...
@@ -5,11 +5,14 @@ and browse address space
...
@@ -5,11 +5,14 @@ and browse address space
import
opcua.uaprotocol
as
ua
import
opcua.uaprotocol
as
ua
class
Node
(
object
):
class
Node
(
object
):
"""
"""
High level node object, to access node attribute,
High level node object, to access node attribute,
browse and populate address space
browse and populate address space
"""
"""
def
__init__
(
self
,
server
,
nodeid
):
def
__init__
(
self
,
server
,
nodeid
):
self
.
server
=
server
self
.
server
=
server
self
.
nodeid
=
None
self
.
nodeid
=
None
...
@@ -21,6 +24,7 @@ class Node(object):
...
@@ -21,6 +24,7 @@ class Node(object):
self
.
nodeid
=
ua
.
NodeId
(
nodeid
,
0
)
self
.
nodeid
=
ua
.
NodeId
(
nodeid
,
0
)
else
:
else
:
raise
Exception
(
"argument to node must be a NodeId object or a string defining a nodeid found {} of type {}"
.
format
(
nodeid
,
type
(
nodeid
)))
raise
Exception
(
"argument to node must be a NodeId object or a string defining a nodeid found {} of type {}"
.
format
(
nodeid
,
type
(
nodeid
)))
def
__eq__
(
self
,
other
):
def
__eq__
(
self
,
other
):
if
isinstance
(
other
,
Node
)
and
self
.
nodeid
==
other
.
nodeid
:
if
isinstance
(
other
,
Node
)
and
self
.
nodeid
==
other
.
nodeid
:
return
True
return
True
...
@@ -73,7 +77,7 @@ class Node(object):
...
@@ -73,7 +77,7 @@ class Node(object):
An exception will be generated for other node types.
An exception will be generated for other node types.
"""
"""
variant
=
None
variant
=
None
if
type
(
value
)
==
ua
.
Variant
:
if
isinstance
(
value
,
ua
.
Variant
)
:
variant
=
value
variant
=
value
else
:
else
:
variant
=
ua
.
Variant
(
value
,
varianttype
)
variant
=
ua
.
Variant
(
value
,
varianttype
)
...
@@ -172,7 +176,7 @@ class Node(object):
...
@@ -172,7 +176,7 @@ class Node(object):
el
.
ReferenceTypeId
=
ua
.
TwoByteNodeId
(
ua
.
ObjectIds
.
HierarchicalReferences
)
el
.
ReferenceTypeId
=
ua
.
TwoByteNodeId
(
ua
.
ObjectIds
.
HierarchicalReferences
)
el
.
IsInverse
=
False
el
.
IsInverse
=
False
el
.
IncludeSubtypes
=
True
el
.
IncludeSubtypes
=
True
if
type
(
item
)
==
ua
.
QualifiedName
:
if
isinstance
(
item
,
ua
.
QualifiedName
)
:
el
.
TargetName
=
item
el
.
TargetName
=
item
else
:
else
:
el
.
TargetName
=
ua
.
QualifiedName
.
from_string
(
item
)
el
.
TargetName
=
ua
.
QualifiedName
.
from_string
(
item
)
...
@@ -183,7 +187,7 @@ class Node(object):
...
@@ -183,7 +187,7 @@ class Node(object):
result
=
self
.
server
.
translate_browsepaths_to_nodeids
([
bpath
])
result
=
self
.
server
.
translate_browsepaths_to_nodeids
([
bpath
])
result
=
result
[
0
]
result
=
result
[
0
]
result
.
StatusCode
.
check
()
result
.
StatusCode
.
check
()
#FIXME: seems this method may return several nodes
#
FIXME: seems this method may return several nodes
return
Node
(
self
.
server
,
result
.
Targets
[
0
].
TargetId
)
return
Node
(
self
.
server
,
result
.
Targets
[
0
].
TargetId
)
def
add_folder
(
self
,
*
args
):
def
add_folder
(
self
,
*
args
):
...
@@ -333,7 +337,6 @@ class Node(object):
...
@@ -333,7 +337,6 @@ class Node(object):
self
.
server
.
add_method_callback
(
method
.
nodeid
,
callback
)
self
.
server
.
add_method_callback
(
method
.
nodeid
,
callback
)
return
method
return
method
def
call_method
(
self
,
methodid
,
*
args
):
def
call_method
(
self
,
methodid
,
*
args
):
"""
"""
Call an OPC-UA method. methodid is browse name of child method or the
Call an OPC-UA method. methodid is browse name of child method or the
...
@@ -342,9 +345,9 @@ class Node(object):
...
@@ -342,9 +345,9 @@ class Node(object):
which may be of different types
which may be of different types
returns a list of variants which are output of the method
returns a list of variants which are output of the method
"""
"""
if
type
(
methodid
)
is
str
:
if
isinstance
(
methodid
,
str
)
:
methodid
=
self
.
get_child
(
methodid
).
nodeid
methodid
=
self
.
get_child
(
methodid
).
nodeid
elif
type
(
methodid
)
is
Node
:
elif
isinstance
(
methodid
,
Node
)
:
methodid
=
methodid
.
nodeid
methodid
=
methodid
.
nodeid
arguments
=
[]
arguments
=
[]
...
@@ -391,21 +394,20 @@ class Node(object):
...
@@ -391,21 +394,20 @@ class Node(object):
return
ua
.
NodeId
(
getattr
(
ua
.
ObjectIds
,
variant
.
VariantType
.
name
))
return
ua
.
NodeId
(
getattr
(
ua
.
ObjectIds
,
variant
.
VariantType
.
name
))
def
_parse_add_args
(
self
,
*
args
):
def
_parse_add_args
(
self
,
*
args
):
if
type
(
args
[
0
])
is
ua
.
NodeId
:
if
isinstance
(
args
[
0
],
ua
.
NodeId
)
:
return
args
[
0
],
args
[
1
]
return
args
[
0
],
args
[
1
]
elif
type
(
args
[
0
])
is
str
:
elif
isinstance
(
args
[
0
],
str
)
:
return
ua
.
NodeId
.
from_string
(
args
[
0
]),
ua
.
QualifiedName
.
from_string
(
args
[
1
])
return
ua
.
NodeId
.
from_string
(
args
[
0
]),
ua
.
QualifiedName
.
from_string
(
args
[
1
])
elif
type
(
args
[
0
])
is
int
:
elif
isinstance
(
args
[
0
],
int
)
:
return
generate_nodeid
(
args
[
0
]),
ua
.
QualifiedName
(
args
[
1
],
args
[
0
])
return
generate_nodeid
(
args
[
0
]),
ua
.
QualifiedName
(
args
[
1
],
args
[
0
])
else
:
else
:
raise
TypeError
(
"Add methods takes a nodeid and a qualifiedname as argument, received %s"
%
args
)
raise
TypeError
(
"Add methods takes a nodeid and a qualifiedname as argument, received %s"
%
args
)
__nodeid_counter
=
2000
__nodeid_counter
=
2000
def
generate_nodeid
(
idx
):
def
generate_nodeid
(
idx
):
global
__nodeid_counter
global
__nodeid_counter
__nodeid_counter
+=
1
__nodeid_counter
+=
1
return
ua
.
NodeId
(
__nodeid_counter
,
idx
)
return
ua
.
NodeId
(
__nodeid_counter
,
idx
)
opcua/server.py
View file @
37f69181
...
@@ -16,6 +16,7 @@ from opcua import Node, Subscription, ObjectIds, Event
...
@@ -16,6 +16,7 @@ from opcua import Node, Subscription, ObjectIds, Event
class
Server
(
object
):
class
Server
(
object
):
def
__init__
(
self
):
def
__init__
(
self
):
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
endpoint
=
"opc.tcp://localhost:4841/freeopcua/server/"
self
.
endpoint
=
"opc.tcp://localhost:4841/freeopcua/server/"
...
@@ -26,7 +27,7 @@ class Server(object):
...
@@ -26,7 +27,7 @@ class Server(object):
self
.
iserver
=
InternalServer
()
self
.
iserver
=
InternalServer
()
self
.
bserver
=
None
self
.
bserver
=
None
#setup some expected values
#
setup some expected values
self
.
register_namespace
(
self
.
server_uri
)
self
.
register_namespace
(
self
.
server_uri
)
sa_node
=
self
.
get_node
(
ua
.
NodeId
(
ua
.
ObjectIds
.
Server_ServerArray
))
sa_node
=
self
.
get_node
(
ua
.
NodeId
(
ua
.
ObjectIds
.
Server_ServerArray
))
sa_node
.
set_value
([
self
.
server_uri
])
sa_node
.
set_value
([
self
.
server_uri
])
...
@@ -38,7 +39,7 @@ class Server(object):
...
@@ -38,7 +39,7 @@ class Server(object):
return
self
.
iserver
.
get_endpoints
()
return
self
.
iserver
.
get_endpoints
()
def
_setup_server_nodes
(
self
):
def
_setup_server_nodes
(
self
):
#to be called just before starting server since it needs all parameters to be setup
#
to be called just before starting server since it needs all parameters to be setup
self
.
_set_endpoints
()
self
.
_set_endpoints
()
def
_set_endpoints
(
self
):
def
_set_endpoints
(
self
):
...
@@ -116,7 +117,7 @@ class Server(object):
...
@@ -116,7 +117,7 @@ class Server(object):
uries
=
ns_node
.
get_value
()
uries
=
ns_node
.
get_value
()
uries
.
append
(
uri
)
uries
.
append
(
uri
)
ns_node
.
set_value
(
uries
)
ns_node
.
set_value
(
uries
)
return
(
len
(
uries
)
-
1
)
return
(
len
(
uries
)
-
1
)
def
get_namespace_index
(
self
,
uri
):
def
get_namespace_index
(
self
,
uri
):
uries
=
self
.
get_namespace_array
()
uries
=
self
.
get_namespace_array
()
...
@@ -124,8 +125,3 @@ class Server(object):
...
@@ -124,8 +125,3 @@ class Server(object):
def
get_event_object
(
self
,
etype
=
ObjectIds
.
BaseEventType
,
source
=
ObjectIds
.
Server
):
def
get_event_object
(
self
,
etype
=
ObjectIds
.
BaseEventType
,
source
=
ObjectIds
.
Server
):
return
Event
(
self
.
iserver
.
isession
,
etype
,
source
)
return
Event
(
self
.
iserver
.
isession
,
etype
,
source
)
opcua/standard_address_space.py
View file @
37f69181
...
@@ -23,9 +23,8 @@ def fill_address_space(nodeservice):
...
@@ -23,9 +23,8 @@ def fill_address_space(nodeservice):
create_standard_address_space_Part11
(
nodeservice
)
create_standard_address_space_Part11
(
nodeservice
)
create_standard_address_space_Part13
(
nodeservice
)
create_standard_address_space_Part13
(
nodeservice
)
def
fill_address_space_from_disk
(
aspace
):
def
fill_address_space_from_disk
(
aspace
):
dirname
=
os
.
path
.
dirname
(
opcua
.
__file__
)
dirname
=
os
.
path
.
dirname
(
opcua
.
__file__
)
path
=
os
.
path
.
join
(
dirname
,
"binary_address_space.pickle"
)
path
=
os
.
path
.
join
(
dirname
,
"binary_address_space.pickle"
)
aspace
.
load
(
path
)
aspace
.
load
(
path
)
opcua/subscription.py
View file @
37f69181
...
@@ -12,14 +12,16 @@ from opcua import ObjectIds
...
@@ -12,14 +12,16 @@ from opcua import ObjectIds
from
opcua
import
AttributeIds
from
opcua
import
AttributeIds
from
opcua
import
Event
from
opcua
import
Event
class
EventResult
():
class
EventResult
():
def
__str__
(
self
):
def
__str__
(
self
):
return
"EventResult({})"
.
format
([
str
(
k
)
+
":"
+
str
(
v
)
for
k
,
v
in
self
.
__dict__
.
items
()])
return
"EventResult({})"
.
format
([
str
(
k
)
+
":"
+
str
(
v
)
for
k
,
v
in
self
.
__dict__
.
items
()])
__repr__
=
__str__
__repr__
=
__str__
class
SubscriptionItemData
():
class
SubscriptionItemData
():
def
__init__
(
self
):
def
__init__
(
self
):
self
.
node
=
None
self
.
node
=
None
self
.
client_handle
=
None
self
.
client_handle
=
None
...
@@ -27,18 +29,20 @@ class SubscriptionItemData():
...
@@ -27,18 +29,20 @@ class SubscriptionItemData():
self
.
attribute
=
None
self
.
attribute
=
None
self
.
mfilter
=
None
self
.
mfilter
=
None
class
Subscription
(
object
):
class
Subscription
(
object
):
def
__init__
(
self
,
server
,
params
,
handler
):
def
__init__
(
self
,
server
,
params
,
handler
):
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
server
=
server
self
.
server
=
server
self
.
_client_handle
=
200
self
.
_client_handle
=
200
self
.
_handler
=
handler
self
.
_handler
=
handler
self
.
parameters
=
params
#
move to data class
self
.
parameters
=
params
#
move to data class
self
.
_monitoreditems_map
=
{}
self
.
_monitoreditems_map
=
{}
self
.
_lock
=
RLock
()
self
.
_lock
=
RLock
()
self
.
subscription_id
=
None
self
.
subscription_id
=
None
response
=
self
.
server
.
create_subscription
(
params
,
self
.
publish_callback
)
response
=
self
.
server
.
create_subscription
(
params
,
self
.
publish_callback
)
self
.
subscription_id
=
response
.
SubscriptionId
#
move to data class
self
.
subscription_id
=
response
.
SubscriptionId
#
move to data class
self
.
server
.
publish
()
self
.
server
.
publish
()
self
.
server
.
publish
()
self
.
server
.
publish
()
...
@@ -133,7 +137,7 @@ class Subscription(object):
...
@@ -133,7 +137,7 @@ class Subscription(object):
rv
=
ua
.
ReadValueId
()
rv
=
ua
.
ReadValueId
()
rv
.
NodeId
=
node
.
nodeid
rv
.
NodeId
=
node
.
nodeid
rv
.
AttributeId
=
attr
rv
.
AttributeId
=
attr
#rv.IndexRange //We leave it null, then the entire array is returned
#
rv.IndexRange //We leave it null, then the entire array is returned
mparams
=
ua
.
MonitoringParameters
()
mparams
=
ua
.
MonitoringParameters
()
self
.
_client_handle
+=
1
self
.
_client_handle
+=
1
mparams
.
ClientHandle
=
self
.
_client_handle
mparams
.
ClientHandle
=
self
.
_client_handle
...
@@ -176,6 +180,3 @@ class Subscription(object):
...
@@ -176,6 +180,3 @@ class Subscription(object):
params
.
MonitoredItemIds
=
[
handle
]
params
.
MonitoredItemIds
=
[
handle
]
results
=
self
.
server
.
delete_monitored_items
(
params
)
results
=
self
.
server
.
delete_monitored_items
(
params
)
results
[
0
].
check
()
results
[
0
].
check
()
opcua/subscription_service.py
View file @
37f69181
...
@@ -8,7 +8,9 @@ import logging
...
@@ -8,7 +8,9 @@ import logging
from
opcua
import
ua
from
opcua
import
ua
class
SubscriptionService
(
object
):
class
SubscriptionService
(
object
):
def
__init__
(
self
,
loop
,
aspace
):
def
__init__
(
self
,
loop
,
aspace
):
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
loop
=
loop
self
.
loop
=
loop
...
@@ -90,7 +92,7 @@ class SubscriptionService(object):
...
@@ -90,7 +92,7 @@ class SubscriptionService(object):
def
republish
(
self
,
params
):
def
republish
(
self
,
params
):
with
self
.
_lock
:
with
self
.
_lock
:
if
not
params
.
SubscriptionId
in
self
.
subscriptions
:
if
not
params
.
SubscriptionId
in
self
.
subscriptions
:
#what should I do?
#
what should I do?
return
ua
.
NotificationMessage
()
return
ua
.
NotificationMessage
()
return
self
.
subscriptions
[
params
.
SubscriptionId
].
republish
(
params
.
RetransmitSequenceNumber
)
return
self
.
subscriptions
[
params
.
SubscriptionId
].
republish
(
params
.
RetransmitSequenceNumber
)
...
@@ -100,8 +102,8 @@ class SubscriptionService(object):
...
@@ -100,8 +102,8 @@ class SubscriptionService(object):
sub
.
trigger_event
(
event
)
sub
.
trigger_event
(
event
)
class
MonitoredItemData
(
object
):
class
MonitoredItemData
(
object
):
def
__init__
(
self
):
def
__init__
(
self
):
self
.
client_handle
=
None
self
.
client_handle
=
None
self
.
callback_handle
=
None
self
.
callback_handle
=
None
...
@@ -111,6 +113,7 @@ class MonitoredItemData(object):
...
@@ -111,6 +113,7 @@ class MonitoredItemData(object):
class
InternalSubscription
(
object
):
class
InternalSubscription
(
object
):
def
__init__
(
self
,
manager
,
data
,
addressspace
,
callback
):
def
__init__
(
self
,
manager
,
data
,
addressspace
,
callback
):
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
aspace
=
addressspace
self
.
aspace
=
addressspace
...
@@ -151,7 +154,7 @@ class InternalSubscription(object):
...
@@ -151,7 +154,7 @@ class InternalSubscription(object):
def
_subscription_loop
(
self
):
def
_subscription_loop
(
self
):
#self.logger.debug("%s loop", self)
#self.logger.debug("%s loop", self)
if
not
self
.
_stopev
:
if
not
self
.
_stopev
:
self
.
manager
.
loop
.
call_later
(
self
.
data
.
RevisedPublishingInterval
/
1000.0
,
self
.
_sub_loop
)
self
.
manager
.
loop
.
call_later
(
self
.
data
.
RevisedPublishingInterval
/
1000.0
,
self
.
_sub_loop
)
def
_sub_loop
(
self
):
def
_sub_loop
(
self
):
self
.
publish_results
()
self
.
publish_results
()
...
@@ -170,11 +173,11 @@ class InternalSubscription(object):
...
@@ -170,11 +173,11 @@ class InternalSubscription(object):
def
publish_results
(
self
):
def
publish_results
(
self
):
if
self
.
_publish_cycles_count
>
self
.
data
.
RevisedLifetimeCount
:
if
self
.
_publish_cycles_count
>
self
.
data
.
RevisedLifetimeCount
:
self
.
logger
.
warn
(
"Subscription %s has expired, publish cycle count(%s) > lifetime count (%s)"
,
self
,
self
.
_publish_cycles_count
,
self
.
data
.
RevisedLifetimeCount
)
self
.
logger
.
warn
(
"Subscription %s has expired, publish cycle count(%s) > lifetime count (%s)"
,
self
,
self
.
_publish_cycles_count
,
self
.
data
.
RevisedLifetimeCount
)
#FIXME this will never be send since we do not have publish request anyway
#
FIXME this will never be send since we do not have publish request anyway
self
.
trigger_statuschange
(
ua
.
StatusCode
(
ua
.
StatusCodes
.
BadTimeout
))
self
.
trigger_statuschange
(
ua
.
StatusCode
(
ua
.
StatusCodes
.
BadTimeout
))
self
.
_stopev
=
True
self
.
_stopev
=
True
with
self
.
_lock
:
with
self
.
_lock
:
if
self
.
has_published_results
():
#
FIXME: should we pop a publish request here? or we do not care?
if
self
.
has_published_results
():
#
FIXME: should we pop a publish request here? or we do not care?
self
.
_publish_cycles_count
+=
1
self
.
_publish_cycles_count
+=
1
result
=
self
.
_pop_publish_result
()
result
=
self
.
_pop_publish_result
()
self
.
callback
(
result
)
self
.
callback
(
result
)
...
@@ -248,11 +251,11 @@ class InternalSubscription(object):
...
@@ -248,11 +251,11 @@ class InternalSubscription(object):
result
=
ua
.
MonitoredItemCreateResult
()
result
=
ua
.
MonitoredItemCreateResult
()
if
mdata
.
monitored_item_id
==
params
.
MonitoredItemId
:
if
mdata
.
monitored_item_id
==
params
.
MonitoredItemId
:
result
.
RevisedSamplingInterval
=
self
.
data
.
RevisedPublishingInterval
result
.
RevisedSamplingInterval
=
self
.
data
.
RevisedPublishingInterval
result
.
RevisedQueueSize
=
ua
.
downcast_extobject
(
params
.
RequestedParameters
.
QueueSize
)
#
FIXME check and use value
result
.
RevisedQueueSize
=
ua
.
downcast_extobject
(
params
.
RequestedParameters
.
QueueSize
)
#
FIXME check and use value
result
.
FilterResult
=
params
.
RequestedParameters
.
Filter
result
.
FilterResult
=
params
.
RequestedParameters
.
Filter
mdata
.
parameters
=
result
mdata
.
parameters
=
result
return
result
return
result
#FIXME modify event subscriptions
#
FIXME modify event subscriptions
result
=
ua
.
MonitoredItemCreateResult
()
result
=
ua
.
MonitoredItemCreateResult
()
result
.
StatusCode
(
ua
.
StatusCodes
.
BadMonitoredItemIdInvalid
)
result
.
StatusCode
(
ua
.
StatusCodes
.
BadMonitoredItemIdInvalid
)
return
result
return
result
...
@@ -261,7 +264,7 @@ class InternalSubscription(object):
...
@@ -261,7 +264,7 @@ class InternalSubscription(object):
with
self
.
_lock
:
with
self
.
_lock
:
result
=
ua
.
MonitoredItemCreateResult
()
result
=
ua
.
MonitoredItemCreateResult
()
result
.
RevisedSamplingInterval
=
self
.
data
.
RevisedPublishingInterval
result
.
RevisedSamplingInterval
=
self
.
data
.
RevisedPublishingInterval
result
.
RevisedQueueSize
=
params
.
RequestedParameters
.
QueueSize
#
FIXME check and use value
result
.
RevisedQueueSize
=
params
.
RequestedParameters
.
QueueSize
#
FIXME check and use value
result
.
FilterResult
=
ua
.
downcast_extobject
(
params
.
RequestedParameters
.
Filter
)
result
.
FilterResult
=
ua
.
downcast_extobject
(
params
.
RequestedParameters
.
Filter
)
self
.
_monitored_item_counter
+=
1
self
.
_monitored_item_counter
+=
1
result
.
MonitoredItemId
=
self
.
_monitored_item_counter
result
.
MonitoredItemId
=
self
.
_monitored_item_counter
...
@@ -284,7 +287,7 @@ class InternalSubscription(object):
...
@@ -284,7 +287,7 @@ class InternalSubscription(object):
self
.
logger
.
debug
(
"adding callback return status %s and handle %s"
,
result
.
StatusCode
,
handle
)
self
.
logger
.
debug
(
"adding callback return status %s and handle %s"
,
result
.
StatusCode
,
handle
)
mdata
.
callback_handle
=
handle
mdata
.
callback_handle
=
handle
self
.
_monitored_datachange
[
handle
]
=
result
.
MonitoredItemId
self
.
_monitored_datachange
[
handle
]
=
result
.
MonitoredItemId
#force data change event generation
#
force data change event generation
self
.
trigger_datachange
(
handle
,
params
.
ItemToMonitor
.
NodeId
,
params
.
ItemToMonitor
.
AttributeId
)
self
.
trigger_datachange
(
handle
,
params
.
ItemToMonitor
.
NodeId
,
params
.
ItemToMonitor
.
AttributeId
)
return
result
return
result
...
@@ -312,7 +315,6 @@ class InternalSubscription(object):
...
@@ -312,7 +315,6 @@ class InternalSubscription(object):
self
.
_monitored_items
.
pop
(
mid
)
self
.
_monitored_items
.
pop
(
mid
)
return
ua
.
StatusCode
()
return
ua
.
StatusCode
()
def
datachange_callback
(
self
,
handle
,
value
):
def
datachange_callback
(
self
,
handle
,
value
):
self
.
logger
.
info
(
"subscription %s: datachange callback called with handle '%s' and value '%s'"
,
self
,
handle
,
value
.
Value
)
self
.
logger
.
info
(
"subscription %s: datachange callback called with handle '%s' and value '%s'"
,
self
,
handle
,
value
.
Value
)
event
=
ua
.
MonitoredItemNotification
()
event
=
ua
.
MonitoredItemNotification
()
...
@@ -354,9 +356,3 @@ class InternalSubscription(object):
...
@@ -354,9 +356,3 @@ class InternalSubscription(object):
except
AttributeError
:
except
AttributeError
:
fields
.
append
(
ua
.
Variant
())
fields
.
append
(
ua
.
Variant
())
return
fields
return
fields
opcua/uaprocessor.py
View file @
37f69181
...
@@ -6,13 +6,17 @@ from datetime import datetime
...
@@ -6,13 +6,17 @@ from datetime import datetime
from
opcua
import
ua
from
opcua
import
ua
from
opcua
import
utils
from
opcua
import
utils
class
PublishRequestData
(
object
):
class
PublishRequestData
(
object
):
def
__init__
(
self
):
def
__init__
(
self
):
self
.
requesthdr
=
None
self
.
requesthdr
=
None
self
.
algohdr
=
None
self
.
algohdr
=
None
self
.
seqhdr
=
None
self
.
seqhdr
=
None
class
UAProcessor
(
object
):
class
UAProcessor
(
object
):
def
__init__
(
self
,
internal_server
,
socket
,
name
):
def
__init__
(
self
,
internal_server
,
socket
,
name
):
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
iserver
=
internal_server
self
.
iserver
=
internal_server
...
@@ -26,7 +30,7 @@ class UAProcessor(object):
...
@@ -26,7 +30,7 @@ class UAProcessor(object):
self
.
_seq_number
=
1
self
.
_seq_number
=
1
def
loop
(
self
):
def
loop
(
self
):
#first we want a hello message
#
first we want a hello message
while
True
:
while
True
:
header
=
ua
.
Header
.
from_stream
(
self
.
socket
)
header
=
ua
.
Header
.
from_stream
(
self
.
socket
)
body
=
self
.
_receive_body
(
header
.
body_size
)
body
=
self
.
_receive_body
(
header
.
body_size
)
...
@@ -59,7 +63,7 @@ class UAProcessor(object):
...
@@ -59,7 +63,7 @@ class UAProcessor(object):
seqhdr
.
SequenceNumber
=
self
.
_seq_number
seqhdr
.
SequenceNumber
=
self
.
_seq_number
self
.
_seq_number
+=
1
self
.
_seq_number
+=
1
hdr
=
ua
.
Header
(
msgtype
,
ua
.
ChunkType
.
Single
,
self
.
channel
.
SecurityToken
.
ChannelId
)
hdr
=
ua
.
Header
(
msgtype
,
ua
.
ChunkType
.
Single
,
self
.
channel
.
SecurityToken
.
ChannelId
)
if
type
(
algohdr
)
is
ua
.
SymmetricAlgorithmHeader
:
if
isinstance
(
algohdr
,
ua
.
SymmetricAlgorithmHeader
)
:
algohdr
.
TokenId
=
self
.
channel
.
SecurityToken
.
TokenId
algohdr
.
TokenId
=
self
.
channel
.
SecurityToken
.
TokenId
self
.
_write_socket
(
hdr
,
algohdr
,
seqhdr
,
response
)
self
.
_write_socket
(
hdr
,
algohdr
,
seqhdr
,
response
)
...
@@ -89,7 +93,7 @@ class UAProcessor(object):
...
@@ -89,7 +93,7 @@ class UAProcessor(object):
request
=
ua
.
OpenSecureChannelRequest
.
from_binary
(
body
)
request
=
ua
.
OpenSecureChannelRequest
.
from_binary
(
body
)
channel
=
self
.
_open_secure_channel
(
request
.
Parameters
)
channel
=
self
.
_open_secure_channel
(
request
.
Parameters
)
#send response
#
send response
response
=
ua
.
OpenSecureChannelResponse
()
response
=
ua
.
OpenSecureChannelResponse
()
response
.
Parameters
=
channel
response
.
Parameters
=
channel
self
.
send_response
(
request
.
RequestHeader
.
RequestHandle
,
algohdr
,
seqhdr
,
response
,
ua
.
MessageType
.
SecureOpen
)
self
.
send_response
(
request
.
RequestHeader
.
RequestHandle
,
algohdr
,
seqhdr
,
response
,
ua
.
MessageType
.
SecureOpen
)
...
@@ -131,8 +135,8 @@ class UAProcessor(object):
...
@@ -131,8 +135,8 @@ class UAProcessor(object):
self
.
logger
.
info
(
"Create session request"
)
self
.
logger
.
info
(
"Create session request"
)
params
=
ua
.
CreateSessionParameters
.
from_binary
(
body
)
params
=
ua
.
CreateSessionParameters
.
from_binary
(
body
)
self
.
session
=
self
.
iserver
.
create_session
(
self
.
name
)
#
create the session on server
self
.
session
=
self
.
iserver
.
create_session
(
self
.
name
)
#
create the session on server
sessiondata
=
self
.
session
.
create_session
(
params
)
#
get a session creation result to send back
sessiondata
=
self
.
session
.
create_session
(
params
)
#
get a session creation result to send back
response
=
ua
.
CreateSessionResponse
()
response
=
ua
.
CreateSessionResponse
()
response
.
Parameters
=
sessiondata
response
.
Parameters
=
sessiondata
...
@@ -153,7 +157,7 @@ class UAProcessor(object):
...
@@ -153,7 +157,7 @@ class UAProcessor(object):
if
not
self
.
session
:
if
not
self
.
session
:
#result = ua.ActivateSessionResult()
#result = ua.ActivateSessionResult()
#result.Results.append(ua.StatusCode(ua.StatusCodes.BadSessionIdInvalid))
#
result.Results.append(ua.StatusCode(ua.StatusCodes.BadSessionIdInvalid))
response
=
ua
.
ServiceFault
()
response
=
ua
.
ServiceFault
()
response
.
ResponseHeader
.
ServiceResult
=
ua
.
StatusCode
(
ua
.
StatusCodes
.
BadSessionIdInvalid
)
response
.
ResponseHeader
.
ServiceResult
=
ua
.
StatusCode
(
ua
.
StatusCodes
.
BadSessionIdInvalid
)
self
.
send_response
(
requesthdr
.
RequestHandle
,
algohdr
,
seqhdr
,
response
)
self
.
send_response
(
requesthdr
.
RequestHandle
,
algohdr
,
seqhdr
,
response
)
...
@@ -338,7 +342,7 @@ class UAProcessor(object):
...
@@ -338,7 +342,7 @@ class UAProcessor(object):
self
.
logger
.
info
(
"open secure channel"
)
self
.
logger
.
info
(
"open secure channel"
)
if
params
.
RequestType
==
ua
.
SecurityTokenRequestType
.
Issue
:
if
params
.
RequestType
==
ua
.
SecurityTokenRequestType
.
Issue
:
self
.
channel
=
ua
.
OpenSecureChannelResult
()
self
.
channel
=
ua
.
OpenSecureChannelResult
()
self
.
channel
.
SecurityToken
.
TokenId
=
13
#
random value
self
.
channel
.
SecurityToken
.
TokenId
=
13
#
random value
self
.
channel
.
SecurityToken
.
ChannelId
=
self
.
iserver
.
get_new_channel_id
()
self
.
channel
.
SecurityToken
.
ChannelId
=
self
.
iserver
.
get_new_channel_id
()
self
.
channel
.
SecurityToken
.
RevisedLifetime
=
params
.
RequestedLifetime
self
.
channel
.
SecurityToken
.
RevisedLifetime
=
params
.
RequestedLifetime
self
.
channel
.
SecurityToken
.
TokenId
+=
1
self
.
channel
.
SecurityToken
.
TokenId
+=
1
...
@@ -346,5 +350,3 @@ class UAProcessor(object):
...
@@ -346,5 +350,3 @@ class UAProcessor(object):
self
.
channel
.
SecurityToken
.
RevisedLifetime
=
params
.
RequestedLifetime
self
.
channel
.
SecurityToken
.
RevisedLifetime
=
params
.
RequestedLifetime
self
.
channel
.
ServerNonce
=
utils
.
create_nonce
()
self
.
channel
.
ServerNonce
=
utils
.
create_nonce
()
return
self
.
channel
return
self
.
channel
opcua/uaprotocol.py
View file @
37f69181
...
@@ -5,7 +5,6 @@ from opcua.uaprotocol_auto import *
...
@@ -5,7 +5,6 @@ from opcua.uaprotocol_auto import *
from
opcua.uaprotocol_hand
import
*
from
opcua.uaprotocol_hand
import
*
# FIXME: this is really crappy, should thing about a better implementation
# FIXME: this is really crappy, should thing about a better implementation
# maybe never inherit extensionobject and parse only body....
# maybe never inherit extensionobject and parse only body....
def
downcast_extobject
(
item
):
def
downcast_extobject
(
item
):
...
@@ -15,5 +14,3 @@ def downcast_extobject(item):
...
@@ -15,5 +14,3 @@ def downcast_extobject(item):
classname
=
objectidname
.
split
(
"_"
)[
0
]
classname
=
objectidname
.
split
(
"_"
)[
0
]
cmd
=
"{}.from_binary(utils.Buffer(item.to_binary()))"
.
format
(
classname
)
cmd
=
"{}.from_binary(utils.Buffer(item.to_binary()))"
.
format
(
classname
)
return
eval
(
cmd
)
return
eval
(
cmd
)
opcua/uaprotocol_hand.py
View file @
37f69181
...
@@ -14,14 +14,16 @@ logger = logging.getLogger('opcua.uaprotocol')
...
@@ -14,14 +14,16 @@ logger = logging.getLogger('opcua.uaprotocol')
class
SocketClosedException
(
Exception
):
class
SocketClosedException
(
Exception
):
pass
pass
def
get_bytes_from_sock
(
sock
,
size
):
def
get_bytes_from_sock
(
sock
,
size
):
data
=
utils
.
recv_all
(
sock
,
size
)
data
=
utils
.
recv_all
(
sock
,
size
)
if
len
(
data
)
<
size
:
#
socket has closed!
if
len
(
data
)
<
size
:
#
socket has closed!
raise
SocketClosedException
(
"Server socket has closed"
)
raise
SocketClosedException
(
"Server socket has closed"
)
return
io
.
BytesIO
(
data
)
return
io
.
BytesIO
(
data
)
class
Hello
(
uatypes
.
FrozenClass
):
class
Hello
(
uatypes
.
FrozenClass
):
def
__init__
(
self
):
def
__init__
(
self
):
self
.
ProtocolVersion
=
0
self
.
ProtocolVersion
=
0
self
.
ReceiveBufferSize
=
65536
self
.
ReceiveBufferSize
=
65536
...
@@ -53,9 +55,8 @@ class Hello(uatypes.FrozenClass):
...
@@ -53,9 +55,8 @@ class Hello(uatypes.FrozenClass):
return
hello
return
hello
class
MessageType
(
object
):
class
MessageType
(
object
):
Invalid
=
b"INV"
#
FIXME: check value
Invalid
=
b"INV"
#
FIXME: check value
Hello
=
b"HEL"
Hello
=
b"HEL"
Acknowledge
=
b"ACK"
Acknowledge
=
b"ACK"
Error
=
b"ERR"
Error
=
b"ERR"
...
@@ -63,15 +64,16 @@ class MessageType(object):
...
@@ -63,15 +64,16 @@ class MessageType(object):
SecureClose
=
b"CLO"
SecureClose
=
b"CLO"
SecureMessage
=
b"MSG"
SecureMessage
=
b"MSG"
class
ChunkType
(
object
):
class
ChunkType
(
object
):
Invalid
=
b"0"
#
FIXME check
Invalid
=
b"0"
#
FIXME check
Single
=
b"F"
Single
=
b"F"
Intermediate
=
b"C"
Intermediate
=
b"C"
Final
=
b"A"
Final
=
b"A"
class
Header
(
uatypes
.
FrozenClass
):
class
Header
(
uatypes
.
FrozenClass
):
def
__init__
(
self
,
msgType
=
None
,
chunkType
=
None
,
channelid
=
0
):
def
__init__
(
self
,
msgType
=
None
,
chunkType
=
None
,
channelid
=
0
):
self
.
MessageType
=
msgType
self
.
MessageType
=
msgType
self
.
ChunkType
=
chunkType
self
.
ChunkType
=
chunkType
...
@@ -113,6 +115,7 @@ class Header(uatypes.FrozenClass):
...
@@ -113,6 +115,7 @@ class Header(uatypes.FrozenClass):
class
ErrorMessage
(
uatypes
.
FrozenClass
):
class
ErrorMessage
(
uatypes
.
FrozenClass
):
def
__init__
(
self
):
def
__init__
(
self
):
self
.
Error
=
uatypes
.
StatusCode
()
self
.
Error
=
uatypes
.
StatusCode
()
self
.
Reason
=
""
self
.
Reason
=
""
...
@@ -137,12 +140,13 @@ class ErrorMessage(uatypes.FrozenClass):
...
@@ -137,12 +140,13 @@ class ErrorMessage(uatypes.FrozenClass):
class
Acknowledge
(
uatypes
.
FrozenClass
):
class
Acknowledge
(
uatypes
.
FrozenClass
):
def
__init__
(
self
):
def
__init__
(
self
):
self
.
ProtocolVersion
=
0
self
.
ProtocolVersion
=
0
self
.
ReceiveBufferSize
=
65536
self
.
ReceiveBufferSize
=
65536
self
.
SendBufferSize
=
65536
self
.
SendBufferSize
=
65536
self
.
MaxMessageSize
=
0
#
No limits
self
.
MaxMessageSize
=
0
#
No limits
self
.
MaxChunkCount
=
0
#
No limits
self
.
MaxChunkCount
=
0
#
No limits
self
.
_freeze
()
self
.
_freeze
()
def
to_binary
(
self
):
def
to_binary
(
self
):
...
@@ -154,8 +158,6 @@ class Acknowledge(uatypes.FrozenClass):
...
@@ -154,8 +158,6 @@ class Acknowledge(uatypes.FrozenClass):
b
.
append
(
struct
.
pack
(
"<I"
,
self
.
MaxChunkCount
))
b
.
append
(
struct
.
pack
(
"<I"
,
self
.
MaxChunkCount
))
return
b""
.
join
(
b
)
return
b""
.
join
(
b
)
@
staticmethod
@
staticmethod
def
from_binary
(
data
):
def
from_binary
(
data
):
ack
=
Acknowledge
()
ack
=
Acknowledge
()
...
@@ -166,7 +168,9 @@ class Acknowledge(uatypes.FrozenClass):
...
@@ -166,7 +168,9 @@ class Acknowledge(uatypes.FrozenClass):
ack
.
MaxChunkCount
=
struct
.
unpack
(
"<I"
,
data
.
read
(
4
))[
0
]
ack
.
MaxChunkCount
=
struct
.
unpack
(
"<I"
,
data
.
read
(
4
))[
0
]
return
ack
return
ack
class
AsymmetricAlgorithmHeader
(
uatypes
.
FrozenClass
):
class
AsymmetricAlgorithmHeader
(
uatypes
.
FrozenClass
):
def
__init__
(
self
):
def
__init__
(
self
):
self
.
SecurityPolicyURI
=
"http://opcfoundation.org/UA/SecurityPolicy#None"
self
.
SecurityPolicyURI
=
"http://opcfoundation.org/UA/SecurityPolicy#None"
self
.
SenderCertificate
=
b""
self
.
SenderCertificate
=
b""
...
@@ -194,6 +198,7 @@ class AsymmetricAlgorithmHeader(uatypes.FrozenClass):
...
@@ -194,6 +198,7 @@ class AsymmetricAlgorithmHeader(uatypes.FrozenClass):
class
SymmetricAlgorithmHeader
(
uatypes
.
FrozenClass
):
class
SymmetricAlgorithmHeader
(
uatypes
.
FrozenClass
):
def
__init__
(
self
):
def
__init__
(
self
):
self
.
TokenId
=
0
self
.
TokenId
=
0
self
.
_freeze
()
self
.
_freeze
()
...
@@ -213,6 +218,7 @@ class SymmetricAlgorithmHeader(uatypes.FrozenClass):
...
@@ -213,6 +218,7 @@ class SymmetricAlgorithmHeader(uatypes.FrozenClass):
class
SequenceHeader
(
uatypes
.
FrozenClass
):
class
SequenceHeader
(
uatypes
.
FrozenClass
):
def
__init__
(
self
):
def
__init__
(
self
):
self
.
SequenceNumber
=
None
self
.
SequenceNumber
=
None
self
.
RequestId
=
None
self
.
RequestId
=
None
...
@@ -235,54 +241,64 @@ class SequenceHeader(uatypes.FrozenClass):
...
@@ -235,54 +241,64 @@ class SequenceHeader(uatypes.FrozenClass):
return
"{}(SequenceNumber:{}, RequestId:{} )"
.
format
(
self
.
__class__
.
__name__
,
self
.
SequenceNumber
,
self
.
RequestId
)
return
"{}(SequenceNumber:{}, RequestId:{} )"
.
format
(
self
.
__class__
.
__name__
,
self
.
SequenceNumber
,
self
.
RequestId
)
__repr__
=
__str__
__repr__
=
__str__
#
##
FIXES for missing switchfield in NodeAttributes classes
#
FIXES for missing switchfield in NodeAttributes classes
ana
=
auto
.
NodeAttributesMask
ana
=
auto
.
NodeAttributesMask
class
ObjectAttributes
(
auto
.
ObjectAttributes
):
class
ObjectAttributes
(
auto
.
ObjectAttributes
):
def
__init__
(
self
):
def
__init__
(
self
):
auto
.
ObjectAttributes
.
__init__
(
self
)
auto
.
ObjectAttributes
.
__init__
(
self
)
self
.
SpecifiedAttributes
=
ana
.
DisplayName
|
ana
.
Description
|
ana
.
WriteMask
|
ana
.
UserWriteMask
|
ana
.
EventNotifier
self
.
SpecifiedAttributes
=
ana
.
DisplayName
|
ana
.
Description
|
ana
.
WriteMask
|
ana
.
UserWriteMask
|
ana
.
EventNotifier
class
ObjectTypeAttributes
(
auto
.
ObjectTypeAttributes
):
class
ObjectTypeAttributes
(
auto
.
ObjectTypeAttributes
):
def
__init__
(
self
):
def
__init__
(
self
):
auto
.
ObjectTypeAttributes
.
__init__
(
self
)
auto
.
ObjectTypeAttributes
.
__init__
(
self
)
self
.
SpecifiedAttributes
=
ana
.
DisplayName
|
ana
.
Description
|
ana
.
WriteMask
|
ana
.
UserWriteMask
|
ana
.
IsAbstract
self
.
SpecifiedAttributes
=
ana
.
DisplayName
|
ana
.
Description
|
ana
.
WriteMask
|
ana
.
UserWriteMask
|
ana
.
IsAbstract
class
VariableAttributes
(
auto
.
VariableAttributes
):
class
VariableAttributes
(
auto
.
VariableAttributes
):
def
__init__
(
self
):
def
__init__
(
self
):
auto
.
VariableAttributes
.
__init__
(
self
)
auto
.
VariableAttributes
.
__init__
(
self
)
self
.
SpecifiedAttributes
=
ana
.
DisplayName
|
ana
.
Description
|
ana
.
WriteMask
|
ana
.
UserWriteMask
|
ana
.
Value
|
ana
.
DataType
|
ana
.
ValueRank
|
ana
.
ArrayDimensions
|
ana
.
AccessLevel
|
ana
.
UserAccessLevel
|
ana
.
MinimumSamplingInterval
|
ana
.
Historizing
self
.
SpecifiedAttributes
=
ana
.
DisplayName
|
ana
.
Description
|
ana
.
WriteMask
|
ana
.
UserWriteMask
|
ana
.
Value
|
ana
.
DataType
|
ana
.
ValueRank
|
ana
.
ArrayDimensions
|
ana
.
AccessLevel
|
ana
.
UserAccessLevel
|
ana
.
MinimumSamplingInterval
|
ana
.
Historizing
class
VariableTypeAttributes
(
auto
.
VariableTypeAttributes
):
class
VariableTypeAttributes
(
auto
.
VariableTypeAttributes
):
def
__init__
(
self
):
def
__init__
(
self
):
auto
.
VariableTypeAttributes
.
__init__
(
self
)
auto
.
VariableTypeAttributes
.
__init__
(
self
)
self
.
SpecifiedAttributes
=
ana
.
DisplayName
|
ana
.
Description
|
ana
.
WriteMask
|
ana
.
UserWriteMask
|
ana
.
Value
|
ana
.
DataType
|
ana
.
ValueRank
|
ana
.
ArrayDimensions
|
ana
.
IsAbstract
self
.
SpecifiedAttributes
=
ana
.
DisplayName
|
ana
.
Description
|
ana
.
WriteMask
|
ana
.
UserWriteMask
|
ana
.
Value
|
ana
.
DataType
|
ana
.
ValueRank
|
ana
.
ArrayDimensions
|
ana
.
IsAbstract
class
MethodAttributes
(
auto
.
MethodAttributes
):
class
MethodAttributes
(
auto
.
MethodAttributes
):
def
__init__
(
self
):
def
__init__
(
self
):
auto
.
MethodAttributes
.
__init__
(
self
)
auto
.
MethodAttributes
.
__init__
(
self
)
self
.
SpecifiedAttributes
=
ana
.
DisplayName
|
ana
.
Description
|
ana
.
WriteMask
|
ana
.
UserWriteMask
|
ana
.
Executable
|
ana
.
UserExecutable
self
.
SpecifiedAttributes
=
ana
.
DisplayName
|
ana
.
Description
|
ana
.
WriteMask
|
ana
.
UserWriteMask
|
ana
.
Executable
|
ana
.
UserExecutable
class
ReferenceTypeAttributes
(
auto
.
ReferenceTypeAttributes
):
class
ReferenceTypeAttributes
(
auto
.
ReferenceTypeAttributes
):
def
__init__
(
self
):
def
__init__
(
self
):
auto
.
ReferenceTypeAttributes
.
__init__
(
self
)
auto
.
ReferenceTypeAttributes
.
__init__
(
self
)
self
.
SpecifiedAttributes
=
ana
.
DisplayName
|
ana
.
Description
|
ana
.
WriteMask
|
ana
.
UserWriteMask
|
ana
.
IsAbstract
|
ana
.
Symmetric
|
ana
.
InverseName
self
.
SpecifiedAttributes
=
ana
.
DisplayName
|
ana
.
Description
|
ana
.
WriteMask
|
ana
.
UserWriteMask
|
ana
.
IsAbstract
|
ana
.
Symmetric
|
ana
.
InverseName
class
DataTypeAttributes
(
auto
.
DataTypeAttributes
):
class
DataTypeAttributes
(
auto
.
DataTypeAttributes
):
def
__init__
(
self
):
def
__init__
(
self
):
auto
.
DataTypeAttributes
.
__init__
(
self
)
auto
.
DataTypeAttributes
.
__init__
(
self
)
self
.
SpecifiedAttributes
=
ana
.
DisplayName
|
ana
.
Description
|
ana
.
WriteMask
|
ana
.
UserWriteMask
|
ana
.
IsAbstract
self
.
SpecifiedAttributes
=
ana
.
DisplayName
|
ana
.
Description
|
ana
.
WriteMask
|
ana
.
UserWriteMask
|
ana
.
IsAbstract
class
ViewAttributes
(
auto
.
ViewAttributes
):
class
ViewAttributes
(
auto
.
ViewAttributes
):
def
__init__
(
self
):
def
__init__
(
self
):
auto
.
ViewAttributes
.
__init__
(
self
)
auto
.
ViewAttributes
.
__init__
(
self
)
self
.
SpecifiedAttributes
=
ana
.
DisplayName
|
ana
.
Description
|
ana
.
WriteMask
|
ana
.
UserWriteMask
|
ana
.
ContainsNoLoops
|
ana
.
EventNotifier
self
.
SpecifiedAttributes
=
ana
.
DisplayName
|
ana
.
Description
|
ana
.
WriteMask
|
ana
.
UserWriteMask
|
ana
.
ContainsNoLoops
|
ana
.
EventNotifier
ObjectIdsInv
=
{
v
:
k
for
k
,
v
in
ObjectIds
.
__dict__
.
items
()}
ObjectIdsInv
=
{
v
:
k
for
k
,
v
in
ObjectIds
.
__dict__
.
items
()}
AttributeIdsInv
=
{
v
:
k
for
k
,
v
in
AttributeIds
.
__dict__
.
items
()}
AttributeIdsInv
=
{
v
:
k
for
k
,
v
in
AttributeIds
.
__dict__
.
items
()}
opcua/uatypes.py
View file @
37f69181
...
@@ -17,15 +17,18 @@ from opcua.object_ids import ObjectIds
...
@@ -17,15 +17,18 @@ from opcua.object_ids import ObjectIds
logger
=
logging
.
getLogger
(
'opcua.uaprotocol'
)
logger
=
logging
.
getLogger
(
'opcua.uaprotocol'
)
#types that will packed and unpacked directly using struct (string, bytes and datetime are handles as special cases
#
types that will packed and unpacked directly using struct (string, bytes and datetime are handles as special cases
UaTypes
=
(
"Boolean"
,
"SByte"
,
"Byte"
,
"Int8"
,
"UInt8"
,
"Int16"
,
"UInt16"
,
"Int32"
,
"UInt32"
,
"Int64"
,
"UInt64"
,
"Float"
,
"Double"
)
UaTypes
=
(
"Boolean"
,
"SByte"
,
"Byte"
,
"Int8"
,
"UInt8"
,
"Int16"
,
"UInt16"
,
"Int32"
,
"UInt32"
,
"Int64"
,
"UInt64"
,
"Float"
,
"Double"
)
EPOCH_AS_FILETIME
=
116444736000000000
# January 1, 1970 as MS file time
EPOCH_AS_FILETIME
=
116444736000000000
# January 1, 1970 as MS file time
HUNDREDS_OF_NANOSECONDS
=
10000000
HUNDREDS_OF_NANOSECONDS
=
10000000
class
UTC
(
tzinfo
):
class
UTC
(
tzinfo
):
"""UTC"""
"""UTC"""
def
utcoffset
(
self
,
dt
):
def
utcoffset
(
self
,
dt
):
return
timedelta
(
0
)
return
timedelta
(
0
)
...
@@ -36,26 +39,26 @@ class UTC(tzinfo):
...
@@ -36,26 +39,26 @@ class UTC(tzinfo):
return
timedelta
(
0
)
return
timedelta
(
0
)
#methods copied from David Buxton <david@gasmark6.com> sample code
#
methods copied from David Buxton <david@gasmark6.com> sample code
def
datetime_to_win_epoch
(
dt
):
def
datetime_to_win_epoch
(
dt
):
if
(
dt
.
tzinfo
is
None
)
or
(
dt
.
tzinfo
.
utcoffset
(
dt
)
is
None
):
if
(
dt
.
tzinfo
is
None
)
or
(
dt
.
tzinfo
.
utcoffset
(
dt
)
is
None
):
dt
=
dt
.
replace
(
tzinfo
=
UTC
())
dt
=
dt
.
replace
(
tzinfo
=
UTC
())
ft
=
EPOCH_AS_FILETIME
+
(
timegm
(
dt
.
timetuple
())
*
HUNDREDS_OF_NANOSECONDS
)
ft
=
EPOCH_AS_FILETIME
+
(
timegm
(
dt
.
timetuple
())
*
HUNDREDS_OF_NANOSECONDS
)
return
ft
+
(
dt
.
microsecond
*
10
)
return
ft
+
(
dt
.
microsecond
*
10
)
def
win_epoch_to_datetime
(
epch
):
def
win_epoch_to_datetime
(
epch
):
(
s
,
ns100
)
=
divmod
(
epch
-
EPOCH_AS_FILETIME
,
HUNDREDS_OF_NANOSECONDS
)
(
s
,
ns100
)
=
divmod
(
epch
-
EPOCH_AS_FILETIME
,
HUNDREDS_OF_NANOSECONDS
)
try
:
try
:
# TDA, this generates exceptions on systems where RTC is really off
# TDA, this generates exceptions on systems where RTC is really off
dt
=
datetime
.
utcfromtimestamp
(
s
)
dt
=
datetime
.
utcfromtimestamp
(
s
)
except
:
except
:
logger
.
debug
(
"Exception occurred during conversion within 'win_epoch_to_datetime'."
)
logger
.
debug
(
"Exception occurred during conversion within 'win_epoch_to_datetime'."
)
return
datetime
.
now
()
return
datetime
.
now
()
dt
=
dt
.
replace
(
microsecond
=
(
ns100
//
10
))
dt
=
dt
.
replace
(
microsecond
=
(
ns100
//
10
))
return
dt
return
dt
def
uatype_to_fmt
(
uatype
):
def
uatype_to_fmt
(
uatype
):
if
uatype
==
"Char"
:
if
uatype
==
"Char"
:
return
"B"
return
"B"
...
@@ -90,6 +93,7 @@ def uatype_to_fmt(uatype):
...
@@ -90,6 +93,7 @@ def uatype_to_fmt(uatype):
else
:
else
:
raise
Exception
(
"Error unknown uatype: "
+
uatype
)
raise
Exception
(
"Error unknown uatype: "
+
uatype
)
def
pack_uatype_array
(
uatype
,
value
):
def
pack_uatype_array
(
uatype
,
value
):
if
value
is
None
:
if
value
is
None
:
return
struct
.
pack
(
"<i"
,
-
1
)
return
struct
.
pack
(
"<i"
,
-
1
)
...
@@ -99,6 +103,7 @@ def pack_uatype_array(uatype, value):
...
@@ -99,6 +103,7 @@ def pack_uatype_array(uatype, value):
b
.
append
(
pack_uatype
(
uatype
,
val
))
b
.
append
(
pack_uatype
(
uatype
,
val
))
return
b""
.
join
(
b
)
return
b""
.
join
(
b
)
def
pack_uatype
(
uatype
,
value
):
def
pack_uatype
(
uatype
,
value
):
if
uatype
==
"Null"
:
if
uatype
==
"Null"
:
return
b''
return
b''
...
@@ -115,6 +120,7 @@ def pack_uatype(uatype, value):
...
@@ -115,6 +120,7 @@ def pack_uatype(uatype, value):
else
:
else
:
return
value
.
to_binary
()
return
value
.
to_binary
()
def
unpack_uatype
(
uatype
,
data
):
def
unpack_uatype
(
uatype
,
data
):
if
uatype
==
"String"
:
if
uatype
==
"String"
:
return
unpack_string
(
data
)
return
unpack_string
(
data
)
...
@@ -132,6 +138,7 @@ def unpack_uatype(uatype, data):
...
@@ -132,6 +138,7 @@ def unpack_uatype(uatype, data):
tmp
=
eval
(
code
)
tmp
=
eval
(
code
)
return
tmp
return
tmp
def
unpack_uatype_array
(
uatype
,
data
):
def
unpack_uatype_array
(
uatype
,
data
):
length
=
struct
.
unpack
(
'<i'
,
data
.
read
(
4
))[
0
]
length
=
struct
.
unpack
(
'<i'
,
data
.
read
(
4
))[
0
]
if
length
==
-
1
:
if
length
==
-
1
:
...
@@ -142,43 +149,50 @@ def unpack_uatype_array(uatype, data):
...
@@ -142,43 +149,50 @@ def unpack_uatype_array(uatype, data):
result
.
append
(
unpack_uatype
(
uatype
,
data
))
result
.
append
(
unpack_uatype
(
uatype
,
data
))
return
result
return
result
def
pack_string
(
string
):
def
pack_string
(
string
):
length
=
len
(
string
)
length
=
len
(
string
)
if
length
==
0
:
if
length
==
0
:
return
struct
.
pack
(
"<i"
,
-
1
)
return
struct
.
pack
(
"<i"
,
-
1
)
if
not
type
(
string
)
is
bytes
:
if
not
isinstance
(
string
,
bytes
)
:
string
=
string
.
encode
()
string
=
string
.
encode
()
return
struct
.
pack
(
"<i"
,
length
)
+
string
return
struct
.
pack
(
"<i"
,
length
)
+
string
pack_bytes
=
pack_string
pack_bytes
=
pack_string
def
unpack_bytes
(
data
):
def
unpack_bytes
(
data
):
length
=
struct
.
unpack
(
"<i"
,
data
.
read
(
4
))[
0
]
length
=
struct
.
unpack
(
"<i"
,
data
.
read
(
4
))[
0
]
if
length
==
-
1
:
if
length
==
-
1
:
return
b''
return
b''
return
data
.
read
(
length
)
return
data
.
read
(
length
)
def
unpack_string
(
data
):
def
unpack_string
(
data
):
b
=
unpack_bytes
(
data
)
b
=
unpack_bytes
(
data
)
if
sys
.
version_info
.
major
<
3
:
if
sys
.
version_info
.
major
<
3
:
return
str
(
b
)
return
str
(
b
)
return
b
.
decode
(
"utf-8"
)
return
b
.
decode
(
"utf-8"
)
def
test_bit
(
data
,
offset
):
def
test_bit
(
data
,
offset
):
mask
=
1
<<
offset
mask
=
1
<<
offset
return
data
&
mask
return
data
&
mask
def
set_bit
(
data
,
offset
):
def
set_bit
(
data
,
offset
):
mask
=
1
<<
offset
mask
=
1
<<
offset
return
data
|
mask
return
data
|
mask
class
FrozenClass
(
object
):
class
FrozenClass
(
object
):
"""
"""
make it impossible to add members to a class.
make it impossible to add members to a class.
This is a hack since I found out that most bugs are due to misspelling a variable in protocol
This is a hack since I found out that most bugs are due to misspelling a variable in protocol
"""
"""
__isfrozen
=
False
__isfrozen
=
False
def
__setattr__
(
self
,
key
,
value
):
def
__setattr__
(
self
,
key
,
value
):
if
self
.
__isfrozen
and
not
hasattr
(
self
,
key
):
if
self
.
__isfrozen
and
not
hasattr
(
self
,
key
):
raise
TypeError
(
"Error adding member '{}' to class '{}', class is frozen, members are {}"
.
format
(
key
,
self
.
__class__
.
__name__
,
self
.
__dict__
.
keys
()))
raise
TypeError
(
"Error adding member '{}' to class '{}', class is frozen, members are {}"
.
format
(
key
,
self
.
__class__
.
__name__
,
self
.
__dict__
.
keys
()))
...
@@ -189,6 +203,7 @@ class FrozenClass(object):
...
@@ -189,6 +203,7 @@ class FrozenClass(object):
class
Guid
(
object
):
class
Guid
(
object
):
def
__init__
(
self
):
def
__init__
(
self
):
self
.
uuid
=
uuid
.
uuid4
()
self
.
uuid
=
uuid
.
uuid4
()
...
@@ -204,7 +219,9 @@ class Guid(object):
...
@@ -204,7 +219,9 @@ class Guid(object):
def
__eq__
(
self
,
other
):
def
__eq__
(
self
,
other
):
return
isinstance
(
other
,
Guid
)
and
self
.
uuid
==
other
.
uuid
return
isinstance
(
other
,
Guid
)
and
self
.
uuid
==
other
.
uuid
class
StatusCode
(
object
):
class
StatusCode
(
object
):
def
__init__
(
self
,
value
=
0
):
def
__init__
(
self
,
value
=
0
):
self
.
value
=
value
self
.
value
=
value
self
.
name
,
self
.
doc
=
status_code
.
get_name_and_doc
(
value
)
self
.
name
,
self
.
doc
=
status_code
.
get_name_and_doc
(
value
)
...
@@ -226,6 +243,7 @@ class StatusCode(object):
...
@@ -226,6 +243,7 @@ class StatusCode(object):
return
'StatusCode({})'
.
format
(
self
.
name
)
return
'StatusCode({})'
.
format
(
self
.
name
)
__repr__
=
__str__
__repr__
=
__str__
class
NodeIdType
(
object
):
class
NodeIdType
(
object
):
TwoByte
=
0
TwoByte
=
0
FourByte
=
1
FourByte
=
1
...
@@ -236,6 +254,7 @@ class NodeIdType(object):
...
@@ -236,6 +254,7 @@ class NodeIdType(object):
class
NodeId
(
object
):
class
NodeId
(
object
):
def
__init__
(
self
,
identifier
=
None
,
namespaceidx
=
0
,
nodeidtype
=
None
):
def
__init__
(
self
,
identifier
=
None
,
namespaceidx
=
0
,
nodeidtype
=
None
):
self
.
Identifier
=
identifier
self
.
Identifier
=
identifier
self
.
NamespaceIndex
=
namespaceidx
self
.
NamespaceIndex
=
namespaceidx
...
@@ -247,17 +266,17 @@ class NodeId(object):
...
@@ -247,17 +266,17 @@ class NodeId(object):
self
.
NodeIdType
=
NodeIdType
.
TwoByte
self
.
NodeIdType
=
NodeIdType
.
TwoByte
return
return
if
self
.
NodeIdType
is
None
:
if
self
.
NodeIdType
is
None
:
if
type
(
self
.
Identifier
)
==
int
:
if
isinstance
(
self
.
Identifier
,
int
)
:
self
.
NodeIdType
=
NodeIdType
.
Numeric
self
.
NodeIdType
=
NodeIdType
.
Numeric
elif
type
(
self
.
Identifier
)
==
str
:
elif
isinstance
(
self
.
Identifier
,
str
)
:
self
.
NodeIdType
=
NodeIdType
.
String
self
.
NodeIdType
=
NodeIdType
.
String
elif
type
(
self
.
Identifier
)
==
bytes
:
elif
isinstance
(
self
.
Identifier
,
bytes
)
:
self
.
NodeIdType
=
NodeIdType
.
ByteString
self
.
NodeIdType
=
NodeIdType
.
ByteString
else
:
else
:
raise
Exception
(
"NodeId: Could not guess type of NodeId, set NodeIdType"
)
raise
Exception
(
"NodeId: Could not guess type of NodeId, set NodeIdType"
)
def
__key
(
self
):
def
__key
(
self
):
if
self
.
NodeIdType
in
(
NodeIdType
.
TwoByte
,
NodeIdType
.
FourByte
,
NodeIdType
.
Numeric
):
#
twobyte, fourbyte and numeric may represent the same node
if
self
.
NodeIdType
in
(
NodeIdType
.
TwoByte
,
NodeIdType
.
FourByte
,
NodeIdType
.
Numeric
):
#
twobyte, fourbyte and numeric may represent the same node
return
self
.
NamespaceIndex
,
self
.
Identifier
return
self
.
NamespaceIndex
,
self
.
Identifier
else
:
else
:
return
self
.
NodeIdType
,
self
.
NamespaceIndex
,
self
.
Identifier
return
self
.
NodeIdType
,
self
.
NamespaceIndex
,
self
.
Identifier
...
@@ -307,7 +326,6 @@ class NodeId(object):
...
@@ -307,7 +326,6 @@ class NodeId(object):
nodeid
.
ServerIndex
=
srv
nodeid
.
ServerIndex
=
srv
return
nodeid
return
nodeid
def
to_string
(
self
):
def
to_string
(
self
):
string
=
""
string
=
""
if
self
.
NamespaceIndex
!=
0
:
if
self
.
NamespaceIndex
!=
0
:
...
@@ -384,39 +402,52 @@ class NodeId(object):
...
@@ -384,39 +402,52 @@ class NodeId(object):
return
nid
return
nid
class
TwoByteNodeId
(
NodeId
):
class
TwoByteNodeId
(
NodeId
):
def
__init__
(
self
,
identifier
):
def
__init__
(
self
,
identifier
):
NodeId
.
__init__
(
self
,
identifier
,
0
,
NodeIdType
.
TwoByte
)
NodeId
.
__init__
(
self
,
identifier
,
0
,
NodeIdType
.
TwoByte
)
class
FourByteNodeId
(
NodeId
):
class
FourByteNodeId
(
NodeId
):
def
__init__
(
self
,
identifier
,
namespace
=
0
):
def
__init__
(
self
,
identifier
,
namespace
=
0
):
NodeId
.
__init__
(
self
,
identifier
,
namespace
,
NodeIdType
.
FourByte
)
NodeId
.
__init__
(
self
,
identifier
,
namespace
,
NodeIdType
.
FourByte
)
class
NumericNodeId
(
NodeId
):
class
NumericNodeId
(
NodeId
):
def
__init__
(
self
,
identifier
,
namespace
=
0
):
def
__init__
(
self
,
identifier
,
namespace
=
0
):
NodeId
.
__init__
(
self
,
identifier
,
namespace
,
NodeIdType
.
Numeric
)
NodeId
.
__init__
(
self
,
identifier
,
namespace
,
NodeIdType
.
Numeric
)
class
ByteStringNodeId
(
NodeId
):
class
ByteStringNodeId
(
NodeId
):
def
__init__
(
self
,
identifier
,
namespace
=
0
):
def
__init__
(
self
,
identifier
,
namespace
=
0
):
NodeId
.
__init__
(
self
,
identifier
,
namespace
,
NodeIdType
.
ByteString
)
NodeId
.
__init__
(
self
,
identifier
,
namespace
,
NodeIdType
.
ByteString
)
class
GuidNodeId
(
NodeId
):
class
GuidNodeId
(
NodeId
):
def
__init__
(
self
,
identifier
,
namespace
=
0
):
def
__init__
(
self
,
identifier
,
namespace
=
0
):
NodeId
.
__init__
(
self
,
identifier
,
namespace
,
NodeIdType
.
Guid
)
NodeId
.
__init__
(
self
,
identifier
,
namespace
,
NodeIdType
.
Guid
)
class
StringNodeId
(
NodeId
):
class
StringNodeId
(
NodeId
):
def
__init__
(
self
,
identifier
,
namespace
=
0
):
def
__init__
(
self
,
identifier
,
namespace
=
0
):
NodeId
.
__init__
(
self
,
identifier
,
namespace
,
NodeIdType
.
String
)
NodeId
.
__init__
(
self
,
identifier
,
namespace
,
NodeIdType
.
String
)
ExpandedNodeId
=
NodeId
ExpandedNodeId
=
NodeId
class
QualifiedName
(
object
):
class
QualifiedName
(
object
):
'''
'''
A string qualified with a namespace index.
A string qualified with a namespace index.
'''
'''
def
__init__
(
self
,
name
=
""
,
namespaceidx
=
0
):
def
__init__
(
self
,
name
=
""
,
namespaceidx
=
0
):
self
.
NamespaceIndex
=
namespaceidx
self
.
NamespaceIndex
=
namespaceidx
self
.
Name
=
name
self
.
Name
=
name
...
@@ -458,20 +489,25 @@ class QualifiedName(object):
...
@@ -458,20 +489,25 @@ class QualifiedName(object):
class
LocalizedText
(
FrozenClass
):
class
LocalizedText
(
FrozenClass
):
'''
'''
A string qualified with a namespace index.
A string qualified with a namespace index.
'''
'''
def
__init__
(
self
,
text
=
""
):
def
__init__
(
self
,
text
=
""
):
self
.
Encoding
=
0
self
.
Encoding
=
0
self
.
Text
=
text
.
encode
()
self
.
Text
=
text
.
encode
()
if
self
.
Text
:
self
.
Encoding
|=
(
1
<<
1
)
if
self
.
Text
:
self
.
Encoding
|=
(
1
<<
1
)
self
.
Locale
=
b''
self
.
Locale
=
b''
self
.
_freeze
()
self
.
_freeze
()
def
to_binary
(
self
):
def
to_binary
(
self
):
packet
=
[]
packet
=
[]
if
self
.
Locale
:
self
.
Encoding
|=
(
1
<<
0
)
if
self
.
Locale
:
if
self
.
Text
:
self
.
Encoding
|=
(
1
<<
1
)
self
.
Encoding
|=
(
1
<<
0
)
if
self
.
Text
:
self
.
Encoding
|=
(
1
<<
1
)
packet
.
append
(
pack_uatype
(
'UInt8'
,
self
.
Encoding
))
packet
.
append
(
pack_uatype
(
'UInt8'
,
self
.
Encoding
))
if
self
.
Locale
:
if
self
.
Locale
:
packet
.
append
(
pack_uatype
(
'CharArray'
,
self
.
Locale
))
packet
.
append
(
pack_uatype
(
'CharArray'
,
self
.
Locale
))
...
@@ -502,8 +538,10 @@ class LocalizedText(FrozenClass):
...
@@ -502,8 +538,10 @@ class LocalizedText(FrozenClass):
class
ExtensionObject
(
FrozenClass
):
class
ExtensionObject
(
FrozenClass
):
'''
'''
'''
'''
def
__init__
(
self
):
def
__init__
(
self
):
self
.
TypeId
=
NodeId
()
self
.
TypeId
=
NodeId
()
self
.
Encoding
=
0
self
.
Encoding
=
0
...
@@ -512,7 +550,8 @@ class ExtensionObject(FrozenClass):
...
@@ -512,7 +550,8 @@ class ExtensionObject(FrozenClass):
def
to_binary
(
self
):
def
to_binary
(
self
):
packet
=
[]
packet
=
[]
if
self
.
Body
:
self
.
Encoding
|=
(
1
<<
0
)
if
self
.
Body
:
self
.
Encoding
|=
(
1
<<
0
)
packet
.
append
(
self
.
TypeId
.
to_binary
())
packet
.
append
(
self
.
TypeId
.
to_binary
())
packet
.
append
(
pack_uatype
(
'UInt8'
,
self
.
Encoding
))
packet
.
append
(
pack_uatype
(
'UInt8'
,
self
.
Encoding
))
if
self
.
Body
:
if
self
.
Body
:
...
@@ -545,6 +584,7 @@ class ExtensionObject(FrozenClass):
...
@@ -545,6 +584,7 @@ class ExtensionObject(FrozenClass):
class
VariantType
(
Enum
):
class
VariantType
(
Enum
):
'''
'''
The possible types of a variant.
The possible types of a variant.
'''
'''
...
@@ -575,13 +615,16 @@ class VariantType(Enum):
...
@@ -575,13 +615,16 @@ class VariantType(Enum):
Variant
=
24
Variant
=
24
DiagnosticInfo
=
25
DiagnosticInfo
=
25
class
Variant
(
object
):
class
Variant
(
object
):
"""
"""
Create an OPC-UA Variant object.
Create an OPC-UA Variant object.
if no argument a Null Variant is created.
if no argument a Null Variant is created.
if not variant type is given, attemps to guess type from python type
if not variant type is given, attemps to guess type from python type
if a variant is given as value, the new objects becomes a copy of the argument
if a variant is given as value, the new objects becomes a copy of the argument
"""
"""
def
__init__
(
self
,
value
=
None
,
varianttype
=
None
):
def
__init__
(
self
,
value
=
None
,
varianttype
=
None
):
self
.
Encoding
=
0
self
.
Encoding
=
0
self
.
Value
=
value
self
.
Value
=
value
...
@@ -605,17 +648,17 @@ class Variant(object):
...
@@ -605,17 +648,17 @@ class Variant(object):
def
_guess_type
(
self
,
val
):
def
_guess_type
(
self
,
val
):
if
val
is
None
:
if
val
is
None
:
return
VariantType
.
Null
return
VariantType
.
Null
elif
type
(
val
)
==
float
:
elif
isinstance
(
val
,
float
)
:
return
VariantType
.
Double
return
VariantType
.
Double
elif
type
(
val
)
==
int
:
elif
isinstance
(
val
,
int
)
:
return
VariantType
.
Int64
return
VariantType
.
Int64
elif
type
(
val
)
in
(
str
,
unicode
):
elif
type
(
val
)
in
(
str
,
unicode
):
return
VariantType
.
String
return
VariantType
.
String
elif
type
(
val
)
==
bytes
:
elif
isinstance
(
val
,
bytes
)
:
return
VariantType
.
ByteString
return
VariantType
.
ByteString
elif
type
(
val
)
==
datetime
:
elif
isinstance
(
val
,
datetime
)
:
return
VariantType
.
DateTime
return
VariantType
.
DateTime
elif
type
(
val
)
==
bool
:
elif
isinstance
(
val
,
bool
)
:
# TDA, added this because it was missing and causes exceptions when 'bool' type is used
# TDA, added this because it was missing and causes exceptions when 'bool' type is used
return
VariantType
.
Boolean
return
VariantType
.
Boolean
else
:
else
:
...
@@ -656,38 +699,46 @@ class Variant(object):
...
@@ -656,38 +699,46 @@ class Variant(object):
class
DataValue
(
object
):
class
DataValue
(
object
):
'''
'''
A value with an associated timestamp, and quality.
A value with an associated timestamp, and quality.
Automatically generated from xml , copied and modified here to fix errors in xml spec
Automatically generated from xml , copied and modified here to fix errors in xml spec
'''
'''
def
__init__
(
self
,
variant
=
None
):
def
__init__
(
self
,
variant
=
None
):
self
.
Encoding
=
0
self
.
Encoding
=
0
if
not
type
(
variant
)
is
Variant
:
if
not
isinstance
(
variant
,
Variant
)
:
variant
=
Variant
(
variant
)
variant
=
Variant
(
variant
)
self
.
Value
=
variant
self
.
Value
=
variant
self
.
StatusCode
=
StatusCode
()
self
.
StatusCode
=
StatusCode
()
self
.
SourceTimestamp
=
datetime
.
now
()
#
DateTime()
self
.
SourceTimestamp
=
datetime
.
now
()
#
DateTime()
self
.
SourcePicoseconds
=
0
self
.
SourcePicoseconds
=
0
self
.
ServerTimestamp
=
datetime
.
now
()
#
DateTime()
self
.
ServerTimestamp
=
datetime
.
now
()
#
DateTime()
self
.
ServerPicoseconds
=
0
self
.
ServerPicoseconds
=
0
def
to_binary
(
self
):
def
to_binary
(
self
):
packet
=
[]
packet
=
[]
if
self
.
Value
:
self
.
Encoding
|=
(
1
<<
0
)
if
self
.
Value
:
if
self
.
StatusCode
:
self
.
Encoding
|=
(
1
<<
1
)
self
.
Encoding
|=
(
1
<<
0
)
if
self
.
SourceTimestamp
:
self
.
Encoding
|=
(
1
<<
2
)
if
self
.
StatusCode
:
if
self
.
ServerTimestamp
:
self
.
Encoding
|=
(
1
<<
3
)
self
.
Encoding
|=
(
1
<<
1
)
if
self
.
SourcePicoseconds
:
self
.
Encoding
|=
(
1
<<
4
)
if
self
.
SourceTimestamp
:
if
self
.
ServerPicoseconds
:
self
.
Encoding
|=
(
1
<<
5
)
self
.
Encoding
|=
(
1
<<
2
)
if
self
.
ServerTimestamp
:
self
.
Encoding
|=
(
1
<<
3
)
if
self
.
SourcePicoseconds
:
self
.
Encoding
|=
(
1
<<
4
)
if
self
.
ServerPicoseconds
:
self
.
Encoding
|=
(
1
<<
5
)
packet
.
append
(
pack_uatype
(
'UInt8'
,
self
.
Encoding
))
packet
.
append
(
pack_uatype
(
'UInt8'
,
self
.
Encoding
))
if
self
.
Value
:
if
self
.
Value
:
packet
.
append
(
self
.
Value
.
to_binary
())
packet
.
append
(
self
.
Value
.
to_binary
())
if
self
.
StatusCode
:
if
self
.
StatusCode
:
packet
.
append
(
self
.
StatusCode
.
to_binary
())
packet
.
append
(
self
.
StatusCode
.
to_binary
())
if
self
.
SourceTimestamp
:
if
self
.
SourceTimestamp
:
packet
.
append
(
pack_uatype
(
'DateTime'
,
self
.
SourceTimestamp
))
#
self.SourceTimestamp.to_binary())
packet
.
append
(
pack_uatype
(
'DateTime'
,
self
.
SourceTimestamp
))
#
self.SourceTimestamp.to_binary())
if
self
.
ServerTimestamp
:
if
self
.
ServerTimestamp
:
packet
.
append
(
pack_uatype
(
'DateTime'
,
self
.
ServerTimestamp
))
#
self.ServerTimestamp.to_binary())
packet
.
append
(
pack_uatype
(
'DateTime'
,
self
.
ServerTimestamp
))
#
self.ServerTimestamp.to_binary())
if
self
.
SourcePicoseconds
:
if
self
.
SourcePicoseconds
:
packet
.
append
(
pack_uatype
(
'UInt16'
,
self
.
SourcePicoseconds
))
packet
.
append
(
pack_uatype
(
'UInt16'
,
self
.
SourcePicoseconds
))
if
self
.
ServerPicoseconds
:
if
self
.
ServerPicoseconds
:
...
@@ -703,9 +754,9 @@ class DataValue(object):
...
@@ -703,9 +754,9 @@ class DataValue(object):
if
obj
.
Encoding
&
(
1
<<
1
):
if
obj
.
Encoding
&
(
1
<<
1
):
obj
.
StatusCode
=
StatusCode
.
from_binary
(
data
)
obj
.
StatusCode
=
StatusCode
.
from_binary
(
data
)
if
obj
.
Encoding
&
(
1
<<
2
):
if
obj
.
Encoding
&
(
1
<<
2
):
obj
.
SourceTimestamp
=
unpack_uatype
(
'DateTime'
,
data
)
#
DateTime.from_binary(data)
obj
.
SourceTimestamp
=
unpack_uatype
(
'DateTime'
,
data
)
#
DateTime.from_binary(data)
if
obj
.
Encoding
&
(
1
<<
3
):
if
obj
.
Encoding
&
(
1
<<
3
):
obj
.
ServerTimestamp
=
unpack_uatype
(
'DateTime'
,
data
)
#
DateTime.from_binary(data)
obj
.
ServerTimestamp
=
unpack_uatype
(
'DateTime'
,
data
)
#
DateTime.from_binary(data)
if
obj
.
Encoding
&
(
1
<<
4
):
if
obj
.
Encoding
&
(
1
<<
4
):
obj
.
SourcePicoseconds
=
unpack_uatype
(
'UInt16'
,
data
)
obj
.
SourcePicoseconds
=
unpack_uatype
(
'UInt16'
,
data
)
if
obj
.
Encoding
&
(
1
<<
5
):
if
obj
.
Encoding
&
(
1
<<
5
):
...
@@ -722,8 +773,3 @@ class DataValue(object):
...
@@ -722,8 +773,3 @@ class DataValue(object):
'ServerPicoseconds:'
+
str
(
self
.
ServerPicoseconds
)
+
')'
'ServerPicoseconds:'
+
str
(
self
.
ServerPicoseconds
)
+
')'
__repr__
=
__str__
__repr__
=
__str__
opcua/utils.py
View file @
37f69181
...
@@ -3,10 +3,12 @@ import uuid
...
@@ -3,10 +3,12 @@ import uuid
class
Buffer
(
object
):
class
Buffer
(
object
):
"""
"""
alternative to io.BytesIO making debug easier
alternative to io.BytesIO making debug easier
and added a few conveniance methods
and added a few conveniance methods
"""
"""
def
__init__
(
self
,
data
):
def
__init__
(
self
,
data
):
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
data
=
data
self
.
data
=
data
...
@@ -45,8 +47,6 @@ class Buffer(object):
...
@@ -45,8 +47,6 @@ class Buffer(object):
return
self
.
data
[:
size
]
return
self
.
data
[:
size
]
def
recv_all
(
socket
,
size
):
def
recv_all
(
socket
,
size
):
"""
"""
Receive up to size bytes from socket
Receive up to size bytes from socket
...
@@ -60,6 +60,6 @@ def recv_all(socket, size):
...
@@ -60,6 +60,6 @@ def recv_all(socket, size):
size
-=
len
(
chunk
)
size
-=
len
(
chunk
)
return
data
return
data
def
create_nonce
():
return
uuid
.
uuid4
().
bytes
+
uuid
.
uuid4
().
bytes
#seems we need at least 32 bytes not 16 as python gives us...
def
create_nonce
():
return
uuid
.
uuid4
().
bytes
+
uuid
.
uuid4
().
bytes
# seems we need at least 32 bytes not 16 as python gives us...
tests.py
View file @
37f69181
...
@@ -24,20 +24,26 @@ from opcua import AttributeIds
...
@@ -24,20 +24,26 @@ from opcua import AttributeIds
port_num1
=
48510
port_num1
=
48510
port_num2
=
48530
port_num2
=
48530
class
SubHandler
():
class
SubHandler
():
'''
'''
Dummy subscription client
Dummy subscription client
'''
'''
def
data_change
(
self
,
handle
,
node
,
val
,
attr
):
def
data_change
(
self
,
handle
,
node
,
val
,
attr
):
pass
pass
def
event
(
self
,
handle
,
event
):
def
event
(
self
,
handle
,
event
):
pass
pass
class
MySubHandler
():
class
MySubHandler
():
'''
'''
More advanced subscription client using Future, so we can wait for events in tests
More advanced subscription client using Future, so we can wait for events in tests
'''
'''
def
__init__
(
self
):
def
__init__
(
self
):
self
.
future
=
Future
()
self
.
future
=
Future
()
...
@@ -52,6 +58,7 @@ class MySubHandler():
...
@@ -52,6 +58,7 @@ class MySubHandler():
class
Unit
(
unittest
.
TestCase
):
class
Unit
(
unittest
.
TestCase
):
'''
'''
Simple unit test that do not need to setup a server or a client
Simple unit test that do not need to setup a server or a client
'''
'''
...
@@ -76,7 +83,7 @@ class Unit(unittest.TestCase):
...
@@ -76,7 +83,7 @@ class Unit(unittest.TestCase):
fb
=
ua
.
FourByteNodeId
(
53
)
fb
=
ua
.
FourByteNodeId
(
53
)
n
=
ua
.
NumericNodeId
(
53
)
n
=
ua
.
NumericNodeId
(
53
)
n1
=
ua
.
NumericNodeId
(
53
,
0
)
n1
=
ua
.
NumericNodeId
(
53
,
0
)
s
=
ua
.
StringNodeId
(
53
,
0
)
#
should we raise an exception???
s
=
ua
.
StringNodeId
(
53
,
0
)
#
should we raise an exception???
s1
=
ua
.
StringNodeId
(
"53"
,
0
)
s1
=
ua
.
StringNodeId
(
"53"
,
0
)
bs
=
ua
.
ByteStringNodeId
(
b"53"
,
0
)
bs
=
ua
.
ByteStringNodeId
(
b"53"
,
0
)
gid
=
ua
.
Guid
()
gid
=
ua
.
Guid
()
...
@@ -126,11 +133,11 @@ class Unit(unittest.TestCase):
...
@@ -126,11 +133,11 @@ class Unit(unittest.TestCase):
def
test_equal_nodeid
(
self
):
def
test_equal_nodeid
(
self
):
nid1
=
ua
.
NodeId
(
999
,
2
)
nid1
=
ua
.
NodeId
(
999
,
2
)
nid2
=
ua
.
NodeId
(
999
,
2
)
nid2
=
ua
.
NodeId
(
999
,
2
)
self
.
assertTrue
(
nid1
==
nid2
)
self
.
assertTrue
(
nid1
==
nid2
)
self
.
assertTrue
(
id
(
nid1
)
!=
id
(
nid2
))
self
.
assertTrue
(
id
(
nid1
)
!=
id
(
nid2
))
def
test_zero_nodeid
(
self
):
def
test_zero_nodeid
(
self
):
self
.
assertEqual
(
ua
.
NodeId
(),
ua
.
NodeId
(
0
,
0
))
self
.
assertEqual
(
ua
.
NodeId
(),
ua
.
NodeId
(
0
,
0
))
self
.
assertEqual
(
ua
.
NodeId
(),
ua
.
NodeId
.
from_string
(
'ns=0;i=0;'
))
self
.
assertEqual
(
ua
.
NodeId
(),
ua
.
NodeId
.
from_string
(
'ns=0;i=0;'
))
def
test_string_nodeid
(
self
):
def
test_string_nodeid
(
self
):
...
@@ -150,7 +157,6 @@ class Unit(unittest.TestCase):
...
@@ -150,7 +157,6 @@ class Unit(unittest.TestCase):
self
.
assertEqual
(
nid
.
NamespaceIndex
,
2
)
self
.
assertEqual
(
nid
.
NamespaceIndex
,
2
)
self
.
assertEqual
(
nid
.
Identifier
,
'PLC1.Manufacturer'
)
self
.
assertEqual
(
nid
.
Identifier
,
'PLC1.Manufacturer'
)
def
test_strrepr_nodeid
(
self
):
def
test_strrepr_nodeid
(
self
):
nid
=
ua
.
NodeId
.
from_string
(
'ns=2;s=PLC1.Manufacturer;'
)
nid
=
ua
.
NodeId
.
from_string
(
'ns=2;s=PLC1.Manufacturer;'
)
self
.
assertEqual
(
nid
.
to_string
(),
'ns=2;s=PLC1.Manufacturer'
)
self
.
assertEqual
(
nid
.
to_string
(),
'ns=2;s=PLC1.Manufacturer'
)
...
@@ -182,13 +188,13 @@ class Unit(unittest.TestCase):
...
@@ -182,13 +188,13 @@ class Unit(unittest.TestCase):
v2
=
ua
.
Variant
.
from_binary
(
ua
.
utils
.
Buffer
(
v
.
to_binary
()))
v2
=
ua
.
Variant
.
from_binary
(
ua
.
utils
.
Buffer
(
v
.
to_binary
()))
self
.
assertEqual
(
v
.
Value
,
v2
.
Value
)
self
.
assertEqual
(
v
.
Value
,
v2
.
Value
)
self
.
assertEqual
(
v
.
VariantType
,
v2
.
VariantType
)
self
.
assertEqual
(
v
.
VariantType
,
v2
.
VariantType
)
#commonity method:
#
commonity method:
self
.
assertEqual
(
v
,
ua
.
Variant
(
v
))
self
.
assertEqual
(
v
,
ua
.
Variant
(
v
))
def
test_variant_array
(
self
):
def
test_variant_array
(
self
):
v
=
ua
.
Variant
([
1
,
2
,
3
,
4
,
5
])
v
=
ua
.
Variant
([
1
,
2
,
3
,
4
,
5
])
self
.
assertEqual
(
v
.
Value
[
1
],
2
)
self
.
assertEqual
(
v
.
Value
[
1
],
2
)
#self.assertEqual(v.VarianType, ua.VariantType.Int64) # we do not care, we should aonly test for sutff that matter
#
self.assertEqual(v.VarianType, ua.VariantType.Int64) # we do not care, we should aonly test for sutff that matter
v2
=
ua
.
Variant
.
from_binary
(
ua
.
utils
.
Buffer
(
v
.
to_binary
()))
v2
=
ua
.
Variant
.
from_binary
(
ua
.
utils
.
Buffer
(
v
.
to_binary
()))
self
.
assertEqual
(
v
.
Value
,
v2
.
Value
)
self
.
assertEqual
(
v
.
Value
,
v2
.
Value
)
self
.
assertEqual
(
v
.
VariantType
,
v2
.
VariantType
)
self
.
assertEqual
(
v
.
VariantType
,
v2
.
VariantType
)
...
@@ -210,7 +216,9 @@ class Unit(unittest.TestCase):
...
@@ -210,7 +216,9 @@ class Unit(unittest.TestCase):
t4
=
ua
.
LocalizedText
.
from_binary
(
ua
.
utils
.
Buffer
(
t1
.
to_binary
()))
t4
=
ua
.
LocalizedText
.
from_binary
(
ua
.
utils
.
Buffer
(
t1
.
to_binary
()))
self
.
assertEqual
(
t1
,
t4
)
self
.
assertEqual
(
t1
,
t4
)
class
CommonTests
(
object
):
class
CommonTests
(
object
):
'''
'''
Tests that will be run twice. Once on server side and once on
Tests that will be run twice. Once on server side and once on
client side since we have been carefull to have the exact
client side since we have been carefull to have the exact
...
@@ -290,21 +298,20 @@ class CommonTests(object):
...
@@ -290,21 +298,20 @@ class CommonTests(object):
ev
.
trigger
()
ev
.
trigger
()
clthandle
,
ev
=
msclt
.
future
.
result
()
clthandle
,
ev
=
msclt
.
future
.
result
()
#with cond:
#
with cond:
#ret = cond.wait(50000)
#ret = cond.wait(50000)
#if sys.version_info.major>2: self.assertEqual(ret, True) # we went into timeout waiting for subcsription callback
#
if sys.version_info.major>2: self.assertEqual(ret, True) # we went into timeout waiting for subcsription callback
#else: pass # python2
#
else: pass # python2
self
.
assertIsNot
(
ev
,
None
)
# we did not receive event
self
.
assertIsNot
(
ev
,
None
)
# we did not receive event
self
.
assertEqual
(
ev
.
Message
.
Text
,
msg
)
self
.
assertEqual
(
ev
.
Message
.
Text
,
msg
)
#self.assertEqual(msclt.ev.Time, tid)
#self.assertEqual(msclt.ev.Time, tid)
self
.
assertEqual
(
ev
.
Severity
,
500
)
self
.
assertEqual
(
ev
.
Severity
,
500
)
self
.
assertEqual
(
ev
.
SourceNode
,
self
.
opc
.
get_server_node
().
nodeid
)
self
.
assertEqual
(
ev
.
SourceNode
,
self
.
opc
.
get_server_node
().
nodeid
)
#time.sleep(0.1)
#
time.sleep(0.1)
sub
.
unsubscribe
(
handle
)
sub
.
unsubscribe
(
handle
)
sub
.
delete
()
sub
.
delete
()
def
test_non_existing_path
(
self
):
def
test_non_existing_path
(
self
):
root
=
self
.
opc
.
get_root_node
()
root
=
self
.
opc
.
get_root_node
()
with
self
.
assertRaises
(
Exception
):
with
self
.
assertRaises
(
Exception
):
...
@@ -430,10 +437,9 @@ class CommonTests(object):
...
@@ -430,10 +437,9 @@ class CommonTests(object):
with
self
.
assertRaises
(
Exception
):
with
self
.
assertRaises
(
Exception
):
bad
.
set_value
(
89
)
bad
.
set_value
(
89
)
with
self
.
assertRaises
(
Exception
):
with
self
.
assertRaises
(
Exception
):
bad
.
add_object
(
0
,
"myobj"
)
bad
.
add_object
(
0
,
"myobj"
)
with
self
.
assertRaises
(
Exception
):
with
self
.
assertRaises
(
Exception
):
bad
.
get_child
(
0
,
"myobj"
)
bad
.
get_child
(
0
,
"myobj"
)
def
test_array_value
(
self
):
def
test_array_value
(
self
):
o
=
self
.
opc
.
get_objects_node
()
o
=
self
.
opc
.
get_objects_node
()
...
@@ -468,27 +474,27 @@ class CommonTests(object):
...
@@ -468,27 +474,27 @@ class CommonTests(object):
# Now check we get the start value
# Now check we get the start value
clthandle
,
node
,
val
,
attr
=
msclt
.
future
.
result
()
clthandle
,
node
,
val
,
attr
=
msclt
.
future
.
result
()
#with cond:
#
with cond:
#ret = cond.wait(0.5)
#ret = cond.wait(0.5)
#if sys.version_info.major>2: self.assertEqual(ret, True) # we went into timeout waiting for subcsription callback
#
if sys.version_info.major>2: self.assertEqual(ret, True) # we went into timeout waiting for subcsription callback
#else: pass # XXX
#
else: pass # XXX
self
.
assertEqual
(
val
,
startv1
)
self
.
assertEqual
(
val
,
startv1
)
self
.
assertEqual
(
node
,
v1
)
self
.
assertEqual
(
node
,
v1
)
msclt
.
reset
()
#
reset future object
msclt
.
reset
()
#
reset future object
# modify v1 and check we get value
# modify v1 and check we get value
v1
.
set_value
([
5
])
v1
.
set_value
([
5
])
clthandle
,
node
,
val
,
attr
=
msclt
.
future
.
result
()
clthandle
,
node
,
val
,
attr
=
msclt
.
future
.
result
()
#with cond:
#
with cond:
#ret = cond.wait(0.5)
#ret = cond.wait(0.5)
#if sys.version_info.major>2: self.assertEqual(ret, True) # we went into timeout waiting for subcsription callback
#
if sys.version_info.major>2: self.assertEqual(ret, True) # we went into timeout waiting for subcsription callback
#else: pass # XXX
#
else: pass # XXX
self
.
assertEqual
(
node
,
v1
)
self
.
assertEqual
(
node
,
v1
)
self
.
assertEqual
(
val
,
[
5
])
self
.
assertEqual
(
val
,
[
5
])
with
self
.
assertRaises
(
Exception
):
with
self
.
assertRaises
(
Exception
):
sub
.
unsubscribe
(
999
)
# non existing handle
sub
.
unsubscribe
(
999
)
# non existing handle
sub
.
unsubscribe
(
handle1
)
sub
.
unsubscribe
(
handle1
)
with
self
.
assertRaises
(
Exception
):
with
self
.
assertRaises
(
Exception
):
sub
.
unsubscribe
(
handle1
)
# second try should fail
sub
.
unsubscribe
(
handle1
)
# second try should fail
...
@@ -526,10 +532,10 @@ class CommonTests(object):
...
@@ -526,10 +532,10 @@ class CommonTests(object):
result
=
o
.
call_method
(
"2:ServerMethod"
,
2.1
)
result
=
o
.
call_method
(
"2:ServerMethod"
,
2.1
)
self
.
assertEqual
(
result
,
4.2
)
self
.
assertEqual
(
result
,
4.2
)
with
self
.
assertRaises
(
Exception
):
with
self
.
assertRaises
(
Exception
):
#FIXME: we should raise a more precise exception
#
FIXME: we should raise a more precise exception
result
=
o
.
call_method
(
"2:ServerMethod"
,
2.1
,
89
,
9
)
result
=
o
.
call_method
(
"2:ServerMethod"
,
2.1
,
89
,
9
)
with
self
.
assertRaises
(
Exception
):
with
self
.
assertRaises
(
Exception
):
result
=
o
.
call_method
(
ua
.
NodeId
(
999
),
2.1
)
#
non existing method
result
=
o
.
call_method
(
ua
.
NodeId
(
999
),
2.1
)
#
non existing method
def
test_method_array
(
self
):
def
test_method_array
(
self
):
o
=
self
.
opc
.
get_objects_node
()
o
=
self
.
opc
.
get_objects_node
()
...
@@ -558,9 +564,6 @@ class CommonTests(object):
...
@@ -558,9 +564,6 @@ class CommonTests(object):
self
.
assertTrue
(
endpoints
[
0
].
EndpointUrl
.
startswith
(
"opc.tcp://localhost"
))
self
.
assertTrue
(
endpoints
[
0
].
EndpointUrl
.
startswith
(
"opc.tcp://localhost"
))
def
add_server_methods
(
srv
):
def
add_server_methods
(
srv
):
@
uamethod
@
uamethod
def
func
(
parent
,
value
):
def
func
(
parent
,
value
):
...
@@ -569,7 +572,6 @@ def add_server_methods(srv):
...
@@ -569,7 +572,6 @@ def add_server_methods(srv):
o
=
srv
.
get_objects_node
()
o
=
srv
.
get_objects_node
()
v
=
o
.
add_method
(
ua
.
NodeId
(
"ServerMethod"
,
2
),
ua
.
QualifiedName
(
'ServerMethod'
,
2
),
func
,
[
ua
.
VariantType
.
Int64
],
[
ua
.
VariantType
.
Int64
])
v
=
o
.
add_method
(
ua
.
NodeId
(
"ServerMethod"
,
2
),
ua
.
QualifiedName
(
'ServerMethod'
,
2
),
func
,
[
ua
.
VariantType
.
Int64
],
[
ua
.
VariantType
.
Int64
])
@
uamethod
@
uamethod
def
func2
(
parent
,
methodname
,
value
):
def
func2
(
parent
,
methodname
,
value
):
return
math
.
sin
(
value
)
return
math
.
sin
(
value
)
...
@@ -585,8 +587,8 @@ def add_server_methods(srv):
...
@@ -585,8 +587,8 @@ def add_server_methods(srv):
v
=
o
.
add_method
(
ua
.
NodeId
(
"ServerMethodArray2"
,
2
),
ua
.
QualifiedName
(
'ServerMethodArray2'
,
2
),
func3
,
[
ua
.
VariantType
.
Int64
],
[
ua
.
VariantType
.
Int64
])
v
=
o
.
add_method
(
ua
.
NodeId
(
"ServerMethodArray2"
,
2
),
ua
.
QualifiedName
(
'ServerMethodArray2'
,
2
),
func3
,
[
ua
.
VariantType
.
Int64
],
[
ua
.
VariantType
.
Int64
])
class
TestClient
(
unittest
.
TestCase
,
CommonTests
):
class
TestClient
(
unittest
.
TestCase
,
CommonTests
):
'''
'''
Run common tests on client side
Run common tests on client side
Of course we need a server so we start a server in another
Of course we need a server so we start a server in another
...
@@ -619,8 +621,8 @@ class TestClient(unittest.TestCase, CommonTests):
...
@@ -619,8 +621,8 @@ class TestClient(unittest.TestCase, CommonTests):
self
.
clt
.
bclient
.
_send_request
(
request
)
self
.
clt
.
bclient
.
_send_request
(
request
)
class
TestServer
(
unittest
.
TestCase
,
CommonTests
):
class
TestServer
(
unittest
.
TestCase
,
CommonTests
):
'''
'''
Run common tests on server side
Run common tests on server side
Tests that can only be run on server side must be defined here
Tests that can only be run on server side must be defined here
...
@@ -637,7 +639,6 @@ class TestServer(unittest.TestCase, CommonTests):
...
@@ -637,7 +639,6 @@ class TestServer(unittest.TestCase, CommonTests):
def
tearDownClass
(
self
):
def
tearDownClass
(
self
):
self
.
srv
.
stop
()
self
.
srv
.
stop
()
def
test_register_namespace
(
self
):
def
test_register_namespace
(
self
):
uri
=
'http://mycustom.Namespace.com'
uri
=
'http://mycustom.Namespace.com'
idx1
=
self
.
opc
.
register_namespace
(
uri
)
idx1
=
self
.
opc
.
register_namespace
(
uri
)
...
@@ -662,10 +663,8 @@ class TestServer(unittest.TestCase, CommonTests):
...
@@ -662,10 +663,8 @@ class TestServer(unittest.TestCase, CommonTests):
self
.
assertEqual
(
result
,
4.2
)
self
.
assertEqual
(
result
,
4.2
)
if
__name__
==
'__main__'
:
if
__name__
==
'__main__'
:
logging
.
basicConfig
(
level
=
logging
.
WARN
)
logging
.
basicConfig
(
level
=
logging
.
WARN
)
sclt
=
SubHandler
()
sclt
=
SubHandler
()
unittest
.
main
(
verbosity
=
3
)
unittest
.
main
(
verbosity
=
3
)
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