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
2566969b
Commit
2566969b
authored
Jan 18, 2016
by
ORD
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #114 from iirob/master
Server side deleting of nodes.
parents
6ee90273
96958774
Changes
12
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
261 additions
and
95 deletions
+261
-95
examples/example-client_deleting.py
examples/example-client_deleting.py
+101
-0
examples/example-server.py
examples/example-server.py
+1
-1
opcua/client/binary_client.py
opcua/client/binary_client.py
+2
-2
opcua/client/client.py
opcua/client/client.py
+3
-3
opcua/common/node.py
opcua/common/node.py
+1
-2
opcua/common/subscription.py
opcua/common/subscription.py
+12
-6
opcua/server/address_space.py
opcua/server/address_space.py
+70
-1
opcua/server/internal_server.py
opcua/server/internal_server.py
+11
-5
opcua/server/internal_subscription.py
opcua/server/internal_subscription.py
+15
-11
opcua/server/uaprocessor.py
opcua/server/uaprocessor.py
+12
-0
opcua/ua/uaprotocol_auto.py
opcua/ua/uaprotocol_auto.py
+23
-54
schemas/generate_model.py
schemas/generate_model.py
+10
-10
No files found.
examples/example-client_deleting.py
0 → 100644
View file @
2566969b
import
sys
sys
.
path
.
insert
(
0
,
".."
)
import
logging
import
time
try
:
from
IPython
import
embed
except
ImportError
:
import
code
def
embed
():
vars
=
globals
()
vars
.
update
(
locals
())
shell
=
code
.
InteractiveConsole
(
vars
)
shell
.
interact
()
from
opcua
import
Client
from
opcua
import
ua
class
SubHandler
(
object
):
"""
Subscription Handler. To receive events from server for a subscription
data_change and event methods are called directly from receiving thread.
Do not do expensive, slow or network operation there. Create another
thread if you need to do such a thing
"""
def
datachange_notification
(
self
,
node
,
val
,
data
):
print
(
"Python: New data change event"
,
node
,
val
)
def
event_notification
(
self
,
event
):
print
(
"Python: New event"
,
event
)
def
status_change_notification
(
self
,
status
):
print
(
"Python: New status change"
,
status
)
if
__name__
==
"__main__"
:
logging
.
basicConfig
(
level
=
logging
.
WARN
)
#logger = logging.getLogger("KeepAlive")
#logger.setLevel(logging.DEBUG)
#client = Client("opc.tcp://localhost:4840/freeopcua/server/")
client
=
Client
(
"opc.tcp://admin@localhost:4840/freeopcua/server/"
)
#connect using a user
try
:
client
.
connect
()
# Client has a few methods to get proxy to UA nodes that should always be in address space such as Root or Objects
root
=
client
.
get_root_node
()
print
(
"Root node is: "
,
root
)
objects
=
client
.
get_objects_node
()
print
(
"Objects node is: "
,
objects
)
# Node objects have methods to read and write node attributes as well as browse or populate address space
print
(
"Children of root are: "
,
root
.
get_children
())
# get a specific node knowing its node id
#var = client.get_node(ua.NodeId(1002, 2))
#var = client.get_node("ns=3;i=2002")
#print(var)
#var.get_data_value() # get value of node as a DataValue object
#var.get_value() # get value of node as a python builtin
#var.set_value(ua.Variant([23], ua.VariantType.Int64)) #set node value using explicit data type
#var.set_value(3.9) # set node value using implicit data type
# Now getting a variable node using its browse path
myvar
=
root
.
get_child
([
"0:Objects"
,
"2:MyObject"
,
"2:MyVariable"
])
obj
=
root
.
get_child
([
"0:Objects"
,
"2:MyObject"
])
print
(
"myvar is: "
,
myvar
)
# subscribing to a variable node
handler
=
SubHandler
()
sub
=
client
.
create_subscription
(
500
,
handler
)
handle
=
sub
.
subscribe_data_change
(
myvar
)
time
.
sleep
(
0.1
)
# we can also subscribe to events from server
sub
.
subscribe_events
()
# sub.unsubscribe(handle)
# sub.delete()
# calling a method on server
res
=
obj
.
call_method
(
"2:multiply"
,
3
,
"klk"
)
print
(
"method result is: "
,
res
)
print
(
"Children of MyObject are: "
,
obj
.
get_children
())
print
(
"myvar should be still there"
)
deletenode
=
ua
.
DeleteNodesItem
()
deletenode
.
NodeId
=
obj
.
get_child
([
"2:MyVariable"
]).
nodeid
deletenode
.
DeleteTargetReferences
=
True
results
=
client
.
bclient
.
delete_nodes
([
deletenode
])
results
[
0
].
check
()
print
(
"Children of MyObject are: "
,
obj
.
get_children
())
print
(
"myvar should disapear"
)
embed
()
finally
:
client
.
disconnect
()
examples/example-server.py
View file @
2566969b
opcua/client/binary_client.py
View file @
2566969b
...
...
@@ -429,7 +429,7 @@ class BinaryClient(object):
request
=
ua
.
DeleteNodesRequest
()
request
.
Parameters
.
NodesToDelete
=
nodestodelete
data
=
self
.
_uasocket
.
send_request
(
request
)
response
=
ua
.
Add
NodesResponse
.
from_binary
(
data
)
response
=
ua
.
Delete
NodesResponse
.
from_binary
(
data
)
response
.
ResponseHeader
.
ServiceResult
.
check
()
return
response
.
Results
...
...
opcua/client/client.py
View file @
2566969b
opcua/common/node.py
View file @
2566969b
...
...
@@ -314,4 +314,3 @@ class Node(object):
def
call_method
(
*
args
,
**
kwargs
):
from
opcua.common
import
methods
return
methods
.
call_method
(
*
args
,
**
kwargs
)
opcua/common/subscription.py
View file @
2566969b
...
...
@@ -33,6 +33,12 @@ class SubHandler(object):
"""
pass
def
status_change_notification
(
self
,
status
):
"""
called for every status change notfication from server
"""
pass
def
event
(
self
,
handle
,
event
):
"""
Deprecated use event_notification
...
...
@@ -176,7 +182,7 @@ class Subscription(object):
def
_call_status
(
self
,
status
):
try
:
self
.
_handler
.
status_change
(
status
.
Status
)
self
.
_handler
.
status_change
_notification
(
status
.
Status
)
except
Exception
:
self
.
logger
.
exception
(
"Exception calling status change handler"
)
...
...
opcua/server/address_space.py
View file @
2566969b
...
...
@@ -181,7 +181,7 @@ class NodeManagementService(object):
result
=
ua
.
AddNodesResult
()
if
item
.
RequestedNewNodeId
in
self
.
_aspace
:
self
.
logger
.
warning
(
"AddNodeItem: node already exists"
)
self
.
logger
.
warning
(
"AddNode
s
Item: node already exists"
)
result
.
StatusCode
=
ua
.
StatusCode
(
ua
.
StatusCodes
.
BadNodeIdExists
)
return
result
nodedata
=
NodeData
(
item
.
RequestedNewNodeId
)
...
...
@@ -233,6 +233,37 @@ class NodeManagementService(object):
return
result
def
delete_nodes
(
self
,
deletenodeitems
,
user
=
User
.
Admin
):
results
=
[]
for
item
in
deletenodeitems
:
results
.
append
(
self
.
_delete_node
(
item
,
user
))
return
results
def
_delete_node
(
self
,
item
,
user
):
if
not
user
==
User
.
Admin
:
return
ua
.
StatusCode
(
ua
.
StatusCodes
.
BadUserAccessDenied
)
if
item
.
NodeId
not
in
self
.
_aspace
:
self
.
logger
.
warning
(
"DeleteNodesItem: node does not exists"
)
return
ua
.
StatusCode
(
ua
.
StatusCodes
.
BadNodeIdUnknown
)
if
item
.
DeleteTargetReferences
:
for
elem
in
self
.
_aspace
.
keys
():
for
rdesc
in
self
.
_aspace
[
elem
].
references
:
if
rdesc
.
NodeId
==
item
.
NodeId
:
self
.
_aspace
[
elem
].
references
.
remove
(
rdesc
)
for
handle
,
callback
in
list
(
self
.
_aspace
[
item
.
NodeId
].
attributes
[
ua
.
AttributeIds
.
Value
].
datachange_callbacks
.
items
()):
try
:
callback
(
handle
,
None
,
ua
.
StatusCode
(
ua
.
StatusCodes
.
BadNodeIdUnknown
))
self
.
_aspace
.
delete_datachange_callback
(
handle
)
except
Exception
as
ex
:
self
.
logger
.
exception
(
"Error calling datachange callback %s, %s, %s"
,
k
,
v
,
ex
)
del
self
.
_aspace
[
item
.
NodeId
]
return
ua
.
StatusCode
()
def
add_references
(
self
,
refs
,
user
=
User
.
Admin
):
result
=
[]
for
ref
in
refs
:
...
...
@@ -260,6 +291,36 @@ class NodeManagementService(object):
self
.
_aspace
[
addref
.
SourceNodeId
].
references
.
append
(
rdesc
)
return
ua
.
StatusCode
()
def
delete_references
(
self
,
refs
,
user
=
User
.
Admin
):
result
=
[]
for
ref
in
refs
:
result
.
append
(
self
.
_delete_reference
(
ref
,
user
))
return
result
def
_delete_reference
(
self
,
item
,
user
):
if
item
.
SourceNodeId
not
in
self
.
_aspace
:
return
ua
.
StatusCode
(
ua
.
StatusCodes
.
BadSourceNodeIdInvalid
)
if
item
.
TargetNodeId
not
in
self
.
_aspace
:
return
ua
.
StatusCode
(
ua
.
StatusCodes
.
BadTargetNodeIdInvalid
)
if
not
user
==
User
.
Admin
:
return
ua
.
StatusCode
(
ua
.
StatusCodes
.
BadUserAccessDenied
)
for
rdesc
in
self
.
_aspace
[
item
.
SourceNodeId
].
references
:
if
rdesc
.
NodeId
is
item
.
TargetNodeId
:
if
rdesc
.
RefrenceTypeId
!=
item
.
RefrenceTypeId
:
return
ua
.
StatusCode
(
ua
.
StatusCode
.
BadReferenceTypeInvalid
)
if
rdesc
.
IsForward
==
item
.
IsForward
or
item
.
DeleteBidirectional
:
self
.
_aspace
[
item
.
SourceNodeId
].
references
.
remove
(
rdesc
)
for
rdesc
in
self
.
_aspace
[
item
.
TargetNodeId
].
references
:
if
rdesc
.
NodeId
is
item
.
SourceNodeId
:
if
rdesc
.
RefrenceTypeId
!=
item
.
RefrenceTypeId
:
return
ua
.
StatusCode
(
ua
.
StatusCode
.
BadReferenceTypeInvalid
)
if
rdesc
.
IsForward
==
item
.
IsForward
or
item
.
DeleteBidirectional
:
self
.
_aspace
[
item
.
SourceNodeId
].
references
.
remove
(
rdesc
)
return
ua
.
StatusCode
()
def
_add_node_attr
(
self
,
item
,
nodedata
,
name
,
vtype
=
None
):
if
item
.
SpecifiedAttributes
&
getattr
(
ua
.
NodeAttributesMask
,
name
):
dv
=
ua
.
DataValue
(
ua
.
Variant
(
getattr
(
item
,
name
),
vtype
))
...
...
@@ -351,6 +412,14 @@ class AddressSpace(object):
with
self
.
_lock
:
return
self
.
_nodes
.
__contains__
(
nodeid
)
def
__delitem__
(
self
,
nodeid
):
with
self
.
_lock
:
self
.
_nodes
.
__delitem__
(
nodeid
)
def
keys
(
self
):
with
self
.
_lock
:
return
self
.
_nodes
.
keys
()
def
dump
(
self
,
path
):
"""
dump address space as binary to file
...
...
opcua/server/internal_server.py
View file @
2566969b
...
...
@@ -231,9 +231,15 @@ class InternalSession(object):
def
add_nodes
(
self
,
params
):
return
self
.
iserver
.
node_mgt_service
.
add_nodes
(
params
,
self
.
user
)
def
delete_nodes
(
self
,
params
):
return
self
.
iserver
.
node_mgt_service
.
delete_nodes
(
params
,
self
.
user
)
def
add_references
(
self
,
params
):
return
self
.
iserver
.
node_mgt_service
.
add_references
(
params
,
self
.
user
)
def
delete_references
(
self
,
params
):
return
self
.
iserver
.
node_mgt_service
.
delete_references
(
params
,
self
.
user
)
def
add_method_callback
(
self
,
methodid
,
callback
):
return
self
.
aspace
.
add_method_callback
(
methodid
,
callback
)
...
...
opcua/server/internal_subscription.py
View file @
2566969b
...
...
@@ -137,7 +137,11 @@ class MonitoredItemService(object):
self
.
_monitored_items
.
pop
(
mid
)
return
ua
.
StatusCode
()
def
datachange_callback
(
self
,
handle
,
value
):
def
datachange_callback
(
self
,
handle
,
value
,
error
=
None
):
if
error
:
self
.
logger
.
info
(
"subscription %s: datachange callback called with handle '%s' and erorr '%s'"
,
self
,
handle
,
error
)
self
.
trigger_statuschange
(
error
)
else
:
self
.
logger
.
info
(
"subscription %s: datachange callback called with handle '%s' and value '%s'"
,
self
,
handle
,
value
.
Value
)
event
=
ua
.
MonitoredItemNotification
()
with
self
.
_lock
:
...
...
@@ -289,7 +293,7 @@ class InternalSubscription(object):
notif
=
ua
.
StatusChangeNotification
()
notif
.
Status
=
self
.
_triggered_statuschanges
.
pop
(
0
)
result
.
NotificationMessage
.
NotificationData
.
append
(
notif
)
self
.
logger
.
debug
(
"sending event notification %s"
,
len
(
notif
.
Status
)
)
self
.
logger
.
debug
(
"sending event notification %s"
,
notif
.
Status
)
def
publish
(
self
,
nb
):
with
self
.
_lock
:
...
...
opcua/server/uaprocessor.py
View file @
2566969b
...
...
@@ -256,6 +256,18 @@ class UAProcessor(object):
self
.
logger
.
info
(
"sending add node response"
)
self
.
send_response
(
requesthdr
.
RequestHandle
,
algohdr
,
seqhdr
,
response
)
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
DeleteNodesRequest_Encoding_DefaultBinary
):
self
.
logger
.
info
(
"delete nodes request"
)
params
=
ua
.
DeleteNodesParameters
.
from_binary
(
body
)
results
=
self
.
session
.
delete_nodes
(
params
.
NodesToDelete
)
response
=
ua
.
DeleteNodesResponse
()
response
.
Results
=
results
self
.
logger
.
info
(
"sending delete node response"
)
self
.
send_response
(
requesthdr
.
RequestHandle
,
algohdr
,
seqhdr
,
response
)
elif
typeid
==
ua
.
NodeId
(
ua
.
ObjectIds
.
CreateSubscriptionRequest_Encoding_DefaultBinary
):
self
.
logger
.
info
(
"create subscription request"
)
params
=
ua
.
CreateSubscriptionParameters
.
from_binary
(
body
)
...
...
opcua/ua/uaprotocol_auto.py
View file @
2566969b
...
...
@@ -4930,8 +4930,14 @@ class DeleteNodesRequest(FrozenClass):
__repr__
=
__str__
class
DeleteNodesRes
ult
(
FrozenClass
):
class
DeleteNodesRes
ponse
(
FrozenClass
):
'''
Delete one or more nodes from the server address space.
:ivar TypeId:
:vartype TypeId: NodeId
:ivar ResponseHeader:
:vartype ResponseHeader: ResponseHeader
:ivar Results:
:vartype Results: StatusCode
:ivar DiagnosticInfos:
...
...
@@ -4942,12 +4948,16 @@ class DeleteNodesResult(FrozenClass):
self
.
_binary_init
(
binary
)
self
.
_freeze
=
True
return
self
.
TypeId
=
FourByteNodeId
(
ObjectIds
.
DeleteNodesResponse_Encoding_DefaultBinary
)
self
.
ResponseHeader
=
ResponseHeader
()
self
.
Results
=
[]
self
.
DiagnosticInfos
=
[]
self
.
_freeze
=
True
def
to_binary
(
self
):
packet
=
[]
packet
.
append
(
self
.
TypeId
.
to_binary
())
packet
.
append
(
self
.
ResponseHeader
.
to_binary
())
packet
.
append
(
uatype_Int32
.
pack
(
len
(
self
.
Results
)))
for
fieldname
in
self
.
Results
:
packet
.
append
(
fieldname
.
to_binary
())
...
...
@@ -4958,9 +4968,11 @@ class DeleteNodesResult(FrozenClass):
@
staticmethod
def
from_binary
(
data
):
return
DeleteNodesRes
ult
(
data
)
return
DeleteNodesRes
ponse
(
data
)
def
_binary_init
(
self
,
data
):
self
.
TypeId
=
NodeId
.
from_binary
(
data
)
self
.
ResponseHeader
=
ResponseHeader
.
from_binary
(
data
)
length
=
uatype_Int32
.
unpack
(
data
.
read
(
4
))[
0
]
array
=
[]
if
length
!=
-
1
:
...
...
@@ -4974,54 +4986,11 @@ class DeleteNodesResult(FrozenClass):
array
.
append
(
DiagnosticInfo
.
from_binary
(
data
))
self
.
DiagnosticInfos
=
array
def
__str__
(
self
):
return
'DeleteNodesResult('
+
'Results:'
+
str
(
self
.
Results
)
+
', '
+
\
'DiagnosticInfos:'
+
str
(
self
.
DiagnosticInfos
)
+
')'
__repr__
=
__str__
class
DeleteNodesResponse
(
FrozenClass
):
'''
Delete one or more nodes from the server address space.
:ivar TypeId:
:vartype TypeId: NodeId
:ivar ResponseHeader:
:vartype ResponseHeader: ResponseHeader
:ivar Parameters:
:vartype Parameters: DeleteNodesResult
'''
def
__init__
(
self
,
binary
=
None
):
if
binary
is
not
None
:
self
.
_binary_init
(
binary
)
self
.
_freeze
=
True
return
self
.
TypeId
=
FourByteNodeId
(
ObjectIds
.
DeleteNodesResponse_Encoding_DefaultBinary
)
self
.
ResponseHeader
=
ResponseHeader
()
self
.
Parameters
=
DeleteNodesResult
()
self
.
_freeze
=
True
def
to_binary
(
self
):
packet
=
[]
packet
.
append
(
self
.
TypeId
.
to_binary
())
packet
.
append
(
self
.
ResponseHeader
.
to_binary
())
packet
.
append
(
self
.
Parameters
.
to_binary
())
return
b''
.
join
(
packet
)
@
staticmethod
def
from_binary
(
data
):
return
DeleteNodesResponse
(
data
)
def
_binary_init
(
self
,
data
):
self
.
TypeId
=
NodeId
.
from_binary
(
data
)
self
.
ResponseHeader
=
ResponseHeader
.
from_binary
(
data
)
self
.
Parameters
=
DeleteNodesResult
.
from_binary
(
data
)
def
__str__
(
self
):
return
'DeleteNodesResponse('
+
'TypeId:'
+
str
(
self
.
TypeId
)
+
', '
+
\
'ResponseHeader:'
+
str
(
self
.
ResponseHeader
)
+
', '
+
\
'Parameters:'
+
str
(
self
.
Parameters
)
+
')'
'Results:'
+
str
(
self
.
Results
)
+
', '
+
\
'DiagnosticInfos:'
+
str
(
self
.
DiagnosticInfos
)
+
')'
__repr__
=
__str__
...
...
schemas/generate_model.py
View file @
2566969b
...
...
@@ -14,7 +14,7 @@ IgnoredEnums = []#["IdType", "NodeIdType"]
#we want to implement som struct by hand, to make better interface or simply because they are too complicated
IgnoredStructs
=
[]
#["NodeId", "ExpandedNodeId", "Variant", "QualifiedName", "DataValue", "LocalizedText"]#, "ExtensionObject"]
#by default we split requests and respons in header and parameters, but some are so simple we do not split them
NoSplitStruct
=
[
"GetEndpointsResponse"
,
"CloseSessionRequest"
,
"AddNodesResponse"
,
"BrowseResponse"
,
"HistoryReadResponse"
,
"HistoryUpdateResponse"
,
"RegisterServerResponse"
,
"CloseSecureChannelRequest"
,
"CloseSecureChannelResponse"
,
"CloseSessionRequest"
,
"CloseSessionResponse"
,
"UnregisterNodesResponse"
,
"MonitoredItemModifyRequest"
,
"MonitoredItemsCreateRequest"
,
"ReadResponse"
,
"WriteResponse"
,
"TranslateBrowsePathsToNodeIdsResponse"
,
"DeleteSubscriptionsResponse"
,
"DeleteMonitoredItemsResponse"
,
"CreateMonitoredItemsResponse"
,
"ServiceFault"
,
"AddReferencesRequest"
,
"AddReferencesResponse"
,
"ModifyMonitoredItemsResponse"
,
"RepublishResponse"
,
"CallResponse"
,
"FindServersResponse"
,
"RegisterServerRequest"
,
"RegisterServer2Response"
]
NoSplitStruct
=
[
"GetEndpointsResponse"
,
"CloseSessionRequest"
,
"AddNodesResponse"
,
"
DeleteNodesResponse"
,
"
BrowseResponse"
,
"HistoryReadResponse"
,
"HistoryUpdateResponse"
,
"RegisterServerResponse"
,
"CloseSecureChannelRequest"
,
"CloseSecureChannelResponse"
,
"CloseSessionRequest"
,
"CloseSessionResponse"
,
"UnregisterNodesResponse"
,
"MonitoredItemModifyRequest"
,
"MonitoredItemsCreateRequest"
,
"ReadResponse"
,
"WriteResponse"
,
"TranslateBrowsePathsToNodeIdsResponse"
,
"DeleteSubscriptionsResponse"
,
"DeleteMonitoredItemsResponse"
,
"CreateMonitoredItemsResponse"
,
"ServiceFault"
,
"AddReferencesRequest"
,
"AddReferencesResponse"
,
"ModifyMonitoredItemsResponse"
,
"RepublishResponse"
,
"CallResponse"
,
"FindServersResponse"
,
"RegisterServerRequest"
,
"RegisterServer2Response"
]
#structs that end with Request or Response but are not
NotRequest
=
[
"MonitoredItemCreateRequest"
,
"MonitoredItemModifyRequest"
,
"CallMethodRequest"
]
OverrideTypes
=
{}
#AttributeId": "AttributeID", "ResultMask": "BrowseResultMask", "NodeClassMask": "NodeClass", "AccessLevel": "VariableAccessLevel", "UserAccessLevel": "VariableAccessLevel", "NotificationData": "NotificationData"}
...
...
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