Skip to content

Commit 6290f0f

Browse files
committed
SQLite address space: added support to remove nodes
1 parent 2b91c94 commit 6290f0f

File tree

1 file changed

+79
-42
lines changed

1 file changed

+79
-42
lines changed

opcua/server/address_space_sqlite.py

Lines changed: 79 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11

22
import sys
33
import os.path
4-
import time
54
import datetime
65
import sqlite3
76
from struct import pack
@@ -12,6 +11,7 @@
1211
from opcua.common.sqlite3_backend import SQLite3Backend
1312
from opcua.server.address_space import NodeData, AddressSpace, AttributeValue
1413

14+
1515
class ReadOnlyException(Exception):
1616
pass
1717

@@ -33,6 +33,7 @@ def value(self, newVal):
3333
self._value = newVal
3434
self.onchange_cb()
3535

36+
3637
class MonitoredNode(object):
3738

3839
def __init__(self, aspace, ndata):
@@ -132,7 +133,7 @@ def _getitem_backend(self, nodeid):
132133
except KeyError:
133134
(nodeData, fromDisk) = (NodeData(nodeid), True)
134135
AddressSpaceSQLite._read_nodedata(self.backend, nodeid, nodeData)
135-
if len(nodeData.attributes) is 0:
136+
if len(nodeData.attributes) == 0:
136137
raise
137138
elif self.readonly is False:
138139
self._monitor_nodedata(nodeData)
@@ -190,15 +191,15 @@ def _nodeid_surjection(nodeid):
190191

191192
def keys(self):
192193
raise Exception("dict.keys() is not supported for performance. Use iterator.")
193-
194+
194195
def __delitem__(self, key):
195-
# TODO only deleting items from the cache is implemented.
196+
self._drop_nodedata(nodeid=key)
196197
super(AddressSpaceSQLite, self).__delitem__(key)
197-
198+
198199
def __iter__(self):
199200
# TODO only the cache can be iterated over.
200201
return super(AddressSpaceSQLite, self).__iter__()
201-
202+
202203
def __len__(self):
203204
# TODO only returns the length of items in the cache.
204205
return super(AddressSpaceSQLite, self).__len__()
@@ -262,6 +263,14 @@ def _write_references(self, nodeid, ndata, commit=True):
262263
for ref in ndata.references:
263264
AddressSpaceSQLite._insert_reference(self.backend, nodeid, ref, commit=commit)
264265

266+
# Remove NodeData from database
267+
def _drop_nodedata(self, nodeid, commit=True):
268+
assert(nodeid.NodeIdType == NodeIdType.Numeric)
269+
if self.readonly is True:
270+
return
271+
AddressSpaceSQLite._drop_attributes(self.backend, nodeid, commit=commit)
272+
AddressSpaceSQLite._drop_references(self.backend, nodeid, commit=commit)
273+
265274
# Read NodeData from database
266275
@staticmethod
267276
def _read_nodedata(backend, nodeid, ndata):
@@ -282,6 +291,7 @@ def _to_hex(b):
282291
@staticmethod
283292
def _read_attributes(backend, hexNodeId, ndata, attrTable=ATTR_TABLE_NAME):
284293
cmd = 'SELECT * FROM "{tn}" WHERE NodeId = x\'{h}\''.format(tn=attrTable, h=hexNodeId)
294+
285295
def CB(row):
286296
(attrId, attr) = AddressSpaceSQLite._read_attribute_row(row)
287297
ndata.attributes[attrId] = attr
@@ -290,6 +300,7 @@ def CB(row):
290300
@staticmethod
291301
def _read_references(backend, hexNodeId, ndata, refsTable=REFS_TABLE_NAME):
292302
cmd = 'SELECT * FROM "{tn}" WHERE NodeId = x\'{h}\''.format(tn=refsTable, h=hexNodeId)
303+
293304
def CB(row):
294305
ref = AddressSpaceSQLite._read_reference_row(row)
295306
ndata.references.append(ref)
@@ -300,16 +311,16 @@ def CB(row):
300311
def _create_attr_table(backend, table=ATTR_TABLE_NAME, drop=False):
301312
nid = AddressSpaceSQLite.NODEID_COL_NAME
302313
ATTR_COLS = [
303-
'_Id BLOB PRIMARY KEY NOT NULL', # 0
304-
'{:s} BLOB'.format(nid), # 1
305-
'AttributeId INTEGER', # 2
306-
'ServerTimestamp TIMESTAMP', # 3
307-
'ServerPicoseconds INTEGER', # 4
308-
'SourceTimestamp TIMESTAMP', # 5
309-
'SourcePicoseconds INTEGER', # 6
310-
'StatusCode INTEGER', # 7
311-
'Variant BLOB', # 8
312-
'Description STRING', # 9
314+
'_Id BLOB PRIMARY KEY NOT NULL', # 0
315+
'{:s} BLOB'.format(nid), # 1
316+
'AttributeId INTEGER', # 2
317+
'ServerTimestamp TIMESTAMP', # 3
318+
'ServerPicoseconds INTEGER', # 4
319+
'SourceTimestamp TIMESTAMP', # 5
320+
'SourcePicoseconds INTEGER', # 6
321+
'StatusCode INTEGER', # 7
322+
'Variant BLOB', # 8
323+
'Description STRING', # 9
313324
]
314325
AddressSpaceSQLite._create_indexed_table(backend, table, ATTR_COLS, drop)
315326

