Initial commit of akmon project

This commit is contained in:
2026-01-20 08:04:15 +08:00
commit 77a2bab985
1309 changed files with 343305 additions and 0 deletions

179
locations.sql Normal file
View File

@@ -0,0 +1,179 @@
-- Location tracking schema for 2.4G gateway based wearable positioning
-- Run this script in Supabase/PostgreSQL environment after ensuring uuid-ossp extension is available.
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
-- =============================================================
-- 1. 2.4G Gateways registry
-- =============================================================
CREATE TABLE IF NOT EXISTS location_gateways (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
organization_id UUID, -- optional, reference to owning org / school (wire manually)
campus_code TEXT, -- logical campus/region grouping code
area_name TEXT, -- eg. East Campus, Zone B
building_name TEXT,
floor_label TEXT,
room_label TEXT,
name TEXT NOT NULL, -- human friendly name
system_code TEXT NOT NULL UNIQUE, -- system/asset identifier
serial_number TEXT,
description TEXT,
latitude DECIMAL(10, 8) NOT NULL,
longitude DECIMAL(11, 8) NOT NULL,
altitude_m NUMERIC(8, 2),
coordinate_system TEXT DEFAULT 'WGS84', -- WGS84 / GCJ02 / BD09 etc
install_height_m NUMERIC(6, 2),
orientation_deg NUMERIC(5, 2),
coverage_radius_m NUMERIC(6, 2),
coverage_notes TEXT,
ip_address INET,
lan_mac MACADDR,
upstream_mac MACADDR,
firmware_version TEXT,
hardware_version TEXT,
heartbeat_interval_s INTEGER,
status TEXT DEFAULT 'active' CHECK (status IN ('active', 'maintenance', 'inactive', 'retired')),
last_online_at TIMESTAMPTZ,
last_maintenance_at TIMESTAMPTZ,
tags TEXT[],
extra JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ
);
COMMENT ON TABLE location_gateways IS 'Registry of 2.4G gateways installed across campuses.';
COMMENT ON COLUMN location_gateways.extra IS 'Extensible JSON payload for vendor-specific metadata.';
CREATE INDEX IF NOT EXISTS idx_location_gateways_org ON location_gateways(organization_id);
CREATE INDEX IF NOT EXISTS idx_location_gateways_geo ON location_gateways(latitude, longitude);
CREATE INDEX IF NOT EXISTS idx_location_gateways_status ON location_gateways(status);
-- Auto update trigger for updated_at
CREATE OR REPLACE FUNCTION trg_location_gateways_touch() RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at := NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_location_gateways_touch ON location_gateways;
CREATE TRIGGER trg_location_gateways_touch
BEFORE UPDATE ON location_gateways
FOR EACH ROW EXECUTE FUNCTION trg_location_gateways_touch();
-- =============================================================
-- 2. Wearable location snapshots (per time slice, per device)
-- =============================================================
CREATE TABLE IF NOT EXISTS wearable_location_snapshots (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
device_id UUID NOT NULL REFERENCES public.ak_devices(id) ON DELETE CASCADE,
user_id UUID REFERENCES public.ak_users(id) ON DELETE SET NULL,
estimated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
window_started_at TIMESTAMPTZ,
window_ended_at TIMESTAMPTZ,
latitude DECIMAL(10, 8),
longitude DECIMAL(11, 8),
altitude_m NUMERIC(8, 2),
coordinate_system TEXT DEFAULT 'WGS84',
uncertainty_radius_m NUMERIC(6, 2), -- estimated accuracy / radius (meters)
confidence NUMERIC(4, 2), -- 0.00 - 1.00
method TEXT, -- e.g. WCL, Trilateration, Kalman
algorithm_version TEXT,
observation_count INTEGER DEFAULT 0,
campus_code TEXT,
area_name TEXT,
building_name TEXT,
floor_label TEXT,
room_label TEXT,
observations JSONB, -- optional denormalized structure of gateway RSSI readings
diagnostics JSONB, -- troubleshooting info (residuals, covariance etc)
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
COMMENT ON TABLE wearable_location_snapshots IS 'Estimated wearable locations derived from multi-gateway RSSI aggregation.';
COMMENT ON COLUMN wearable_location_snapshots.observations IS 'Optional JSON containing gateway RSSI array for quick lookup.';
CREATE INDEX IF NOT EXISTS idx_wearable_snapshots_device_time ON wearable_location_snapshots(device_id, estimated_at DESC);
CREATE INDEX IF NOT EXISTS idx_wearable_snapshots_user_time ON wearable_location_snapshots(user_id, estimated_at DESC);
CREATE INDEX IF NOT EXISTS idx_wearable_snapshots_geo ON wearable_location_snapshots(latitude, longitude);
CREATE OR REPLACE FUNCTION trg_wearable_snapshots_touch() RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at := NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_wearable_snapshots_touch ON wearable_location_snapshots;
CREATE TRIGGER trg_wearable_snapshots_touch
BEFORE UPDATE ON wearable_location_snapshots
FOR EACH ROW EXECUTE FUNCTION trg_wearable_snapshots_touch();
-- =============================================================
-- 3. Raw gateway RSSI observations per snapshot
-- =============================================================
CREATE TABLE IF NOT EXISTS wearable_location_observations (
id BIGSERIAL PRIMARY KEY,
snapshot_id UUID NOT NULL REFERENCES wearable_location_snapshots(id) ON DELETE CASCADE,
gateway_id UUID NOT NULL REFERENCES location_gateways(id) ON DELETE CASCADE,
received_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
gateway_time TIMESTAMPTZ,
rssi_dbm NUMERIC(5, 2) NOT NULL,
snr_db NUMERIC(5, 2),
distance_m NUMERIC(7, 2), -- computed from RSSI if available
channel INTEGER,
frequency_mhz NUMERIC(6, 2),
packet_sequence INTEGER,
latency_ms INTEGER,
firmware_version TEXT,
payload JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
COMMENT ON TABLE wearable_location_observations IS 'Raw 2.4G gateway readings contributing to a wearable location snapshot.';
CREATE UNIQUE INDEX IF NOT EXISTS idx_observations_unique_sample
ON wearable_location_observations(snapshot_id, gateway_id);
CREATE INDEX IF NOT EXISTS idx_observations_gateway_time
ON wearable_location_observations(gateway_id, received_at DESC);
CREATE INDEX IF NOT EXISTS idx_observations_snapshot ON wearable_location_observations(snapshot_id);
-- Optional view for latest snapshot per device
CREATE OR REPLACE VIEW wearable_latest_location AS
SELECT DISTINCT ON (device_id)
device_id,
id AS snapshot_id,
estimated_at,
latitude,
longitude,
altitude_m,
uncertainty_radius_m,
confidence,
method,
algorithm_version,
created_at
FROM wearable_location_snapshots
ORDER BY device_id, estimated_at DESC;
COMMENT ON VIEW wearable_latest_location IS 'Convenience view returning the latest computed position per wearable device.';