history backend which stores data values and object events in a SQLite database this backend is intended to only be accessed via OPC UA, therefore all UA Variants saved in the history database are in binary format (SQLite BLOBs) note that PARSE_DECLTYPES is active so certain data ty
| 12 | |
| 13 | |
| 14 | class HistorySQLite(HistoryStorageInterface): |
| 15 | """ |
| 16 | history backend which stores data values and object events in a SQLite database |
| 17 | this backend is intended to only be accessed via OPC UA, therefore all UA Variants saved in |
| 18 | the history database are in binary format (SQLite BLOBs) |
| 19 | note that PARSE_DECLTYPES is active so certain data types (such as datetime) will not be BLOBs |
| 20 | """ |
| 21 | |
| 22 | def __init__(self, path="history.db"): |
| 23 | self.logger = logging.getLogger(__name__) |
| 24 | self._datachanges_period = {} |
| 25 | self._db_file = path |
| 26 | self._lock = Lock() |
| 27 | self._event_fields = {} |
| 28 | |
| 29 | self._conn = sqlite3.connect(self._db_file, detect_types=sqlite3.PARSE_DECLTYPES, check_same_thread=False) |
| 30 | |
| 31 | def new_historized_node(self, node_id, period, count=0): |
| 32 | with self._lock: |
| 33 | _c_new = self._conn.cursor() |
| 34 | |
| 35 | table = self._get_table_name(node_id) |
| 36 | |
| 37 | self._datachanges_period[node_id] = period, count |
| 38 | |
| 39 | # create a table for the node which will store attributes of the DataValue object |
| 40 | # note: Value/VariantType TEXT is only for human reading, the actual data is stored in VariantBinary column |
| 41 | try: |
| 42 | _c_new.execute('CREATE TABLE "{tn}" (_Id INTEGER PRIMARY KEY NOT NULL,' |
| 43 | ' ServerTimestamp TIMESTAMP,' |
| 44 | ' SourceTimestamp TIMESTAMP,' |
| 45 | ' StatusCode INTEGER,' |
| 46 | ' Value TEXT,' |
| 47 | ' VariantType TEXT,' |
| 48 | ' VariantBinary BLOB)'.format(tn=table)) |
| 49 | |
| 50 | except sqlite3.Error as e: |
| 51 | self.logger.info('Historizing SQL Table Creation Error for %s: %s', node_id, e) |
| 52 | |
| 53 | self._conn.commit() |
| 54 | |
| 55 | def save_node_value(self, node_id, datavalue): |
| 56 | with self._lock: |
| 57 | _c_sub = self._conn.cursor() |
| 58 | |
| 59 | table = self._get_table_name(node_id) |
| 60 | |
| 61 | # insert the data change into the database |
| 62 | try: |
| 63 | _c_sub.execute('INSERT INTO "{tn}" VALUES (NULL, ?, ?, ?, ?, ?, ?)'.format(tn=table), |
| 64 | ( |
| 65 | datavalue.ServerTimestamp, |
| 66 | datavalue.SourceTimestamp, |
| 67 | datavalue.StatusCode.value, |
| 68 | str(datavalue.Value.Value), |
| 69 | datavalue.Value.VariantType.name, |
| 70 | sqlite3.Binary(variant_to_binary(datavalue.Value)) |
| 71 | ) |
no outgoing calls