@@ -339,21 +350,21 @@ def _insert_attribute(backend, nodeid, attrId, attr, table=ATTR_TABLE_NAME, comm
339350
assert(isinstance(attrId, ua.AttributeIds))
340351
assert(isinstance(attr, AttributeValue))
341352
# Callback methods are not supported.
342-
assert(attr.value_callback is None)
353+
assert(attr.value_callback is None)
343354
# Datachange callbacks not supported and are ignored.
344355
assert(isinstance(attr.datachange_callbacks, dict))
345356
# DataValue has no opc-ua to_binary: flatten object.
346357
assert(isinstance(attr.value, ua.uatypes.DataValue))
347358
# Server timestamp
348-
assert(attr.value.ServerTimestamp is None or \
349-
isinstance(attr.value.ServerTimestamp, datetime.datetime))
350-
assert(attr.value.ServerPicoseconds is None or \
351-
isinstance(attr.value.ServerTimestamp, int))
359+
assert(attr.value.ServerTimestamp is None or
360+
isinstance(attr.value.ServerTimestamp, datetime.datetime))
361+
assert(attr.value.ServerPicoseconds is None or
362+
isinstance(attr.value.ServerTimestamp, int))
352363
# Source timestamp
353-
assert(attr.value.SourceTimestamp is None or \
354-
isinstance(attr.value.SourceTimestamp, datetime.datetime))
355-
assert(attr.value.SourcePicoseconds is None or \
356-
isinstance(attr.value.ServerTimestamp, int))
364+
assert(attr.value.SourceTimestamp is None or
365+
isinstance(attr.value.SourceTimestamp, datetime.datetime))
366+
assert(attr.value.SourcePicoseconds is None or
367+
isinstance(attr.value.ServerTimestamp, int))
357368
assert(isinstance(attr.value.StatusCode, ua.uatypes.StatusCode))
358369
assert(isinstance(attr.value.Value, ua.uatypes.Variant))
359370

@@ -375,6 +386,19 @@ def _insert_attribute(backend, nodeid, attrId, attr, table=ATTR_TABLE_NAME, comm
375386
)
376387
backend.execute_write(cmd, params=params, commit=commit)
377388

389+
@staticmethod
390+
def _drop_attributes(backend, nodeid, table=ATTR_TABLE_NAME, commit=True):
391+
assert(nodeid.NodeIdType == NodeIdType.Numeric)
392+
binNodeId = ua.ua_binary.nodeid_to_binary(nodeid)
393+
cmd = 'DELETE FROM "{tn}" WHERE {nid}=?'.format(
394+
tn=table,
395+
nid=AddressSpaceSQLite.NODEID_COL_NAME
396+
)
397+
params = (
398+
sqlite3.Binary(binNodeId),
399+
)
400+
backend.execute_write(cmd, params=params, commit=commit)
401+
378402
@staticmethod
379403
def _read_attribute_row(row):
380404
attrId = ua.AttributeIds(row[2])
@@ -397,19 +421,19 @@ def _read_attribute_row(row):
397421
def _create_refs_table(backend, table=REFS_TABLE_NAME, drop=False):
398422
nid = AddressSpaceSQLite.NODEID_COL_NAME
399423
REFS_COLS = [
400-
'_Id BLOB PRIMARY KEY NOT NULL', # 0
401-
'{:s} BLOB'.format(nid), # 1 = the nodeid of this ReferenceDescription
402-
'ReferenceTypeId BLOB', # 2
403-
'IsForward INTEGER', # 3
404-
'ReferredNodeId BLOB', # 4 = referred nodeid of ReferenceDescription
405-
'BrowseName_NamespaceIndex INTEGER', # 5
406-
'BrowseName_Name TEXT', # 6
407-
'DisplayName_Text TEXT', # 7
408-
'DisplayName_Locale TEXT', # 8
409-
'DisplayName_Encoding INTEGER', # 9
410-
'NodeClass INTEGER', # 10
411-
'TypeDefinition BLOB', # 11
412-
'Description STRING' # 12
424+
'_Id BLOB PRIMARY KEY NOT NULL', # 0
425+
'{:s} BLOB'.format(nid), # 1 = the nodeid of this ReferenceDescription
426+
'ReferenceTypeId BLOB', # 2
427+
'IsForward INTEGER', # 3
428+
'ReferredNodeId BLOB', # 4 = referred nodeid of ReferenceDescription
429+
'BrowseName_NamespaceIndex INTEGER', # 5
430+
'BrowseName_Name TEXT', # 6
431+
'DisplayName_Text TEXT', # 7
432+
'DisplayName_Locale TEXT', # 8
433+
'DisplayName_Encoding INTEGER', # 9
434+
'NodeClass INTEGER', # 10
435+
'TypeDefinition BLOB', # 11
436+
'Description STRING' # 12
413437
]
414438
AddressSpaceSQLite._create_indexed_table(backend, table, REFS_COLS, drop)
415439

@@ -459,9 +483,22 @@ def _insert_reference(backend, nodeid, ref, table=REFS_TABLE_NAME, commit=True):
459483
)
460484
backend.execute_write(cmd, params=params, commit=commit)
461485

