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
Hide 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,8 +17,7 @@ class SubHandler(object):
...
@@ -14,8 +17,7 @@ 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
)
client
=
Client
(
"opc.tcp://localhost:53530/OPCUA/SimulationServer/"
)
client
=
Client
(
"opc.tcp://localhost:53530/OPCUA/SimulationServer/"
)
...
@@ -33,7 +35,7 @@ if __name__ == "__main__":
...
@@ -33,7 +35,7 @@ if __name__ == "__main__":
myuint64
=
client
.
get_node
(
"ns=4;s=UInt64"
)
myuint64
=
client
.
get_node
(
"ns=4;s=UInt64"
)
myint32
=
client
.
get_node
(
"ns=4;s=Int32"
)
myint32
=
client
.
get_node
(
"ns=4;s=Int32"
)
myuint32
=
client
.
get_node
(
"ns=4;s=UInt32"
)
myuint32
=
client
.
get_node
(
"ns=4;s=UInt32"
)
var
=
client
.
get_node
(
ua
.
NodeId
(
"Random1"
,
5
))
var
=
client
.
get_node
(
ua
.
NodeId
(
"Random1"
,
5
))
print
(
"var is: "
,
var
)
print
(
"var is: "
,
var
)
print
(
"value of var is: "
,
var
.
get_value
())
print
(
"value of var is: "
,
var
.
get_value
())
...
@@ -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,38 +17,43 @@ from opcua import ua, uamethod, Server, Event, ObjectIds
...
@@ -16,38 +17,43 @@ 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
()
server
.
set_endpoint
(
"opc.tcp://localhost:4841/freeopcua/server/"
)
server
.
set_endpoint
(
"opc.tcp://localhost:4841/freeopcua/server/"
)
server
.
set_server_name
(
"FreeOpcUa Example Server"
)
server
.
set_server_name
(
"FreeOpcUa Example Server"
)
...
@@ -73,13 +79,13 @@ if __name__ == "__main__":
...
@@ -73,13 +79,13 @@ if __name__ == "__main__":
myevent
=
server
.
get_event_object
(
ObjectIds
.
BaseEventType
)
myevent
=
server
.
get_event_object
(
ObjectIds
.
BaseEventType
)
myevent
.
Message
.
Text
=
"This is my event"
myevent
.
Message
.
Text
=
"This is my event"
myevent
.
Severity
=
300
myevent
.
Severity
=
300
# starting!
# starting!
server
.
start
()
server
.
start
()
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
...
@@ -15,7 +15,7 @@ from opcua.server import Server
...
@@ -15,7 +15,7 @@ from opcua.server import Server
def
uamethod
(
func
):
def
uamethod
(
func
):
"""
"""
Method decorator to automatically convert
Method decorator to automatically convert
arguments and output to and from variants
arguments and output to and from variants
"""
"""
def
wrapper
(
parent
,
*
args
):
def
wrapper
(
parent
,
*
args
):
...
@@ -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,17 +4,21 @@ import pickle
...
@@ -4,17 +4,21 @@ 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
self
.
datachange_callbacks
=
{}
self
.
datachange_callbacks
=
{}
def
__str__
(
self
):
def
__str__
(
self
):
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
...
@@ -132,7 +139,7 @@ class ViewService(object):
...
@@ -132,7 +139,7 @@ class ViewService(object):
current
=
nodeid
current
=
nodeid
target
=
ua
.
BrowsePathTarget
()
target
=
ua
.
BrowsePathTarget
()
target
.
TargetId
=
current
target
.
TargetId
=
current
target
.
RemainingPathIndex
=
4294967295
target
.
RemainingPathIndex
=
4294967295
res
.
Targets
=
[
target
]
res
.
Targets
=
[
target
]
return
res
return
res
...
@@ -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,19 +175,19 @@ class NodeManagementService(object):
...
@@ -167,19 +175,19 @@ 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
elif
not
item
.
ParentNodeId
in
self
.
_aspace
.
nodes
:
elif
not
item
.
ParentNodeId
in
self
.
_aspace
.
nodes
:
#self.logger.warning("add_node: while adding node %s, requested parent node %s does not exists", item.RequestedNewNodeId, item.ParentNodeId)
#self.logger.warning("add_node: while adding node %s, requested parent node %s does not exists", item.RequestedNewNodeId, item.ParentNodeId)
result
.
StatusCode
=
ua
.
StatusCode
(
ua
.
StatusCodes
.
BadParentNodeIdInvalid
)
result
.
StatusCode
=
ua
.
StatusCode
(
ua
.
StatusCodes
.
BadParentNodeIdInvalid
)
return
result
return
result
else
:
else
:
...
@@ -192,11 +200,11 @@ class NodeManagementService(object):
...
@@ -192,11 +200,11 @@ class NodeManagementService(object):
desc
.
TypeDefinition
=
item
.
TypeDefinition
desc
.
TypeDefinition
=
item
.
TypeDefinition
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
...
@@ -230,10 +238,10 @@ class NodeManagementService(object):
...
@@ -230,10 +238,10 @@ class NodeManagementService(object):
rdesc
.
NodeClass
=
addref
.
TargetNodeClass
rdesc
.
NodeClass
=
addref
.
TargetNodeClass
bname
=
self
.
_aspace
.
get_attribute_value
(
addref
.
TargetNodeId
,
ua
.
AttributeIds
.
BrowseName
).
Value
.
Value
bname
=
self
.
_aspace
.
get_attribute_value
(
addref
.
TargetNodeId
,
ua
.
AttributeIds
.
BrowseName
).
Value
.
Value
if
bname
:
if
bname
:
rdesc
.
BrowseName
=
bname
rdesc
.
BrowseName
=
bname
dname
=
self
.
_aspace
.
get_attribute_value
(
addref
.
TargetNodeId
,
ua
.
AttributeIds
.
DisplayName
).
Value
.
Value
dname
=
self
.
_aspace
.
get_attribute_value
(
addref
.
TargetNodeId
,
ua
.
AttributeIds
.
DisplayName
).
Value
.
Value
if
dname
:
if
dname
:
rdesc
.
DisplayName
=
dname
rdesc
.
DisplayName
=
dname
self
.
_aspace
.
nodes
[
addref
.
SourceNodeId
].
references
.
append
(
rdesc
)
self
.
_aspace
.
nodes
[
addref
.
SourceNodeId
].
references
.
append
(
rdesc
)
return
ua
.
StatusCode
()
return
ua
.
StatusCode
()
...
@@ -241,7 +249,7 @@ class NodeManagementService(object):
...
@@ -241,7 +249,7 @@ class NodeManagementService(object):
if
item
.
SpecifiedAttributes
&
getattr
(
ua
.
NodeAttributesMask
,
name
):
if
item
.
SpecifiedAttributes
&
getattr
(
ua
.
NodeAttributesMask
,
name
):
dv
=
ua
.
DataValue
(
ua
.
Variant
(
getattr
(
item
,
name
),
vtype
))
dv
=
ua
.
DataValue
(
ua
.
Variant
(
getattr
(
item
,
name
),
vtype
))
nodedata
.
attributes
[
getattr
(
ua
.
AttributeIds
,
name
)]
=
AttributeValue
(
dv
)
nodedata
.
attributes
[
getattr
(
ua
.
AttributeIds
,
name
)]
=
AttributeValue
(
dv
)
def
_add_nodeattributes
(
self
,
item
,
nodedata
):
def
_add_nodeattributes
(
self
,
item
,
nodedata
):
item
=
ua
.
downcast_extobject
(
item
)
item
=
ua
.
downcast_extobject
(
item
)
self
.
_add_node_attr
(
item
,
nodedata
,
"AccessLevel"
,
ua
.
VariantType
.
Byte
)
self
.
_add_node_attr
(
item
,
nodedata
,
"AccessLevel"
,
ua
.
VariantType
.
Byte
)
...
@@ -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
...
@@ -281,9 +290,9 @@ class MethodService(object):
...
@@ -281,9 +290,9 @@ class MethodService(object):
results
.
append
(
self
.
_call
(
method
))
results
.
append
(
self
.
_call
(
method
))
return
results
return
results
def
_call
(
self
,
method
):
def
_call
(
self
,
method
):
res
=
ua
.
CallMethodResult
()
res
=
ua
.
CallMethodResult
()
if
method
.
ObjectId
not
in
self
.
_aspace
.
nodes
or
method
.
MethodId
not
in
self
.
_aspace
.
nodes
:
if
method
.
ObjectId
not
in
self
.
_aspace
.
nodes
or
method
.
MethodId
not
in
self
.
_aspace
.
nodes
:
res
.
StatusCode
=
ua
.
StatusCode
(
ua
.
StatusCodes
.
BadNodeIdInvalid
)
res
.
StatusCode
=
ua
.
StatusCode
(
ua
.
StatusCodes
.
BadNodeIdInvalid
)
else
:
else
:
node
=
self
.
_aspace
.
nodes
[
method
.
MethodId
]
node
=
self
.
_aspace
.
nodes
[
method
.
MethodId
]
...
@@ -298,19 +307,21 @@ class MethodService(object):
...
@@ -298,19 +307,21 @@ class MethodService(object):
self
.
logger
.
exception
(
"Error executing method call %s, an exception was raised: "
,
method
)
self
.
logger
.
exception
(
"Error executing method call %s, an exception was raised: "
,
method
)
res
.
StatusCode
=
ua
.
StatusCode
(
ua
.
StatusCodes
.
BadUnexpectedError
)
res
.
StatusCode
=
ua
.
StatusCode
(
ua
.
StatusCodes
.
BadUnexpectedError
)
return
res
return
res
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,11 +20,12 @@ class BinaryClient(object):
...
@@ -19,11 +20,12 @@ 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
self
.
_do_stop
=
False
self
.
_do_stop
=
False
self
.
_security_token
=
ua
.
ChannelSecurityToken
()
self
.
_security_token
=
ua
.
ChannelSecurityToken
()
self
.
_authentication_token
=
ua
.
NodeId
()
self
.
_authentication_token
=
ua
.
NodeId
()
self
.
_sequence_number
=
0
self
.
_sequence_number
=
0
self
.
_request_id
=
0
self
.
_request_id
=
0
...
@@ -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
):
...
@@ -183,7 +184,7 @@ class BinaryClient(object):
...
@@ -183,7 +184,7 @@ class BinaryClient(object):
self
.
_callbackmap
[
0
]
=
future
self
.
_callbackmap
[
0
]
=
future
self
.
_write_socket
(
header
,
hello
)
self
.
_write_socket
(
header
,
hello
)
return
ua
.
Acknowledge
.
from_binary
(
future
.
result
())
return
ua
.
Acknowledge
.
from_binary
(
future
.
result
())
def
open_secure_channel
(
self
,
params
):
def
open_secure_channel
(
self
,
params
):
self
.
logger
.
info
(
"open_secure_channel"
)
self
.
logger
.
info
(
"open_secure_channel"
)
request
=
ua
.
OpenSecureChannelRequest
()
request
=
ua
.
OpenSecureChannelRequest
()
...
@@ -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"
)
...
@@ -315,7 +316,7 @@ class BinaryClient(object):
...
@@ -315,7 +316,7 @@ class BinaryClient(object):
def
publish
(
self
,
acks
=
None
):
def
publish
(
self
,
acks
=
None
):
self
.
logger
.
info
(
"publish"
)
self
.
logger
.
info
(
"publish"
)
if
acks
is
None
:
if
acks
is
None
:
acks
=
[]
acks
=
[]
request
=
ua
.
PublishRequest
()
request
=
ua
.
PublishRequest
()
request
.
Parameters
.
SubscriptionAcknowledgements
=
acks
request
.
Parameters
.
SubscriptionAcknowledgements
=
acks
...
@@ -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
()
...
@@ -356,14 +356,11 @@ class BinaryClient(object):
...
@@ -356,14 +356,11 @@ class BinaryClient(object):
response
=
ua
.
AddNodesResponse
.
from_binary
(
data
)
response
=
ua
.
AddNodesResponse
.
from_binary
(
data
)
response
.
ResponseHeader
.
ServiceResult
.
check
()
response
.
ResponseHeader
.
ServiceResult
.
check
()
return
response
.
Results
return
response
.
Results
def
call
(
self
,
methodstocall
):
def
call
(
self
,
methodstocall
):
request
=
ua
.
CallRequest
()
request
=
ua
.
CallRequest
()
request
.
Parameters
.
MethodsToCall
=
methodstocall
request
.
Parameters
.
MethodsToCall
=
methodstocall
data
=
self
.
_send_request
(
request
)
data
=
self
.
_send_request
(
request
)
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,14 +12,16 @@ from opcua import utils
...
@@ -12,14 +12,16 @@ 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__
)
if
timeout
==
0
:
# means no timeout bu we do not trust such servers
if
timeout
==
0
:
# means no timeout bu we do not trust such servers
timeout
=
360000
timeout
=
360000
self
.
timeout
=
timeout
self
.
timeout
=
timeout
self
.
client
=
client
self
.
client
=
client
...
@@ -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,8 +50,9 @@ class KeepAlive(Thread):
...
@@ -48,8 +50,9 @@ 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.
It attemps to expose as much functionality as possible
It attemps to expose as much functionality as possible
but if you want to do to special things you will probably need
but if you want to do to special things you will probably need
...
@@ -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.
...
@@ -65,8 +69,8 @@ class Client(object):
...
@@ -65,8 +69,8 @@ class Client(object):
"""
"""
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
server_url
=
urlparse
(
url
)
self
.
server_url
=
urlparse
(
url
)
self
.
name
=
"Pure Python Client"
self
.
name
=
"Pure Python Client"
self
.
description
=
self
.
name
self
.
description
=
self
.
name
self
.
application_uri
=
"urn:freeopcua:client"
self
.
application_uri
=
"urn:freeopcua:client"
self
.
product_uri
=
"urn:freeopcua.github.no:client"
self
.
product_uri
=
"urn:freeopcua.github.no:client"
self
.
security_policy_uri
=
"http://opcfoundation.org/UA/SecurityPolicy#None"
self
.
security_policy_uri
=
"http://opcfoundation.org/UA/SecurityPolicy#None"
...
@@ -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
):
"""
"""
...
@@ -161,14 +165,14 @@ class Client(object):
...
@@ -161,14 +165,14 @@ class Client(object):
params
=
ua
.
CreateSessionParameters
()
params
=
ua
.
CreateSessionParameters
()
params
.
ClientNonce
=
utils
.
create_nonce
()
params
.
ClientNonce
=
utils
.
create_nonce
()
params
.
ClientCertificate
=
b''
params
.
ClientCertificate
=
b''
params
.
ClientDescription
=
desc
params
.
ClientDescription
=
desc
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
...
@@ -25,16 +27,16 @@ class Event(object):
...
@@ -25,16 +27,16 @@ class Event(object):
self
.
node
=
Node
(
self
.
isession
,
etype
)
self
.
node
=
Node
(
self
.
isession
,
etype
)
else
:
else
:
self
.
node
=
Node
(
self
.
isession
,
ua
.
NodeId
(
etype
))
self
.
node
=
Node
(
self
.
isession
,
ua
.
NodeId
(
etype
))
self
.
set_members_from_node
(
self
.
node
)
self
.
set_members_from_node
(
self
.
node
)
if
isinstance
(
source
,
Node
):
if
isinstance
(
source
,
Node
):
self
.
SourceNode
=
source
.
NodeId
self
.
SourceNode
=
source
.
NodeId
elif
isinstance
(
etype
,
ua
.
NodeId
):
elif
isinstance
(
etype
,
ua
.
NodeId
):
self
.
SourceNode
=
source
.
NodeId
self
.
SourceNode
=
source
.
NodeId
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,13 +84,13 @@ class InternalServer(object):
...
@@ -81,13 +84,13 @@ 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
)
# create a session to use on server side
# create a session to use on server side
self
.
isession
=
InternalSession
(
self
,
self
.
aspace
,
self
.
subcsription_service
,
"Internal"
)
self
.
isession
=
InternalSession
(
self
,
self
.
aspace
,
self
.
subcsription_service
,
"Internal"
)
self
.
_timer
=
None
self
.
_timer
=
None
self
.
current_time_node
=
Node
(
self
.
isession
,
ua
.
NodeId
(
ua
.
ObjectIds
.
Server_ServerStatus_CurrentTime
))
self
.
current_time_node
=
Node
(
self
.
isession
,
ua
.
NodeId
(
ua
.
ObjectIds
.
Server_ServerStatus_CurrentTime
))
uries
=
[
"http://opcfoundation.org/UA/"
]
uries
=
[
"http://opcfoundation.org/UA/"
]
...
@@ -100,7 +103,7 @@ class InternalServer(object):
...
@@ -100,7 +103,7 @@ class InternalServer(object):
def
dump_address_space
(
self
,
path
):
def
dump_address_space
(
self
,
path
):
self
.
aspace
.
dump
(
path
)
self
.
aspace
.
dump
(
path
)
def
start
(
self
):
def
start
(
self
):
self
.
logger
.
info
(
"starting internal server"
)
self
.
logger
.
info
(
"starting internal server"
)
self
.
loop
.
start
()
self
.
loop
.
start
()
Node
(
self
.
isession
,
ua
.
NodeId
(
ua
.
ObjectIds
.
Server_ServerStatus_State
)).
set_value
(
0
)
Node
(
self
.
isession
,
ua
.
NodeId
(
ua
.
ObjectIds
.
Server_ServerStatus_State
)).
set_value
(
0
)
...
@@ -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
...
@@ -144,14 +149,14 @@ class InternalSession(object):
...
@@ -144,14 +149,14 @@ class InternalSession(object):
InternalSession
.
_counter
+=
1
InternalSession
.
_counter
+=
1
self
.
authentication_token
=
ua
.
NodeId
(
self
.
_auth_counter
)
self
.
authentication_token
=
ua
.
NodeId
(
self
.
_auth_counter
)
InternalSession
.
_auth_counter
+=
1
InternalSession
.
_auth_counter
+=
1
self
.
nonce
=
utils
.
create_nonce
()
self
.
nonce
=
utils
.
create_nonce
()
self
.
subscriptions
=
[]
self
.
subscriptions
=
[]
self
.
logger
.
warning
(
"Created internal session %s"
,
self
.
name
)
self
.
logger
.
warning
(
"Created internal session %s"
,
self
.
name
)
self
.
_lock
=
Lock
()
self
.
_lock
=
Lock
()
def
__str__
(
self
):
def
__str__
(
self
):
return
"InternalSession(name:{}, id:{}, auth_token:{})"
.
format
(
self
.
name
,
self
.
session_id
,
self
.
authentication_token
)
return
"InternalSession(name:{}, id:{}, auth_token:{})"
.
format
(
self
.
name
,
self
.
session_id
,
self
.
authentication_token
)
def
get_endpoints
(
self
,
params
=
None
):
def
get_endpoints
(
self
,
params
=
None
):
return
self
.
iserver
.
get_endpoints
(
params
)
return
self
.
iserver
.
get_endpoints
(
params
)
...
@@ -160,7 +165,7 @@ class InternalSession(object):
...
@@ -160,7 +165,7 @@ class InternalSession(object):
result
=
ua
.
CreateSessionResult
()
result
=
ua
.
CreateSessionResult
()
result
.
SessionId
=
self
.
session_id
result
.
SessionId
=
self
.
session_id
result
.
AuthenticationToken
=
self
.
authentication_token
result
.
AuthenticationToken
=
self
.
authentication_token
result
.
RevisedSessionTimeout
=
params
.
RequestedSessionTimeout
result
.
RevisedSessionTimeout
=
params
.
RequestedSessionTimeout
result
.
MaxRequestMessageSize
=
65536
result
.
MaxRequestMessageSize
=
65536
result
.
ServerNonce
=
self
.
nonce
result
.
ServerNonce
=
self
.
nonce
...
@@ -230,10 +235,8 @@ class InternalSession(object):
...
@@ -230,10 +235,8 @@ class InternalSession(object):
def
delete_monitored_items
(
self
,
params
):
def
delete_monitored_items
(
self
,
params
):
return
self
.
subscription_service
.
delete_monitored_items
(
params
)
return
self
.
subscription_service
.
delete_monitored_items
(
params
)
def
publish
(
self
,
acks
=
None
):
def
publish
(
self
,
acks
=
None
):
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
"""
"""
High level node object, to access node attribute
High level node object, to access node attribute
and browse address space
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
...
@@ -61,7 +65,7 @@ class Node(object):
...
@@ -61,7 +65,7 @@ class Node(object):
def
get_value
(
self
):
def
get_value
(
self
):
"""
"""
Get value of a node. Only variables(properties) have values.
Get value of a node. Only variables(properties) have values.
An exception will be generated for other node types.
An exception will be generated for other node types.
"""
"""
result
=
self
.
get_attribute
(
ua
.
AttributeIds
.
Value
)
result
=
self
.
get_attribute
(
ua
.
AttributeIds
.
Value
)
...
@@ -69,11 +73,11 @@ class Node(object):
...
@@ -69,11 +73,11 @@ class Node(object):
def
set_value
(
self
,
value
,
varianttype
=
None
):
def
set_value
(
self
,
value
,
varianttype
=
None
):
"""
"""
Set value of a node. Only variables(properties) have values.
Set value of a node. Only variables(properties) have values.
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
)
...
@@ -148,7 +152,7 @@ class Node(object):
...
@@ -148,7 +152,7 @@ class Node(object):
desc
.
IncludeSubtypes
=
includesubtypes
desc
.
IncludeSubtypes
=
includesubtypes
desc
.
NodeClassMask
=
nodeclassmask
desc
.
NodeClassMask
=
nodeclassmask
desc
.
ResultMask
=
ua
.
BrowseResultMask
.
All
desc
.
ResultMask
=
ua
.
BrowseResultMask
.
All
desc
.
NodeId
=
self
.
nodeid
desc
.
NodeId
=
self
.
nodeid
params
=
ua
.
BrowseParameters
()
params
=
ua
.
BrowseParameters
()
params
.
NodesToBrowse
.
append
(
desc
)
params
.
NodesToBrowse
.
append
(
desc
)
...
@@ -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
):
...
@@ -194,11 +198,11 @@ class Node(object):
...
@@ -194,11 +198,11 @@ class Node(object):
"""
"""
nodeid
,
qname
=
self
.
_parse_add_args
(
*
args
)
nodeid
,
qname
=
self
.
_parse_add_args
(
*
args
)
return
self
.
_add_folder
(
nodeid
,
qname
)
return
self
.
_add_folder
(
nodeid
,
qname
)
def
_add_folder
(
self
,
nodeid
,
qname
):
def
_add_folder
(
self
,
nodeid
,
qname
):
node
=
ua
.
AddNodesItem
()
node
=
ua
.
AddNodesItem
()
node
.
RequestedNewNodeId
=
nodeid
node
.
RequestedNewNodeId
=
nodeid
node
.
BrowseName
=
qname
node
.
BrowseName
=
qname
node
.
NodeClass
=
ua
.
NodeClass
.
Object
node
.
NodeClass
=
ua
.
NodeClass
.
Object
node
.
ParentNodeId
=
self
.
nodeid
node
.
ParentNodeId
=
self
.
nodeid
node
.
ReferenceTypeId
=
ua
.
NodeId
.
from_string
(
"i=35"
)
node
.
ReferenceTypeId
=
ua
.
NodeId
.
from_string
(
"i=35"
)
...
@@ -211,7 +215,7 @@ class Node(object):
...
@@ -211,7 +215,7 @@ class Node(object):
results
=
self
.
server
.
add_nodes
([
node
])
results
=
self
.
server
.
add_nodes
([
node
])
results
[
0
].
StatusCode
.
check
()
results
[
0
].
StatusCode
.
check
()
return
Node
(
self
.
server
,
nodeid
)
return
Node
(
self
.
server
,
nodeid
)
def
add_object
(
self
,
*
args
):
def
add_object
(
self
,
*
args
):
"""
"""
create a child node object
create a child node object
...
@@ -220,13 +224,13 @@ class Node(object):
...
@@ -220,13 +224,13 @@ class Node(object):
"""
"""
nodeid
,
qname
=
self
.
_parse_add_args
(
*
args
)
nodeid
,
qname
=
self
.
_parse_add_args
(
*
args
)
return
self
.
_add_object
(
nodeid
,
qname
)
return
self
.
_add_object
(
nodeid
,
qname
)
def
_add_object
(
self
,
nodeid
,
qname
):
def
_add_object
(
self
,
nodeid
,
qname
):
node
=
ua
.
AddNodesItem
()
node
=
ua
.
AddNodesItem
()
node
.
RequestedNewNodeId
=
nodeid
node
.
RequestedNewNodeId
=
nodeid
node
.
BrowseName
=
qname
node
.
BrowseName
=
qname
node
.
NodeClass
=
ua
.
NodeClass
.
Object
node
.
NodeClass
=
ua
.
NodeClass
.
Object
node
.
ParentNodeId
=
self
.
nodeid
node
.
ParentNodeId
=
self
.
nodeid
node
.
ReferenceTypeId
=
ua
.
NodeId
.
from_string
(
"i=35"
)
node
.
ReferenceTypeId
=
ua
.
NodeId
.
from_string
(
"i=35"
)
node
.
TypeDefinition
=
ua
.
NodeId
(
ua
.
ObjectIds
.
BaseObjectType
)
node
.
TypeDefinition
=
ua
.
NodeId
(
ua
.
ObjectIds
.
BaseObjectType
)
attrs
=
ua
.
ObjectAttributes
()
attrs
=
ua
.
ObjectAttributes
()
...
@@ -247,7 +251,7 @@ class Node(object):
...
@@ -247,7 +251,7 @@ class Node(object):
nodeid
,
qname
=
self
.
_parse_add_args
(
*
args
[:
2
])
nodeid
,
qname
=
self
.
_parse_add_args
(
*
args
[:
2
])
val
=
self
.
_to_variant
(
*
args
[
2
:])
val
=
self
.
_to_variant
(
*
args
[
2
:])
return
self
.
_add_variable
(
nodeid
,
qname
,
val
,
isproperty
=
True
)
return
self
.
_add_variable
(
nodeid
,
qname
,
val
,
isproperty
=
True
)
def
add_variable
(
self
,
*
args
):
def
add_variable
(
self
,
*
args
):
"""
"""
create a child node variable
create a child node variable
...
@@ -266,10 +270,10 @@ class Node(object):
...
@@ -266,10 +270,10 @@ class Node(object):
def
_add_variable
(
self
,
nodeid
,
qname
,
val
,
isproperty
=
False
):
def
_add_variable
(
self
,
nodeid
,
qname
,
val
,
isproperty
=
False
):
node
=
ua
.
AddNodesItem
()
node
=
ua
.
AddNodesItem
()
node
.
RequestedNewNodeId
=
nodeid
node
.
RequestedNewNodeId
=
nodeid
node
.
BrowseName
=
qname
node
.
BrowseName
=
qname
node
.
NodeClass
=
ua
.
NodeClass
.
Variable
node
.
NodeClass
=
ua
.
NodeClass
.
Variable
node
.
ParentNodeId
=
self
.
nodeid
node
.
ParentNodeId
=
self
.
nodeid
if
isproperty
:
if
isproperty
:
node
.
ReferenceTypeId
=
ua
.
NodeId
(
ua
.
ObjectIds
.
HasProperty
)
node
.
ReferenceTypeId
=
ua
.
NodeId
(
ua
.
ObjectIds
.
HasProperty
)
node
.
TypeDefinition
=
ua
.
NodeId
(
ua
.
ObjectIds
.
PropertyType
)
node
.
TypeDefinition
=
ua
.
NodeId
(
ua
.
ObjectIds
.
PropertyType
)
...
@@ -281,7 +285,7 @@ class Node(object):
...
@@ -281,7 +285,7 @@ class Node(object):
attrs
.
DisplayName
=
ua
.
LocalizedText
(
qname
.
Name
)
attrs
.
DisplayName
=
ua
.
LocalizedText
(
qname
.
Name
)
attrs
.
DataType
=
self
.
_guess_uatype
(
val
)
attrs
.
DataType
=
self
.
_guess_uatype
(
val
)
attrs
.
Value
=
val
attrs
.
Value
=
val
attrs
.
ValueRank
=
0
attrs
.
ValueRank
=
0
attrs
.
WriteMask
=
0
attrs
.
WriteMask
=
0
attrs
.
UserWriteMask
=
0
attrs
.
UserWriteMask
=
0
attrs
.
Historizing
=
0
attrs
.
Historizing
=
0
...
@@ -302,17 +306,17 @@ class Node(object):
...
@@ -302,17 +306,17 @@ class Node(object):
nodeid
,
qname
=
self
.
_parse_add_args
(
*
args
[:
2
])
nodeid
,
qname
=
self
.
_parse_add_args
(
*
args
[:
2
])
callback
=
args
[
2
]
callback
=
args
[
2
]
if
len
(
args
)
>
3
:
if
len
(
args
)
>
3
:
inputs
=
args
[
3
]
inputs
=
args
[
3
]
if
len
(
args
)
>
4
:
if
len
(
args
)
>
4
:
outputs
=
args
[
4
]
outputs
=
args
[
4
]
return
self
.
_add_method
(
nodeid
,
qname
,
callback
,
inputs
,
outputs
)
return
self
.
_add_method
(
nodeid
,
qname
,
callback
,
inputs
,
outputs
)
def
_add_method
(
self
,
nodeid
,
qname
,
callback
,
inputs
,
outputs
):
def
_add_method
(
self
,
nodeid
,
qname
,
callback
,
inputs
,
outputs
):
node
=
ua
.
AddNodesItem
()
node
=
ua
.
AddNodesItem
()
node
.
RequestedNewNodeId
=
nodeid
node
.
RequestedNewNodeId
=
nodeid
node
.
BrowseName
=
qname
node
.
BrowseName
=
qname
node
.
NodeClass
=
ua
.
NodeClass
.
Method
node
.
NodeClass
=
ua
.
NodeClass
.
Method
node
.
ParentNodeId
=
self
.
nodeid
node
.
ParentNodeId
=
self
.
nodeid
node
.
ReferenceTypeId
=
ua
.
NodeId
.
from_string
(
"i=47"
)
node
.
ReferenceTypeId
=
ua
.
NodeId
.
from_string
(
"i=47"
)
#node.TypeDefinition = ua.NodeId(ua.ObjectIds.BaseObjectType)
#node.TypeDefinition = ua.NodeId(ua.ObjectIds.BaseObjectType)
attrs
=
ua
.
MethodAttributes
()
attrs
=
ua
.
MethodAttributes
()
...
@@ -332,19 +336,18 @@ class Node(object):
...
@@ -332,19 +336,18 @@ class Node(object):
method
.
add_property
(
qname
.
NamespaceIndex
,
"OutputArguments"
,
[
self
.
_vtype_to_argument
(
vtype
)
for
vtype
in
outputs
])
method
.
add_property
(
qname
.
NamespaceIndex
,
"OutputArguments"
,
[
self
.
_vtype_to_argument
(
vtype
)
for
vtype
in
outputs
])
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
nodeid of method as a NodeId object
nodeid of method as a NodeId object
arguments are variants or python object convertible to variants.
arguments are variants or python object convertible to variants.
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
):
...
@@ -48,9 +49,9 @@ class Server(object):
...
@@ -48,9 +49,9 @@ class Server(object):
appdesc
=
ua
.
ApplicationDescription
()
appdesc
=
ua
.
ApplicationDescription
()
appdesc
.
ApplicationName
=
ua
.
LocalizedText
(
self
.
name
)
appdesc
.
ApplicationName
=
ua
.
LocalizedText
(
self
.
name
)
appdesc
.
ApplicationUri
=
self
.
server_uri
appdesc
.
ApplicationUri
=
self
.
server_uri
appdesc
.
ApplicationType
=
ua
.
ApplicationType
.
Server
appdesc
.
ApplicationType
=
ua
.
ApplicationType
.
Server
appdesc
.
ProductUri
=
self
.
product_uri
appdesc
.
ProductUri
=
self
.
product_uri
appdesc
.
DiscoveryUrls
.
append
(
self
.
endpoint
.
geturl
())
appdesc
.
DiscoveryUrls
.
append
(
self
.
endpoint
.
geturl
())
edp
=
ua
.
EndpointDescription
()
edp
=
ua
.
EndpointDescription
()
...
@@ -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
()
...
@@ -63,7 +67,7 @@ class Subscription(object):
...
@@ -63,7 +67,7 @@ class Subscription(object):
self
.
_call_status
(
statuschange
)
self
.
_call_status
(
statuschange
)
else
:
else
:
self
.
logger
.
warn
(
"Notification type not supported yet for notification %s"
,
notif
)
self
.
logger
.
warn
(
"Notification type not supported yet for notification %s"
,
notif
)
ack
=
ua
.
SubscriptionAcknowledgement
()
ack
=
ua
.
SubscriptionAcknowledgement
()
ack
.
SubscriptionId
=
self
.
subscription_id
ack
.
SubscriptionId
=
self
.
subscription_id
ack
.
SequenceNumber
=
publishresult
.
NotificationMessage
.
SequenceNumber
ack
.
SequenceNumber
=
publishresult
.
NotificationMessage
.
SequenceNumber
...
@@ -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
...
@@ -143,7 +147,7 @@ class Subscription(object):
...
@@ -143,7 +147,7 @@ class Subscription(object):
if
mfilter
:
if
mfilter
:
mparams
.
Filter
=
mfilter
mparams
.
Filter
=
mfilter
mir
=
ua
.
MonitoredItemCreateRequest
()
mir
=
ua
.
MonitoredItemCreateRequest
()
mir
.
ItemToMonitor
=
rv
mir
.
ItemToMonitor
=
rv
mir
.
MonitoringMode
=
ua
.
MonitoringMode
.
Reporting
mir
.
MonitoringMode
=
ua
.
MonitoringMode
.
Reporting
mir
.
RequestedParameters
=
mparams
mir
.
RequestedParameters
=
mparams
...
@@ -156,7 +160,7 @@ class Subscription(object):
...
@@ -156,7 +160,7 @@ class Subscription(object):
results
=
self
.
server
.
create_monitored_items
(
params
)
results
=
self
.
server
.
create_monitored_items
(
params
)
result
=
results
[
0
]
result
=
results
[
0
]
result
.
StatusCode
.
check
()
result
.
StatusCode
.
check
()
data
=
SubscriptionItemData
()
data
=
SubscriptionItemData
()
data
.
client_handle
=
mparams
.
ClientHandle
data
.
client_handle
=
mparams
.
ClientHandle
data
.
node
=
node
data
.
node
=
node
...
@@ -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,17 +102,18 @@ class SubscriptionService(object):
...
@@ -100,17 +102,18 @@ 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
self
.
monitored_item_id
=
None
self
.
monitored_item_id
=
None
self
.
parameters
=
None
self
.
parameters
=
None
self
.
mode
=
None
self
.
mode
=
None
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
...
@@ -128,18 +131,18 @@ class InternalSubscription(object):
...
@@ -128,18 +131,18 @@ class InternalSubscription(object):
self
.
_triggered_statuschanges
=
[]
self
.
_triggered_statuschanges
=
[]
self
.
_notification_seq
=
1
self
.
_notification_seq
=
1
self
.
_not_acknowledged_results
=
{}
self
.
_not_acknowledged_results
=
{}
self
.
_startup
=
True
self
.
_startup
=
True
self
.
_keep_alive_count
=
0
self
.
_keep_alive_count
=
0
self
.
_publish_cycles_count
=
0
self
.
_publish_cycles_count
=
0
self
.
_stopev
=
False
self
.
_stopev
=
False
def
__str__
(
self
):
def
__str__
(
self
):
return
"Subscription(id:{})"
.
format
(
self
.
data
.
SubscriptionId
)
return
"Subscription(id:{})"
.
format
(
self
.
data
.
SubscriptionId
)
def
start
(
self
):
def
start
(
self
):
self
.
logger
.
debug
(
"starting subscription %s"
,
self
.
data
.
SubscriptionId
)
self
.
logger
.
debug
(
"starting subscription %s"
,
self
.
data
.
SubscriptionId
)
self
.
_subscription_loop
()
self
.
_subscription_loop
()
def
stop
(
self
):
def
stop
(
self
):
self
.
logger
.
debug
(
"stopping subscription %s"
,
self
.
data
.
SubscriptionId
)
self
.
logger
.
debug
(
"stopping subscription %s"
,
self
.
data
.
SubscriptionId
)
self
.
_stopev
=
True
self
.
_stopev
=
True
...
@@ -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
()
...
@@ -167,14 +170,14 @@ class InternalSubscription(object):
...
@@ -167,14 +170,14 @@ class InternalSubscription(object):
self
.
_keep_alive_count
+=
1
self
.
_keep_alive_count
+=
1
return
False
return
False
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
)
...
@@ -185,7 +188,7 @@ class InternalSubscription(object):
...
@@ -185,7 +188,7 @@ class InternalSubscription(object):
if
self
.
_triggered_datachanges
:
if
self
.
_triggered_datachanges
:
notif
=
ua
.
DataChangeNotification
()
notif
=
ua
.
DataChangeNotification
()
notif
.
MonitoredItems
=
self
.
_triggered_datachanges
[:]
notif
.
MonitoredItems
=
self
.
_triggered_datachanges
[:]
self
.
_triggered_datachanges
=
[]
self
.
_triggered_datachanges
=
[]
self
.
logger
.
debug
(
"sending datachanges nontification with %s events"
,
len
(
notif
.
MonitoredItems
))
self
.
logger
.
debug
(
"sending datachanges nontification with %s events"
,
len
(
notif
.
MonitoredItems
))
result
.
NotificationMessage
.
NotificationData
.
append
(
notif
)
result
.
NotificationMessage
.
NotificationData
.
append
(
notif
)
if
self
.
_triggered_events
:
if
self
.
_triggered_events
:
...
@@ -205,7 +208,7 @@ class InternalSubscription(object):
...
@@ -205,7 +208,7 @@ class InternalSubscription(object):
result
.
AvailableSequenceNumbers
=
list
(
self
.
_not_acknowledged_results
.
keys
())
result
.
AvailableSequenceNumbers
=
list
(
self
.
_not_acknowledged_results
.
keys
())
self
.
_not_acknowledged_results
[
result
.
NotificationMessage
.
SequenceNumber
]
=
result
self
.
_not_acknowledged_results
[
result
.
NotificationMessage
.
SequenceNumber
]
=
result
return
result
return
result
def
trigger_statuschange
(
self
,
code
):
def
trigger_statuschange
(
self
,
code
):
self
.
_triggered_statuschanges
.
append
(
code
)
self
.
_triggered_statuschanges
.
append
(
code
)
...
@@ -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
...
@@ -271,7 +274,7 @@ class InternalSubscription(object):
...
@@ -271,7 +274,7 @@ class InternalSubscription(object):
mdata
.
parameters
=
result
mdata
.
parameters
=
result
mdata
.
mode
=
params
.
MonitoringMode
mdata
.
mode
=
params
.
MonitoringMode
mdata
.
client_handle
=
params
.
RequestedParameters
.
ClientHandle
mdata
.
client_handle
=
params
.
RequestedParameters
.
ClientHandle
mdata
.
monitored_item_id
=
result
.
MonitoredItemId
mdata
.
monitored_item_id
=
result
.
MonitoredItemId
self
.
_monitored_items
[
result
.
MonitoredItemId
]
=
mdata
self
.
_monitored_items
[
result
.
MonitoredItemId
]
=
mdata
...
@@ -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,8 +63,8 @@ class UAProcessor(object):
...
@@ -59,8 +63,8 @@ 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
)
def
_write_socket
(
self
,
hdr
,
*
args
):
def
_write_socket
(
self
,
hdr
,
*
args
):
...
@@ -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
)
...
@@ -123,16 +127,16 @@ class UAProcessor(object):
...
@@ -123,16 +127,16 @@ class UAProcessor(object):
else
:
else
:
self
.
logger
.
warning
(
"Unsupported message type: %s"
,
header
.
MessageType
)
self
.
logger
.
warning
(
"Unsupported message type: %s"
,
header
.
MessageType
)
return
True
return
True
def
process_message
(
self
,
algohdr
,
seqhdr
,
body
):
def
process_message
(
self
,
algohdr
,
seqhdr
,
body
):
typeid
=
ua
.
NodeId
.
from_binary
(
body
)
typeid
=
ua
.
NodeId
.
from_binary
(
body
)
requesthdr
=
ua
.
RequestHeader
.
from_binary
(
body
)
requesthdr
=
ua
.
RequestHeader
.
from_binary
(
body
)
if
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
CreateSessionRequest_Encoding_DefaultBinary
):
if
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
CreateSessionRequest_Encoding_DefaultBinary
):
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
...
@@ -141,7 +145,7 @@ class UAProcessor(object):
...
@@ -141,7 +145,7 @@ class UAProcessor(object):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
CloseSessionRequest_Encoding_DefaultBinary
):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
CloseSessionRequest_Encoding_DefaultBinary
):
self
.
logger
.
info
(
"Close session request"
)
self
.
logger
.
info
(
"Close session request"
)
deletesubs
=
ua
.
unpack_uatype
(
'Boolean'
,
body
)
deletesubs
=
ua
.
unpack_uatype
(
'Boolean'
,
body
)
self
.
session
.
close_session
(
deletesubs
)
self
.
session
.
close_session
(
deletesubs
)
response
=
ua
.
CloseSessionResponse
()
response
=
ua
.
CloseSessionResponse
()
...
@@ -149,11 +153,11 @@ class UAProcessor(object):
...
@@ -149,11 +153,11 @@ class UAProcessor(object):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
ActivateSessionRequest_Encoding_DefaultBinary
):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
ActivateSessionRequest_Encoding_DefaultBinary
):
self
.
logger
.
info
(
"Activate session request"
)
self
.
logger
.
info
(
"Activate session request"
)
params
=
ua
.
ActivateSessionParameters
.
from_binary
(
body
)
params
=
ua
.
ActivateSessionParameters
.
from_binary
(
body
)
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
)
...
@@ -166,8 +170,8 @@ class UAProcessor(object):
...
@@ -166,8 +170,8 @@ class UAProcessor(object):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
ReadRequest_Encoding_DefaultBinary
):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
ReadRequest_Encoding_DefaultBinary
):
self
.
logger
.
info
(
"Read request"
)
self
.
logger
.
info
(
"Read request"
)
params
=
ua
.
ReadParameters
.
from_binary
(
body
)
params
=
ua
.
ReadParameters
.
from_binary
(
body
)
results
=
self
.
session
.
read
(
params
)
results
=
self
.
session
.
read
(
params
)
response
=
ua
.
ReadResponse
()
response
=
ua
.
ReadResponse
()
...
@@ -176,8 +180,8 @@ class UAProcessor(object):
...
@@ -176,8 +180,8 @@ class UAProcessor(object):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
WriteRequest_Encoding_DefaultBinary
):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
WriteRequest_Encoding_DefaultBinary
):
self
.
logger
.
info
(
"Write request"
)
self
.
logger
.
info
(
"Write request"
)
params
=
ua
.
WriteParameters
.
from_binary
(
body
)
params
=
ua
.
WriteParameters
.
from_binary
(
body
)
results
=
self
.
session
.
write
(
params
)
results
=
self
.
session
.
write
(
params
)
response
=
ua
.
WriteResponse
()
response
=
ua
.
WriteResponse
()
...
@@ -186,8 +190,8 @@ class UAProcessor(object):
...
@@ -186,8 +190,8 @@ class UAProcessor(object):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
BrowseRequest_Encoding_DefaultBinary
):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
BrowseRequest_Encoding_DefaultBinary
):
self
.
logger
.
info
(
"Browse request"
)
self
.
logger
.
info
(
"Browse request"
)
params
=
ua
.
BrowseParameters
.
from_binary
(
body
)
params
=
ua
.
BrowseParameters
.
from_binary
(
body
)
results
=
self
.
session
.
browse
(
params
)
results
=
self
.
session
.
browse
(
params
)
response
=
ua
.
BrowseResponse
()
response
=
ua
.
BrowseResponse
()
...
@@ -196,8 +200,8 @@ class UAProcessor(object):
...
@@ -196,8 +200,8 @@ class UAProcessor(object):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
GetEndpointsRequest_Encoding_DefaultBinary
):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
GetEndpointsRequest_Encoding_DefaultBinary
):
self
.
logger
.
info
(
"get endpoints request"
)
self
.
logger
.
info
(
"get endpoints request"
)
params
=
ua
.
GetEndpointsParameters
.
from_binary
(
body
)
params
=
ua
.
GetEndpointsParameters
.
from_binary
(
body
)
endpoints
=
self
.
iserver
.
get_endpoints
(
params
)
endpoints
=
self
.
iserver
.
get_endpoints
(
params
)
response
=
ua
.
GetEndpointsResponse
()
response
=
ua
.
GetEndpointsResponse
()
...
@@ -207,8 +211,8 @@ class UAProcessor(object):
...
@@ -207,8 +211,8 @@ class UAProcessor(object):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
TranslateBrowsePathsToNodeIdsRequest_Encoding_DefaultBinary
):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
TranslateBrowsePathsToNodeIdsRequest_Encoding_DefaultBinary
):
self
.
logger
.
info
(
"translate browsepaths to nodeids request"
)
self
.
logger
.
info
(
"translate browsepaths to nodeids request"
)
params
=
ua
.
TranslateBrowsePathsToNodeIdsParameters
.
from_binary
(
body
)
params
=
ua
.
TranslateBrowsePathsToNodeIdsParameters
.
from_binary
(
body
)
paths
=
self
.
session
.
translate_browsepaths_to_nodeids
(
params
.
BrowsePaths
)
paths
=
self
.
session
.
translate_browsepaths_to_nodeids
(
params
.
BrowsePaths
)
response
=
ua
.
TranslateBrowsePathsToNodeIdsResponse
()
response
=
ua
.
TranslateBrowsePathsToNodeIdsResponse
()
...
@@ -218,8 +222,8 @@ class UAProcessor(object):
...
@@ -218,8 +222,8 @@ class UAProcessor(object):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
AddNodesRequest_Encoding_DefaultBinary
):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
AddNodesRequest_Encoding_DefaultBinary
):
self
.
logger
.
info
(
"add nodes request"
)
self
.
logger
.
info
(
"add nodes request"
)
params
=
ua
.
AddNodesParameters
.
from_binary
(
body
)
params
=
ua
.
AddNodesParameters
.
from_binary
(
body
)
results
=
self
.
session
.
add_nodes
(
params
.
NodesToAdd
)
results
=
self
.
session
.
add_nodes
(
params
.
NodesToAdd
)
response
=
ua
.
AddNodesResponse
()
response
=
ua
.
AddNodesResponse
()
...
@@ -229,8 +233,8 @@ class UAProcessor(object):
...
@@ -229,8 +233,8 @@ class UAProcessor(object):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
CreateSubscriptionRequest_Encoding_DefaultBinary
):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
CreateSubscriptionRequest_Encoding_DefaultBinary
):
self
.
logger
.
info
(
"create subscription request"
)
self
.
logger
.
info
(
"create subscription request"
)
params
=
ua
.
CreateSubscriptionParameters
.
from_binary
(
body
)
params
=
ua
.
CreateSubscriptionParameters
.
from_binary
(
body
)
result
=
self
.
session
.
create_subscription
(
params
,
self
.
forward_publish_response
)
result
=
self
.
session
.
create_subscription
(
params
,
self
.
forward_publish_response
)
response
=
ua
.
CreateSubscriptionResponse
()
response
=
ua
.
CreateSubscriptionResponse
()
...
@@ -240,8 +244,8 @@ class UAProcessor(object):
...
@@ -240,8 +244,8 @@ class UAProcessor(object):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
DeleteSubscriptionsRequest_Encoding_DefaultBinary
):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
DeleteSubscriptionsRequest_Encoding_DefaultBinary
):
self
.
logger
.
info
(
"delete subscriptions request"
)
self
.
logger
.
info
(
"delete subscriptions request"
)
params
=
ua
.
DeleteSubscriptionsParameters
.
from_binary
(
body
)
params
=
ua
.
DeleteSubscriptionsParameters
.
from_binary
(
body
)
results
=
self
.
session
.
delete_subscriptions
(
params
.
SubscriptionIds
)
results
=
self
.
session
.
delete_subscriptions
(
params
.
SubscriptionIds
)
response
=
ua
.
DeleteSubscriptionsResponse
()
response
=
ua
.
DeleteSubscriptionsResponse
()
...
@@ -261,7 +265,7 @@ class UAProcessor(object):
...
@@ -261,7 +265,7 @@ class UAProcessor(object):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
ModifyMonitoredItemsRequest_Encoding_DefaultBinary
):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
ModifyMonitoredItemsRequest_Encoding_DefaultBinary
):
self
.
logger
.
info
(
"modify monitored items request"
)
self
.
logger
.
info
(
"modify monitored items request"
)
params
=
ua
.
ModifyMonitoredItemsParameters
.
from_binary
(
body
)
params
=
ua
.
ModifyMonitoredItemsParameters
.
from_binary
(
body
)
results
=
self
.
session
.
modify_monitored_items
(
params
)
results
=
self
.
session
.
modify_monitored_items
(
params
)
response
=
ua
.
ModifyMonitoredItemsResponse
()
response
=
ua
.
ModifyMonitoredItemsResponse
()
...
@@ -271,8 +275,8 @@ class UAProcessor(object):
...
@@ -271,8 +275,8 @@ class UAProcessor(object):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
DeleteMonitoredItemsRequest_Encoding_DefaultBinary
):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
DeleteMonitoredItemsRequest_Encoding_DefaultBinary
):
self
.
logger
.
info
(
"delete monitored items request"
)
self
.
logger
.
info
(
"delete monitored items request"
)
params
=
ua
.
DeleteMonitoredItemsParameters
.
from_binary
(
body
)
params
=
ua
.
DeleteMonitoredItemsParameters
.
from_binary
(
body
)
results
=
self
.
session
.
delete_monitored_items
(
params
)
results
=
self
.
session
.
delete_monitored_items
(
params
)
response
=
ua
.
DeleteMonitoredItemsResponse
()
response
=
ua
.
DeleteMonitoredItemsResponse
()
...
@@ -285,21 +289,21 @@ class UAProcessor(object):
...
@@ -285,21 +289,21 @@ class UAProcessor(object):
if
not
self
.
session
:
if
not
self
.
session
:
return
False
return
False
params
=
ua
.
PublishParameters
.
from_binary
(
body
)
params
=
ua
.
PublishParameters
.
from_binary
(
body
)
data
=
PublishRequestData
()
data
=
PublishRequestData
()
data
.
requesthdr
=
requesthdr
data
.
requesthdr
=
requesthdr
data
.
seqhdr
=
seqhdr
data
.
seqhdr
=
seqhdr
data
.
algohdr
=
algohdr
data
.
algohdr
=
algohdr
with
self
.
_datalock
:
with
self
.
_datalock
:
self
.
_publishdata_queue
.
append
(
data
)
# will be used to send publish answers from server
self
.
_publishdata_queue
.
append
(
data
)
# will be used to send publish answers from server
self
.
session
.
publish
(
params
.
SubscriptionAcknowledgements
)
self
.
session
.
publish
(
params
.
SubscriptionAcknowledgements
)
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
RepublishRequest_Encoding_DefaultBinary
):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
RepublishRequest_Encoding_DefaultBinary
):
self
.
logger
.
info
(
"re-publish request"
)
self
.
logger
.
info
(
"re-publish request"
)
params
=
ua
.
RepublishParameters
.
from_binary
(
body
)
params
=
ua
.
RepublishParameters
.
from_binary
(
body
)
msg
=
self
.
session
.
republish
(
params
)
msg
=
self
.
session
.
republish
(
params
)
response
=
ua
.
RepublishResponse
()
response
=
ua
.
RepublishResponse
()
...
@@ -313,12 +317,12 @@ class UAProcessor(object):
...
@@ -313,12 +317,12 @@ class UAProcessor(object):
self
.
send_response
(
requesthdr
.
RequestHandle
,
algohdr
,
seqhdr
,
response
)
self
.
send_response
(
requesthdr
.
RequestHandle
,
algohdr
,
seqhdr
,
response
)
self
.
channel
=
None
self
.
channel
=
None
return
False
return
False
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
CallRequest_Encoding_DefaultBinary
):
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
CallRequest_Encoding_DefaultBinary
):
self
.
logger
.
info
(
"call request"
)
self
.
logger
.
info
(
"call request"
)
params
=
ua
.
CallParameters
.
from_binary
(
body
)
params
=
ua
.
CallParameters
.
from_binary
(
body
)
results
=
self
.
session
.
call
(
params
.
MethodsToCall
)
results
=
self
.
session
.
call
(
params
.
MethodsToCall
)
response
=
ua
.
CallResponse
()
response
=
ua
.
CallResponse
()
...
@@ -338,13 +342,11 @@ class UAProcessor(object):
...
@@ -338,13 +342,11 @@ 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
self
.
channel
.
SecurityToken
.
CreatedAt
=
datetime
.
now
()
self
.
channel
.
SecurityToken
.
CreatedAt
=
datetime
.
now
()
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
import
io
import
io
import
struct
import
struct
import
logging
import
logging
import
opcua.uaprotocol_auto
as
auto
import
opcua.uaprotocol_auto
as
auto
import
opcua.uatypes
as
uatypes
import
opcua.uatypes
as
uatypes
import
opcua.utils
as
utils
import
opcua.utils
as
utils
from
opcua.object_ids
import
ObjectIds
from
opcua.object_ids
import
ObjectIds
from
opcua.attribute_ids
import
AttributeIds
from
opcua.attribute_ids
import
AttributeIds
...
@@ -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
...
@@ -10,22 +10,25 @@ if sys.version_info.major > 2:
...
@@ -10,22 +10,25 @@ if sys.version_info.major > 2:
unicode
=
str
unicode
=
str
import
uuid
import
uuid
import
struct
import
struct
import
opcua.status_code
as
status_code
import
opcua.status_code
as
status_code
from
opcua.object_ids
import
ObjectIds
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,11 +203,12 @@ class FrozenClass(object):
...
@@ -189,11 +203,12 @@ class FrozenClass(object):
class
Guid
(
object
):
class
Guid
(
object
):
def
__init__
(
self
):
def
__init__
(
self
):
self
.
uuid
=
uuid
.
uuid4
()
self
.
uuid
=
uuid
.
uuid4
()
def
to_binary
(
self
):
def
to_binary
(
self
):
return
self
.
uuid
.
bytes
return
self
.
uuid
.
bytes
@
staticmethod
@
staticmethod
def
from_binary
(
data
):
def
from_binary
(
data
):
...
@@ -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
)
...
@@ -212,7 +229,7 @@ class StatusCode(object):
...
@@ -212,7 +229,7 @@ class StatusCode(object):
def
to_binary
(
self
):
def
to_binary
(
self
):
return
struct
.
pack
(
"<I"
,
self
.
value
)
return
struct
.
pack
(
"<I"
,
self
.
value
)
@
staticmethod
@
staticmethod
def
from_binary
(
data
):
def
from_binary
(
data
):
val
=
struct
.
unpack
(
"<I"
,
data
.
read
(
4
))[
0
]
val
=
struct
.
unpack
(
"<I"
,
data
.
read
(
4
))[
0
]
sc
=
StatusCode
(
val
)
sc
=
StatusCode
(
val
)
...
@@ -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
:
...
@@ -318,9 +336,9 @@ class NodeId(object):
...
@@ -318,9 +336,9 @@ class NodeId(object):
elif
self
.
NodeIdType
==
NodeIdType
.
String
:
elif
self
.
NodeIdType
==
NodeIdType
.
String
:
ntype
=
"s"
ntype
=
"s"
elif
self
.
NodeIdType
==
NodeIdType
.
TwoByte
:
elif
self
.
NodeIdType
==
NodeIdType
.
TwoByte
:
ntype
=
"i"
ntype
=
"i"
elif
self
.
NodeIdType
==
NodeIdType
.
FourByte
:
elif
self
.
NodeIdType
==
NodeIdType
.
FourByte
:
ntype
=
"i"
ntype
=
"i"
elif
self
.
NodeIdType
==
NodeIdType
.
Guid
:
elif
self
.
NodeIdType
==
NodeIdType
.
Guid
:
ntype
=
"g"
ntype
=
"g"
elif
self
.
NodeIdType
==
NodeIdType
.
ByteString
:
elif
self
.
NodeIdType
==
NodeIdType
.
ByteString
:
...
@@ -330,7 +348,7 @@ class NodeId(object):
...
@@ -330,7 +348,7 @@ class NodeId(object):
string
=
"srv="
+
str
(
self
.
ServerIndex
)
+
string
string
=
"srv="
+
str
(
self
.
ServerIndex
)
+
string
if
self
.
NamespaceUri
:
if
self
.
NamespaceUri
:
string
+=
"nsu={}"
.
format
(
self
.
NamespaceUri
)
string
+=
"nsu={}"
.
format
(
self
.
NamespaceUri
)
return
string
return
string
def
__str__
(
self
):
def
__str__
(
self
):
return
"NodeId({})"
.
format
(
self
.
to_string
())
return
"NodeId({})"
.
format
(
self
.
to_string
())
...
@@ -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
...
@@ -432,14 +463,14 @@ class QualifiedName(object):
...
@@ -432,14 +463,14 @@ class QualifiedName(object):
idx
=
0
idx
=
0
name
=
string
name
=
string
return
QualifiedName
(
name
,
int
(
idx
))
return
QualifiedName
(
name
,
int
(
idx
))
def
to_binary
(
self
):
def
to_binary
(
self
):
packet
=
[]
packet
=
[]
fmt
=
'<H'
fmt
=
'<H'
packet
.
append
(
struct
.
pack
(
fmt
,
self
.
NamespaceIndex
))
packet
.
append
(
struct
.
pack
(
fmt
,
self
.
NamespaceIndex
))
packet
.
append
(
pack_string
(
self
.
Name
))
packet
.
append
(
pack_string
(
self
.
Name
))
return
b''
.
join
(
packet
)
return
b''
.
join
(
packet
)
@
staticmethod
@
staticmethod
def
from_binary
(
data
):
def
from_binary
(
data
):
obj
=
QualifiedName
()
obj
=
QualifiedName
()
...
@@ -447,38 +478,43 @@ class QualifiedName(object):
...
@@ -447,38 +478,43 @@ class QualifiedName(object):
obj
.
NamespaceIndex
=
struct
.
unpack
(
fmt
,
data
.
read
(
2
))[
0
]
obj
.
NamespaceIndex
=
struct
.
unpack
(
fmt
,
data
.
read
(
2
))[
0
]
obj
.
Name
=
unpack_string
(
data
)
obj
.
Name
=
unpack_string
(
data
)
return
obj
return
obj
def
__eq__
(
self
,
bname
):
def
__eq__
(
self
,
bname
):
return
isinstance
(
bname
,
QualifiedName
)
and
self
.
Name
==
bname
.
Name
and
self
.
NamespaceIndex
==
bname
.
NamespaceIndex
return
isinstance
(
bname
,
QualifiedName
)
and
self
.
Name
==
bname
.
Name
and
self
.
NamespaceIndex
==
bname
.
NamespaceIndex
def
__str__
(
self
):
def
__str__
(
self
):
return
'QualifiedName({}:{})'
.
format
(
self
.
NamespaceIndex
,
self
.
Name
)
return
'QualifiedName({}:{})'
.
format
(
self
.
NamespaceIndex
,
self
.
Name
)
__repr__
=
__str__
__repr__
=
__str__
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
))
if
self
.
Text
:
if
self
.
Text
:
packet
.
append
(
pack_uatype
(
'CharArray'
,
self
.
Text
))
packet
.
append
(
pack_uatype
(
'CharArray'
,
self
.
Text
))
return
b''
.
join
(
packet
)
return
b''
.
join
(
packet
)
@
staticmethod
@
staticmethod
def
from_binary
(
data
):
def
from_binary
(
data
):
obj
=
LocalizedText
()
obj
=
LocalizedText
()
...
@@ -488,11 +524,11 @@ class LocalizedText(FrozenClass):
...
@@ -488,11 +524,11 @@ class LocalizedText(FrozenClass):
if
obj
.
Encoding
&
(
1
<<
1
):
if
obj
.
Encoding
&
(
1
<<
1
):
obj
.
Text
=
unpack_uatype
(
'CharArray'
,
data
)
obj
.
Text
=
unpack_uatype
(
'CharArray'
,
data
)
return
obj
return
obj
def
__str__
(
self
):
def
__str__
(
self
):
return
'LocalizedText('
+
'Encoding:'
+
str
(
self
.
Encoding
)
+
', '
+
\
return
'LocalizedText('
+
'Encoding:'
+
str
(
self
.
Encoding
)
+
', '
+
\
'Locale:'
+
str
(
self
.
Locale
)
+
', '
+
\
'Locale:'
+
str
(
self
.
Locale
)
+
', '
+
\
'Text:'
+
str
(
self
.
Text
)
+
')'
'Text:'
+
str
(
self
.
Text
)
+
')'
__repr__
=
__str__
__repr__
=
__str__
def
__eq__
(
self
,
other
):
def
__eq__
(
self
,
other
):
...
@@ -502,23 +538,26 @@ class LocalizedText(FrozenClass):
...
@@ -502,23 +538,26 @@ 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
self
.
Body
=
b''
self
.
Body
=
b''
self
.
_freeze
()
self
.
_freeze
()
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
:
packet
.
append
(
pack_uatype
(
'ByteString'
,
self
.
Body
))
packet
.
append
(
pack_uatype
(
'ByteString'
,
self
.
Body
))
return
b''
.
join
(
packet
)
return
b''
.
join
(
packet
)
@
staticmethod
@
staticmethod
def
from_binary
(
data
):
def
from_binary
(
data
):
obj
=
ExtensionObject
()
obj
=
ExtensionObject
()
...
@@ -538,13 +577,14 @@ class ExtensionObject(FrozenClass):
...
@@ -538,13 +577,14 @@ class ExtensionObject(FrozenClass):
def
__str__
(
self
):
def
__str__
(
self
):
return
'ExtensionObject('
+
'TypeId:'
+
str
(
self
.
TypeId
)
+
', '
+
\
return
'ExtensionObject('
+
'TypeId:'
+
str
(
self
.
TypeId
)
+
', '
+
\
'Encoding:'
+
str
(
self
.
Encoding
)
+
', '
+
\
'Encoding:'
+
str
(
self
.
Encoding
)
+
', '
+
\
'Body:'
+
str
(
self
.
Body
)
+
')'
'Body:'
+
str
(
self
.
Body
)
+
')'
__repr__
=
__str__
__repr__
=
__str__
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,44 +699,52 @@ class Variant(object):
...
@@ -656,44 +699,52 @@ 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
:
packet
.
append
(
pack_uatype
(
'UInt16'
,
self
.
ServerPicoseconds
))
packet
.
append
(
pack_uatype
(
'UInt16'
,
self
.
ServerPicoseconds
))
return
b''
.
join
(
packet
)
return
b''
.
join
(
packet
)
@
staticmethod
@
staticmethod
def
from_binary
(
data
):
def
from_binary
(
data
):
obj
=
DataValue
()
obj
=
DataValue
()
...
@@ -703,27 +754,22 @@ class DataValue(object):
...
@@ -703,27 +754,22 @@ 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
):
obj
.
ServerPicoseconds
=
unpack_uatype
(
'UInt16'
,
data
)
obj
.
ServerPicoseconds
=
unpack_uatype
(
'UInt16'
,
data
)
return
obj
return
obj
def
__str__
(
self
):
def
__str__
(
self
):
return
'DataValue('
+
'Encoding:'
+
str
(
self
.
Encoding
)
+
', '
+
\
return
'DataValue('
+
'Encoding:'
+
str
(
self
.
Encoding
)
+
', '
+
\
'Value:'
+
str
(
self
.
Value
)
+
', '
+
\
'Value:'
+
str
(
self
.
Value
)
+
', '
+
\
'StatusCode:'
+
str
(
self
.
StatusCode
)
+
', '
+
\
'StatusCode:'
+
str
(
self
.
StatusCode
)
+
', '
+
\
'SourceTimestamp:'
+
str
(
self
.
SourceTimestamp
)
+
', '
+
\
'SourceTimestamp:'
+
str
(
self
.
SourceTimestamp
)
+
', '
+
\
'ServerTimestamp:'
+
str
(
self
.
ServerTimestamp
)
+
', '
+
\
'ServerTimestamp:'
+
str
(
self
.
ServerTimestamp
)
+
', '
+
\
'SourcePicoseconds:'
+
str
(
self
.
SourcePicoseconds
)
+
', '
+
\
'SourcePicoseconds:'
+
str
(
self
.
SourcePicoseconds
)
+
', '
+
\
'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,8 +58,9 @@ class MySubHandler():
...
@@ -52,8 +58,9 @@ 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
'''
'''
def
test_guid
(
self
):
def
test_guid
(
self
):
...
@@ -62,37 +69,37 @@ class Unit(unittest.TestCase):
...
@@ -62,37 +69,37 @@ class Unit(unittest.TestCase):
def
test_nodeid
(
self
):
def
test_nodeid
(
self
):
nid
=
ua
.
NodeId
()
nid
=
ua
.
NodeId
()
self
.
assertEqual
(
nid
.
NodeIdType
,
ua
.
NodeIdType
.
TwoByte
)
self
.
assertEqual
(
nid
.
NodeIdType
,
ua
.
NodeIdType
.
TwoByte
)
nid
=
ua
.
NodeId
(
446
,
3
,
ua
.
NodeIdType
.
FourByte
)
nid
=
ua
.
NodeId
(
446
,
3
,
ua
.
NodeIdType
.
FourByte
)
self
.
assertEqual
(
nid
.
NodeIdType
,
ua
.
NodeIdType
.
FourByte
)
self
.
assertEqual
(
nid
.
NodeIdType
,
ua
.
NodeIdType
.
FourByte
)
d
=
nid
.
to_binary
()
d
=
nid
.
to_binary
()
new_nid
=
nid
.
from_binary
(
io
.
BytesIO
(
d
))
new_nid
=
nid
.
from_binary
(
io
.
BytesIO
(
d
))
self
.
assertEqual
(
new_nid
,
nid
)
self
.
assertEqual
(
new_nid
,
nid
)
self
.
assertEqual
(
new_nid
.
NodeIdType
,
ua
.
NodeIdType
.
FourByte
)
self
.
assertEqual
(
new_nid
.
NodeIdType
,
ua
.
NodeIdType
.
FourByte
)
self
.
assertEqual
(
new_nid
.
Identifier
,
446
)
self
.
assertEqual
(
new_nid
.
Identifier
,
446
)
self
.
assertEqual
(
new_nid
.
NamespaceIndex
,
3
)
self
.
assertEqual
(
new_nid
.
NamespaceIndex
,
3
)
tb
=
ua
.
TwoByteNodeId
(
53
)
tb
=
ua
.
TwoByteNodeId
(
53
)
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
()
g
=
ua
.
ByteStringNodeId
(
gid
,
0
)
g
=
ua
.
ByteStringNodeId
(
gid
,
0
)
self
.
assertEqual
(
tb
,
fb
)
self
.
assertEqual
(
tb
,
fb
)
self
.
assertEqual
(
tb
,
n
)
self
.
assertEqual
(
tb
,
n
)
self
.
assertEqual
(
tb
,
n1
)
self
.
assertEqual
(
tb
,
n1
)
self
.
assertEqual
(
n1
,
fb
)
self
.
assertEqual
(
n1
,
fb
)
self
.
assertNotEqual
(
n1
,
s
)
self
.
assertNotEqual
(
n1
,
s
)
self
.
assertNotEqual
(
s
,
bs
)
self
.
assertNotEqual
(
s
,
bs
)
self
.
assertNotEqual
(
s
,
g
)
self
.
assertNotEqual
(
s
,
g
)
def
test_nodeid_string
(
self
):
def
test_nodeid_string
(
self
):
nid0
=
ua
.
NodeId
(
45
)
nid0
=
ua
.
NodeId
(
45
)
self
.
assertEqual
(
nid0
,
ua
.
NodeId
.
from_string
(
"i=45"
))
self
.
assertEqual
(
nid0
,
ua
.
NodeId
.
from_string
(
"i=45"
))
self
.
assertEqual
(
nid0
,
ua
.
NodeId
.
from_string
(
"ns=0;i=45"
))
self
.
assertEqual
(
nid0
,
ua
.
NodeId
.
from_string
(
"ns=0;i=45"
))
nid
=
ua
.
NodeId
(
45
,
10
)
nid
=
ua
.
NodeId
(
45
,
10
)
self
.
assertEqual
(
nid
,
ua
.
NodeId
.
from_string
(
"i=45; ns=10"
))
self
.
assertEqual
(
nid
,
ua
.
NodeId
.
from_string
(
"i=45; ns=10"
))
self
.
assertNotEqual
(
nid
,
ua
.
NodeId
.
from_string
(
"i=45; ns=11"
))
self
.
assertNotEqual
(
nid
,
ua
.
NodeId
.
from_string
(
"i=45; ns=11"
))
...
@@ -102,7 +109,7 @@ class Unit(unittest.TestCase):
...
@@ -102,7 +109,7 @@ class Unit(unittest.TestCase):
def
test_expandednodeid
(
self
):
def
test_expandednodeid
(
self
):
nid
=
ua
.
ExpandedNodeId
()
nid
=
ua
.
ExpandedNodeId
()
self
.
assertEqual
(
nid
.
NodeIdType
,
ua
.
NodeIdType
.
TwoByte
)
self
.
assertEqual
(
nid
.
NodeIdType
,
ua
.
NodeIdType
.
TwoByte
)
nid2
=
ua
.
ExpandedNodeId
.
from_binary
(
ua
.
utils
.
Buffer
(
nid
.
to_binary
()))
nid2
=
ua
.
ExpandedNodeId
.
from_binary
(
ua
.
utils
.
Buffer
(
nid
.
to_binary
()))
self
.
assertEqual
(
nid
,
nid2
)
self
.
assertEqual
(
nid
,
nid2
)
...
@@ -111,7 +118,7 @@ class Unit(unittest.TestCase):
...
@@ -111,7 +118,7 @@ class Unit(unittest.TestCase):
obj2
=
ua
.
ExtensionObject
.
from_binary
(
ua
.
utils
.
Buffer
(
obj
.
to_binary
()))
obj2
=
ua
.
ExtensionObject
.
from_binary
(
ua
.
utils
.
Buffer
(
obj
.
to_binary
()))
self
.
assertEqual
(
obj2
.
TypeId
,
obj2
.
TypeId
)
self
.
assertEqual
(
obj2
.
TypeId
,
obj2
.
TypeId
)
self
.
assertEqual
(
obj2
.
Body
,
obj2
.
Body
)
self
.
assertEqual
(
obj2
.
Body
,
obj2
.
Body
)
def
test_datetime
(
self
):
def
test_datetime
(
self
):
now
=
datetime
.
now
()
now
=
datetime
.
now
()
epch
=
ua
.
datetime_to_win_epoch
(
now
)
epch
=
ua
.
datetime_to_win_epoch
(
now
)
...
@@ -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,25 +157,24 @@ class Unit(unittest.TestCase):
...
@@ -150,25 +157,24 @@ 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'
)
#self.assertEqual(repr(nid), 'ns=2;s=PLC1.Manufacturer;')
#self.assertEqual(repr(nid), 'ns=2;s=PLC1.Manufacturer;')
def
test_qualified_name
(
self
):
def
test_qualified_name
(
self
):
qn
=
ua
.
QualifiedName
(
'qname'
,
2
)
qn
=
ua
.
QualifiedName
(
'qname'
,
2
)
self
.
assertEqual
(
qn
.
NamespaceIndex
,
2
)
self
.
assertEqual
(
qn
.
NamespaceIndex
,
2
)
self
.
assertEqual
(
qn
.
Name
,
'qname'
)
self
.
assertEqual
(
qn
.
Name
,
'qname'
)
self
.
assertEqual
(
qn
.
to_string
(),
'2:qname'
)
self
.
assertEqual
(
qn
.
to_string
(),
'2:qname'
)
def
test_datavalue
(
self
):
def
test_datavalue
(
self
):
dv
=
ua
.
DataValue
(
123
)
dv
=
ua
.
DataValue
(
123
)
self
.
assertEqual
(
dv
.
Value
,
ua
.
Variant
(
123
))
self
.
assertEqual
(
dv
.
Value
,
ua
.
Variant
(
123
))
self
.
assertEqual
(
type
(
dv
.
Value
),
ua
.
Variant
)
self
.
assertEqual
(
type
(
dv
.
Value
),
ua
.
Variant
)
dv
=
ua
.
DataValue
(
'abc'
)
dv
=
ua
.
DataValue
(
'abc'
)
self
.
assertEqual
(
dv
.
Value
,
ua
.
Variant
(
'abc'
))
self
.
assertEqual
(
dv
.
Value
,
ua
.
Variant
(
'abc'
))
now
=
datetime
.
now
()
now
=
datetime
.
now
()
dv
.
source_timestamp
=
now
dv
.
source_timestamp
=
now
def
test_variant
(
self
):
def
test_variant
(
self
):
...
@@ -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,10 +216,12 @@ class Unit(unittest.TestCase):
...
@@ -210,10 +216,12 @@ 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
same api on server and client side
same api on server and client side
'''
'''
...
@@ -221,13 +229,13 @@ class CommonTests(object):
...
@@ -221,13 +229,13 @@ class CommonTests(object):
root
=
self
.
opc
.
get_root_node
()
root
=
self
.
opc
.
get_root_node
()
self
.
assertEqual
(
ua
.
QualifiedName
(
'Root'
,
0
),
root
.
get_browse_name
())
self
.
assertEqual
(
ua
.
QualifiedName
(
'Root'
,
0
),
root
.
get_browse_name
())
self
.
assertEqual
(
ua
.
LocalizedText
(
'Root'
),
root
.
get_display_name
())
self
.
assertEqual
(
ua
.
LocalizedText
(
'Root'
),
root
.
get_display_name
())
nid
=
ua
.
NodeId
(
84
,
0
)
nid
=
ua
.
NodeId
(
84
,
0
)
self
.
assertEqual
(
nid
,
root
.
nodeid
)
self
.
assertEqual
(
nid
,
root
.
nodeid
)
def
test_objects
(
self
):
def
test_objects
(
self
):
objects
=
self
.
opc
.
get_objects_node
()
objects
=
self
.
opc
.
get_objects_node
()
self
.
assertEqual
(
ua
.
QualifiedName
(
'Objects'
,
0
),
objects
.
get_browse_name
())
self
.
assertEqual
(
ua
.
QualifiedName
(
'Objects'
,
0
),
objects
.
get_browse_name
())
nid
=
ua
.
NodeId
(
85
,
0
)
nid
=
ua
.
NodeId
(
85
,
0
)
self
.
assertEqual
(
nid
,
objects
.
nodeid
)
self
.
assertEqual
(
nid
,
objects
.
nodeid
)
def
test_browse
(
self
):
def
test_browse
(
self
):
...
@@ -265,7 +273,7 @@ class CommonTests(object):
...
@@ -265,7 +273,7 @@ class CommonTests(object):
time
.
sleep
(
0.1
)
time
.
sleep
(
0.1
)
sub
.
unsubscribe
(
handle
)
sub
.
unsubscribe
(
handle
)
sub
.
delete
()
sub
.
delete
()
def
test_subscribe_events
(
self
):
def
test_subscribe_events
(
self
):
sub
=
self
.
opc
.
create_subscription
(
100
,
sclt
)
sub
=
self
.
opc
.
create_subscription
(
100
,
sclt
)
handle
=
sub
.
subscribe_events
()
handle
=
sub
.
subscribe_events
()
...
@@ -278,9 +286,9 @@ class CommonTests(object):
...
@@ -278,9 +286,9 @@ class CommonTests(object):
#cond = msclt.setup()
#cond = msclt.setup()
sub
=
self
.
opc
.
create_subscription
(
100
,
msclt
)
sub
=
self
.
opc
.
create_subscription
(
100
,
msclt
)
handle
=
sub
.
subscribe_events
()
handle
=
sub
.
subscribe_events
()
ev
=
Event
(
self
.
srv
.
iserver
.
isession
)
ev
=
Event
(
self
.
srv
.
iserver
.
isession
)
msg
=
b"this is my msg "
msg
=
b"this is my msg "
ev
.
Message
.
Text
=
msg
ev
.
Message
.
Text
=
msg
tid
=
datetime
.
now
()
tid
=
datetime
.
now
()
ev
.
Time
=
tid
ev
.
Time
=
tid
...
@@ -288,23 +296,22 @@ class CommonTests(object):
...
@@ -288,23 +296,22 @@ class CommonTests(object):
#ev.source_name = "our server node"
#ev.source_name = "our server node"
ev
.
Severity
=
500
ev
.
Severity
=
500
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
):
...
@@ -334,29 +341,29 @@ class CommonTests(object):
...
@@ -334,29 +341,29 @@ class CommonTests(object):
objects
=
self
.
opc
.
get_objects_node
()
objects
=
self
.
opc
.
get_objects_node
()
v1
=
objects
.
add_variable
(
4
,
"test_datetime"
,
now
)
v1
=
objects
.
add_variable
(
4
,
"test_datetime"
,
now
)
tid
=
v1
.
get_value
()
tid
=
v1
.
get_value
()
self
.
assertEqual
(
now
,
tid
)
self
.
assertEqual
(
now
,
tid
)
def
test_add_numeric_variable
(
self
):
def
test_add_numeric_variable
(
self
):
objects
=
self
.
opc
.
get_objects_node
()
objects
=
self
.
opc
.
get_objects_node
()
v
=
objects
.
add_variable
(
'ns=3;i=888;'
,
'3:numericnodefromstring'
,
99
)
v
=
objects
.
add_variable
(
'ns=3;i=888;'
,
'3:numericnodefromstring'
,
99
)
nid
=
ua
.
NodeId
(
888
,
3
)
nid
=
ua
.
NodeId
(
888
,
3
)
qn
=
ua
.
QualifiedName
(
'numericnodefromstring'
,
3
)
qn
=
ua
.
QualifiedName
(
'numericnodefromstring'
,
3
)
self
.
assertEqual
(
nid
,
v
.
nodeid
)
self
.
assertEqual
(
nid
,
v
.
nodeid
)
self
.
assertEqual
(
qn
,
v
.
get_browse_name
())
self
.
assertEqual
(
qn
,
v
.
get_browse_name
())
def
test_add_string_variable
(
self
):
def
test_add_string_variable
(
self
):
objects
=
self
.
opc
.
get_objects_node
()
objects
=
self
.
opc
.
get_objects_node
()
v
=
objects
.
add_variable
(
'ns=3;s=stringid;'
,
'3:stringnodefromstring'
,
[
68
])
v
=
objects
.
add_variable
(
'ns=3;s=stringid;'
,
'3:stringnodefromstring'
,
[
68
])
nid
=
ua
.
NodeId
(
'stringid'
,
3
)
nid
=
ua
.
NodeId
(
'stringid'
,
3
)
qn
=
ua
.
QualifiedName
(
'stringnodefromstring'
,
3
)
qn
=
ua
.
QualifiedName
(
'stringnodefromstring'
,
3
)
self
.
assertEqual
(
nid
,
v
.
nodeid
)
self
.
assertEqual
(
nid
,
v
.
nodeid
)
self
.
assertEqual
(
qn
,
v
.
get_browse_name
())
self
.
assertEqual
(
qn
,
v
.
get_browse_name
())
def
test_add_string_array_variable
(
self
):
def
test_add_string_array_variable
(
self
):
objects
=
self
.
opc
.
get_objects_node
()
objects
=
self
.
opc
.
get_objects_node
()
v
=
objects
.
add_variable
(
'ns=3;s=stringarrayid;'
,
'9:stringarray'
,
[
'l'
,
'b'
])
v
=
objects
.
add_variable
(
'ns=3;s=stringarrayid;'
,
'9:stringarray'
,
[
'l'
,
'b'
])
nid
=
ua
.
NodeId
(
'stringarrayid'
,
3
)
nid
=
ua
.
NodeId
(
'stringarrayid'
,
3
)
qn
=
ua
.
QualifiedName
(
'stringarray'
,
9
)
qn
=
ua
.
QualifiedName
(
'stringarray'
,
9
)
self
.
assertEqual
(
nid
,
v
.
nodeid
)
self
.
assertEqual
(
nid
,
v
.
nodeid
)
self
.
assertEqual
(
qn
,
v
.
get_browse_name
())
self
.
assertEqual
(
qn
,
v
.
get_browse_name
())
val
=
v
.
get_value
()
val
=
v
.
get_value
()
...
@@ -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
()
...
@@ -446,13 +452,13 @@ class CommonTests(object):
...
@@ -446,13 +452,13 @@ class CommonTests(object):
v
=
o
.
add_variable
(
3
,
'VariableArrayValue'
,
[
1
,
2
,
3
])
v
=
o
.
add_variable
(
3
,
'VariableArrayValue'
,
[
1
,
2
,
3
])
v
.
set_value
([
1
])
v
.
set_value
([
1
])
val
=
v
.
get_value
()
val
=
v
.
get_value
()
self
.
assertEqual
([
1
],
val
)
self
.
assertEqual
([
1
],
val
)
def
test_subscription_data_change
(
self
):
def
test_subscription_data_change
(
self
):
'''
'''
test subscriptions. This is far too complicated for
test subscriptions. This is far too complicated for
a unittest but, setting up subscriptions requires a lot
a unittest but, setting up subscriptions requires a lot
of code, so when we first set it up, it is best
of code, so when we first set it up, it is best
to test as many things as possible
to test as many things as possible
'''
'''
msclt
=
MySubHandler
()
msclt
=
MySubHandler
()
...
@@ -467,34 +473,34 @@ class CommonTests(object):
...
@@ -467,34 +473,34 @@ class CommonTests(object):
handle1
=
sub
.
subscribe_data_change
(
v1
)
handle1
=
sub
.
subscribe_data_change
(
v1
)
# 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
sub
.
delete
()
sub
.
delete
()
with
self
.
assertRaises
(
Exception
):
with
self
.
assertRaises
(
Exception
):
sub
.
unsubscribe
(
handle1
)
# sub does not exist anymore
sub
.
unsubscribe
(
handle1
)
# sub does not exist anymore
def
test_subscribe_server_time
(
self
):
def
test_subscribe_server_time
(
self
):
msclt
=
MySubHandler
()
msclt
=
MySubHandler
()
...
@@ -503,10 +509,10 @@ class CommonTests(object):
...
@@ -503,10 +509,10 @@ class CommonTests(object):
sub
=
self
.
opc
.
create_subscription
(
200
,
msclt
)
sub
=
self
.
opc
.
create_subscription
(
200
,
msclt
)
handle
=
sub
.
subscribe_data_change
(
server_time_node
)
handle
=
sub
.
subscribe_data_change
(
server_time_node
)
clthandle
,
node
,
val
,
attr
=
msclt
.
future
.
result
()
clthandle
,
node
,
val
,
attr
=
msclt
.
future
.
result
()
self
.
assertEqual
(
node
,
server_time_node
)
self
.
assertEqual
(
node
,
server_time_node
)
delta
=
datetime
.
now
()
-
val
delta
=
datetime
.
now
()
-
val
self
.
assertTrue
(
delta
<
timedelta
(
seconds
=
1
))
self
.
assertTrue
(
delta
<
timedelta
(
seconds
=
1
))
sub
.
unsubscribe
(
handle
)
sub
.
unsubscribe
(
handle
)
...
@@ -514,11 +520,11 @@ class CommonTests(object):
...
@@ -514,11 +520,11 @@ class CommonTests(object):
def
test_use_namespace
(
self
):
def
test_use_namespace
(
self
):
idx
=
self
.
opc
.
get_namespace_index
(
"urn:freeopcua:python:server"
)
idx
=
self
.
opc
.
get_namespace_index
(
"urn:freeopcua:python:server"
)
self
.
assertEqual
(
idx
,
1
)
self
.
assertEqual
(
idx
,
1
)
root
=
self
.
opc
.
get_root_node
()
root
=
self
.
opc
.
get_root_node
()
myvar
=
root
.
add_variable
(
idx
,
'var_in_custom_namespace'
,
[
5
])
myvar
=
root
.
add_variable
(
idx
,
'var_in_custom_namespace'
,
[
5
])
myid
=
myvar
.
nodeid
myid
=
myvar
.
nodeid
self
.
assertEqual
(
idx
,
myid
.
NamespaceIndex
)
self
.
assertEqual
(
idx
,
myid
.
NamespaceIndex
)
def
test_method
(
self
):
def
test_method
(
self
):
o
=
self
.
opc
.
get_objects_node
()
o
=
self
.
opc
.
get_objects_node
()
...
@@ -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,18 +587,18 @@ def add_server_methods(srv):
...
@@ -585,18 +587,18 @@ 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
process using python Process module
process using python Process module
Tests that can only be run on client side must be defined here
Tests that can only be run on client side must be defined here
'''
'''
@
classmethod
@
classmethod
def
setUpClass
(
self
):
def
setUpClass
(
self
):
# start our own server
# start our own server
self
.
srv
=
Server
()
self
.
srv
=
Server
()
self
.
srv
.
set_endpoint
(
'opc.tcp://localhost:%d'
%
port_num1
)
self
.
srv
.
set_endpoint
(
'opc.tcp://localhost:%d'
%
port_num1
)
add_server_methods
(
self
.
srv
)
add_server_methods
(
self
.
srv
)
self
.
srv
.
start
()
self
.
srv
.
start
()
...
@@ -614,13 +616,13 @@ class TestClient(unittest.TestCase, CommonTests):
...
@@ -614,13 +616,13 @@ class TestClient(unittest.TestCase, CommonTests):
def
test_service_fault
(
self
):
def
test_service_fault
(
self
):
request
=
ua
.
ReadRequest
()
request
=
ua
.
ReadRequest
()
request
.
TypeId
=
ua
.
FourByteNodeId
(
999
)
# bad type!
request
.
TypeId
=
ua
.
FourByteNodeId
(
999
)
# bad type!
with
self
.
assertRaises
(
Exception
):
with
self
.
assertRaises
(
Exception
):
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
...
@@ -631,18 +633,17 @@ class TestServer(unittest.TestCase, CommonTests):
...
@@ -631,18 +633,17 @@ class TestServer(unittest.TestCase, CommonTests):
self
.
srv
.
set_endpoint
(
'opc.tcp://localhost:%d'
%
port_num2
)
self
.
srv
.
set_endpoint
(
'opc.tcp://localhost:%d'
%
port_num2
)
add_server_methods
(
self
.
srv
)
add_server_methods
(
self
.
srv
)
self
.
srv
.
start
()
self
.
srv
.
start
()
self
.
opc
=
self
.
srv
self
.
opc
=
self
.
srv
@
classmethod
@
classmethod
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
)
idx2
=
self
.
opc
.
get_namespace_index
(
uri
)
idx2
=
self
.
opc
.
get_namespace_index
(
uri
)
self
.
assertEqual
(
idx1
,
idx2
)
self
.
assertEqual
(
idx1
,
idx2
)
def
test_register_use_namespace
(
self
):
def
test_register_use_namespace
(
self
):
uri
=
'http://my_very_custom.Namespace.com'
uri
=
'http://my_very_custom.Namespace.com'
...
@@ -650,7 +651,7 @@ class TestServer(unittest.TestCase, CommonTests):
...
@@ -650,7 +651,7 @@ class TestServer(unittest.TestCase, CommonTests):
root
=
self
.
opc
.
get_root_node
()
root
=
self
.
opc
.
get_root_node
()
myvar
=
root
.
add_variable
(
idx
,
'var_in_custom_namespace'
,
[
5
])
myvar
=
root
.
add_variable
(
idx
,
'var_in_custom_namespace'
,
[
5
])
myid
=
myvar
.
nodeid
myid
=
myvar
.
nodeid
self
.
assertEqual
(
idx
,
myid
.
NamespaceIndex
)
self
.
assertEqual
(
idx
,
myid
.
NamespaceIndex
)
def
test_server_method
(
self
):
def
test_server_method
(
self
):
def
func
(
parent
,
variant
):
def
func
(
parent
,
variant
):
...
@@ -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