"""aiosqlite connection helper: WAL pragmas, schema bootstrap, migrations.""" from __future__ import annotations import logging import os from pathlib import Path from typing import Any import aiosqlite log = logging.getLogger(__name__) SCHEMA_VERSION = 1 SCHEMA_SQL: tuple[str, ...] = ( """ CREATE TABLE IF NOT EXISTS schema_meta ( key TEXT PRIMARY KEY, value TEXT NOT NULL ) """, """ CREATE TABLE IF NOT EXISTS vessel_static ( mmsi INTEGER PRIMARY KEY, name TEXT, callsign TEXT, imo INTEGER, ship_type INTEGER, dim_a INTEGER, dim_b INTEGER, dim_c INTEGER, dim_d INTEGER, eta TEXT, draught REAL, destination TEXT, updated_at REAL NOT NULL ) """, """ CREATE TABLE IF NOT EXISTS ais_dynamic ( id INTEGER PRIMARY KEY AUTOINCREMENT, mmsi INTEGER NOT NULL, ts REAL NOT NULL, lat REAL, lon REAL, sog REAL, cog REAL, heading REAL, nav_status INTEGER, rot REAL, raw_msg_type INTEGER ) """, "CREATE INDEX IF NOT EXISTS ix_ais_dynamic_mmsi_ts ON ais_dynamic(mmsi, ts)", "CREATE INDEX IF NOT EXISTS ix_ais_dynamic_ts ON ais_dynamic(ts)", """ CREATE TABLE IF NOT EXISTS gps_fix ( id INTEGER PRIMARY KEY AUTOINCREMENT, ts REAL NOT NULL, lat REAL, lon REAL, sog REAL, cog REAL, alt REAL, fix_quality INTEGER, sats INTEGER, hdop REAL ) """, "CREATE INDEX IF NOT EXISTS ix_gps_fix_ts ON gps_fix(ts)", """ CREATE TABLE IF NOT EXISTS raw_nmea ( id INTEGER PRIMARY KEY AUTOINCREMENT, ts REAL NOT NULL, source TEXT NOT NULL, kind TEXT NOT NULL, line TEXT NOT NULL ) """, "CREATE INDEX IF NOT EXISTS ix_raw_nmea_ts ON raw_nmea(ts)", "CREATE INDEX IF NOT EXISTS ix_raw_nmea_kind_ts ON raw_nmea(kind, ts)", """ CREATE TABLE IF NOT EXISTS base_station ( mmsi INTEGER PRIMARY KEY, ts REAL NOT NULL, lat REAL, lon REAL, epfd INTEGER, updated_at REAL NOT NULL ) """, """ CREATE TABLE IF NOT EXISTS aton ( mmsi INTEGER PRIMARY KEY, ts REAL NOT NULL, lat REAL, lon REAL, aton_type INTEGER, name TEXT, virtual INTEGER, updated_at REAL NOT NULL ) """, """ CREATE TABLE IF NOT EXISTS radio_telemetry ( id INTEGER PRIMARY KEY AUTOINCREMENT, ts REAL NOT NULL, channel TEXT, rssi REAL, snr REAL, slot INTEGER, raw_json TEXT ) """, "CREATE INDEX IF NOT EXISTS ix_radio_telemetry_ts ON radio_telemetry(ts)", ) async def connect(path: str) -> aiosqlite.Connection: """Open the SQLite DB with WAL pragmas and ensure the schema exists.""" p = Path(path) p.parent.mkdir(parents=True, exist_ok=True) conn = await aiosqlite.connect(path, timeout=5.0) # Pragmas — WAL + sane defaults for embedded devices. await conn.execute("PRAGMA journal_mode=WAL") await conn.execute("PRAGMA synchronous=NORMAL") await conn.execute("PRAGMA busy_timeout=5000") await conn.execute("PRAGMA foreign_keys=ON") await conn.execute("PRAGMA temp_store=MEMORY") await _ensure_schema(conn) return conn async def _ensure_schema(conn: aiosqlite.Connection) -> None: for stmt in SCHEMA_SQL: await conn.execute(stmt) await conn.execute( "INSERT OR REPLACE INTO schema_meta(key, value) VALUES('version', ?)", (str(SCHEMA_VERSION),), ) await conn.commit() async def close(conn: aiosqlite.Connection | None) -> None: if conn is None: return try: await conn.commit() except Exception: pass try: await conn.close() except Exception: pass __all__ = ["connect", "close", "SCHEMA_VERSION", "SCHEMA_SQL"]