Initial commit: adacam-api v1.0.0
Clean Python Flask replacement for odc-api (434k lines Node.js → ~350 lines Python)
- GET /api/1/landmarks/last/{N} - last N detections from SQLite
- POST /api/1/landmarks - ingest detections + forward to AdaMaps
- GET /api/1/gnssConcise/latestValid - GPS fix from Redis
- GET /api/1/status - device status
- GET /api/1/deviceinfo - device identity
- GET /api/1/recording/frames/latest - latest frame path
No /api/1/cmd - that's the CVE, it's gone.
Includes:
- SQLite for local storage + offline queue
- Background thread for AdaMaps retry
- systemd service unit
- install.sh for device deployment
This commit is contained in:
parent
5dd91b0b15
commit
7acc5ab088
17 changed files with 507 additions and 1 deletions
88
adacam_api/db.py
Normal file
88
adacam_api/db.py
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
"""SQLite database for landmarks and offline queue."""
|
||||
import sqlite3
|
||||
import threading
|
||||
import json
|
||||
|
||||
DB_PATH = "/data/adacam/adacam.db"
|
||||
_local = threading.local()
|
||||
|
||||
|
||||
def get_conn():
|
||||
"""Get thread-local database connection."""
|
||||
if not hasattr(_local, "conn"):
|
||||
_local.conn = sqlite3.connect(DB_PATH, check_same_thread=False)
|
||||
_local.conn.row_factory = sqlite3.Row
|
||||
return _local.conn
|
||||
|
||||
|
||||
def init():
|
||||
"""Initialize database schema."""
|
||||
conn = get_conn()
|
||||
conn.executescript("""
|
||||
CREATE TABLE IF NOT EXISTS landmarks (
|
||||
id INTEGER PRIMARY KEY,
|
||||
class_label TEXT,
|
||||
class_label_confidence REAL,
|
||||
overall_confidence REAL,
|
||||
lat REAL, lon REAL, alt REAL,
|
||||
azimuth REAL,
|
||||
width INTEGER, height INTEGER,
|
||||
ts INTEGER,
|
||||
forwarded INTEGER DEFAULT 0
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS forward_queue (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
payload TEXT,
|
||||
created_at INTEGER DEFAULT (strftime('%s','now'))
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_landmarks_ts ON landmarks(ts DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_landmarks_forwarded ON landmarks(forwarded);
|
||||
""")
|
||||
conn.commit()
|
||||
|
||||
|
||||
def insert_landmark(data):
|
||||
"""Insert a landmark detection."""
|
||||
conn = get_conn()
|
||||
conn.execute("""
|
||||
INSERT INTO landmarks (id, class_label, class_label_confidence, overall_confidence,
|
||||
lat, lon, alt, azimuth, width, height, ts)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""", (data.get("id"), data["class_label"], data.get("class_label_confidence"),
|
||||
data.get("overall_confidence"), data.get("lat"), data.get("lon"),
|
||||
data.get("alt"), data.get("azimuth"), data.get("width"),
|
||||
data.get("height"), data.get("ts")))
|
||||
conn.commit()
|
||||
return conn.execute("SELECT last_insert_rowid()").fetchone()[0]
|
||||
|
||||
|
||||
def get_last_landmarks(n):
|
||||
"""Get last N landmarks."""
|
||||
conn = get_conn()
|
||||
rows = conn.execute("SELECT * FROM landmarks ORDER BY ts DESC LIMIT ?", (n,)).fetchall()
|
||||
return [dict(r) for r in rows]
|
||||
|
||||
|
||||
def queue_forward(payload):
|
||||
"""Queue a payload for later forwarding."""
|
||||
conn = get_conn()
|
||||
conn.execute("INSERT INTO forward_queue (payload) VALUES (?)", (json.dumps(payload),))
|
||||
conn.commit()
|
||||
|
||||
|
||||
def pop_queue(limit=50):
|
||||
"""Pop items from forward queue."""
|
||||
conn = get_conn()
|
||||
rows = conn.execute("SELECT id, payload FROM forward_queue ORDER BY id LIMIT ?", (limit,)).fetchall()
|
||||
if rows:
|
||||
ids = [r["id"] for r in rows]
|
||||
conn.execute(f"DELETE FROM forward_queue WHERE id IN ({','.join('?'*len(ids))})", ids)
|
||||
conn.commit()
|
||||
return [(r["id"], json.loads(r["payload"])) for r in rows]
|
||||
|
||||
|
||||
def mark_forwarded(landmark_id):
|
||||
"""Mark a landmark as forwarded."""
|
||||
conn = get_conn()
|
||||
conn.execute("UPDATE landmarks SET forwarded = 1 WHERE id = ?", (landmark_id,))
|
||||
conn.commit()
|
||||
Loading…
Add table
Add a link
Reference in a new issue