11
22import sys
33import os .path
4- import time
54import datetime
65import sqlite3
76from struct import pack
1211from opcua .common .sqlite3_backend import SQLite3Backend
1312from opcua .server .address_space import NodeData , AddressSpace , AttributeValue
1413
14+
1515class ReadOnlyException (Exception ):
1616 pass
1717
@@ -33,6 +33,7 @@ def value(self, newVal):
3333 self ._value = newVal
3434 self .onchange_cb ()
3535
36+
3637class 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