486+
@staticmethod
487+
def _drop_references(backend, nodeid, table=REFS_TABLE_NAME, commit=True):
488+
assert(nodeid.NodeIdType == NodeIdType.Numeric)
489+
binNodeId = ua.ua_binary.nodeid_to_binary(nodeid)
490+
cmd = 'DELETE FROM "{tn}" WHERE {nid}=?'.format(
491+
tn=table,
492+
nid=AddressSpaceSQLite.NODEID_COL_NAME
493+
)
494+
params = (
495+
sqlite3.Binary(binNodeId),
496+
)
497+
backend.execute_write(cmd, params=params, commit=commit)
498+
462499
def _calcRefPrimaryKey(nodeid, ref):
463-
binNodeId = ua.ua_binary.nodeid_to_binary(nodeid) # Our own nodeid
464-
refNodeId = ua.ua_binary.nodeid_to_binary(ref.NodeId) # Referred nodeid
500+
binNodeId = ua.ua_binary.nodeid_to_binary(nodeid) # Our own nodeid
501+
refNodeId = ua.ua_binary.nodeid_to_binary(ref.NodeId) # Referred nodeid
465502
primaryKey = binNodeId + refNodeId + pack(">B", int(ref.IsForward))
466503
return binNodeId, refNodeId, primaryKey
467504

@@ -512,7 +549,7 @@ def _cmp_attr(attr, attr2):
512549
assert(attr.value.StatusCode.value == attr2.value.StatusCode.value)
513550
try:
514551
assert(str(attr.value.Value.Value) == str(attr2.value.Value.Value))
515-
except:
552+
except Exception:
516553
assert(int(attr.value.Value.Value) == int(attr2.value.Value.Value))
517554
assert(attr.value.Value.VariantType == attr2.value.Value.VariantType)
518555

@@ -532,7 +569,7 @@ class StandardAddressSpaceSQLite(AddressSpaceSQLite):
532569

533570
def __init__(self, cache=None):
534571
path = os.path.join(os.path.dirname(__file__), "standard_address_space", "standard_address_space.sql")
535-
backend = SQLite3Backend(sqlFile=path, readonly=True)
572+
backend = SQLite3Backend(sqlFile=path, readonly=True)
536573
super(StandardAddressSpaceSQLite, self).__init__(backend, cache)
537574

538575
def __enter__(self):

0 commit comments

Comments
 (0)