Files
akmon/mock_location.sql
2026-01-20 08:04:15 +08:00

423 lines
11 KiB
PL/PgSQL

-- mock_location.sql
-- Populates deterministic wearable location data for demos and local development.
-- Requires schema from doc_chat/locations.sql and zhipao.sql (users/devices tables).
BEGIN;
-- Stable identifiers reused across the script to keep reruns idempotent.
CREATE TEMP TABLE __mock_actor AS
SELECT
'eed3824b-bba1-4309-8048-19d17367c084'::UUID AS user_id,
'b2d0aabd-4f7d-42c8-a7a8-9d5b4c67d222'::UUID AS device_id;
CREATE TEMP TABLE __mock_gateways AS
SELECT *
FROM (
VALUES
(
'11111111-aaaa-bbbb-cccc-000000000001'::UUID,
'北侧主楼网关',
'MOCK-GW-NORTH',
'active',
'main',
'north-zone',
'教学楼A',
'3F',
'301',
31.230900,
121.473200,
'WGS84',
35.0,
30,
3.5,
0.0,
'Mock gateway providing northern coverage',
-51.0,
12.0
),
(
'11111111-aaaa-bbbb-cccc-000000000002'::UUID,
'南侧体育馆网关',
'MOCK-GW-SOUTH',
'active',
'main',
'south-zone',
'体育馆',
'1F',
'大厅',
31.229700,
121.473400,
'WGS84',
40.0,
30,
4.0,
180.0,
'Mock gateway covering southern sports hall',
-56.0,
11.5
),
(
'11111111-aaaa-bbbb-cccc-000000000003'::UUID,
'东侧宿舍楼网关',
'MOCK-GW-EAST',
'active',
'main',
'east-zone',
'宿舍楼C',
'2F',
'走廊',
31.230100,
121.474500,
'WGS84',
30.0,
30,
3.0,
90.0,
'Mock gateway for east dormitory coverage',
-53.5,
12.5
)
) AS g(
gateway_id,
name,
system_code,
status,
campus_code,
area_name,
building_name,
floor_label,
room_label,
latitude,
longitude,
coordinate_system,
coverage_radius_m,
heartbeat_interval_s,
install_height_m,
orientation_deg,
description,
base_rssi_dbm,
base_snr_db
);
-- Ensure the mock user exists.
INSERT INTO public.ak_users (id, username, email, password_hash, role, created_at, updated_at)
SELECT
actor.user_id,
'mock_location_user',
'mock_location_user@example.com',
crypt('Mock@123', gen_salt('bf')),
'student',
NOW(),
NOW()
FROM __mock_actor actor
ON CONFLICT (id) DO UPDATE
SET
username = EXCLUDED.username,
email = EXCLUDED.email,
password_hash = EXCLUDED.password_hash,
updated_at = NOW();
-- Ensure the mock device exists.
INSERT INTO public.ak_devices (
id,
user_id,
device_type,
device_name,
device_mac,
bind_time,
status,
extra
)
SELECT
actor.device_id,
actor.user_id,
'wristband',
'模拟定位手环',
'AA-BB-CC-11-22-33',
NOW(),
'active',
jsonb_build_object(
'firmware_version', 'mock-1.0',
'hardware_revision', 'rev-A',
'notes', 'Mocked location feed device for UI demos'
)
FROM __mock_actor actor
ON CONFLICT (id) DO UPDATE
SET
user_id = EXCLUDED.user_id,
device_type = EXCLUDED.device_type,
device_name = EXCLUDED.device_name,
device_mac = EXCLUDED.device_mac,
status = EXCLUDED.status,
extra = EXCLUDED.extra,
bind_time = EXCLUDED.bind_time;
-- Ensure reference gateways are present (upsert by primary key).
INSERT INTO location_gateways (
id,
name,
system_code,
status,
campus_code,
area_name,
building_name,
floor_label,
room_label,
latitude,
longitude,
coordinate_system,
coverage_radius_m,
heartbeat_interval_s,
install_height_m,
orientation_deg,
description
)
SELECT
gateway_id,
name,
system_code,
status,
campus_code,
area_name,
building_name,
floor_label,
room_label,
latitude,
longitude,
coordinate_system,
coverage_radius_m,
heartbeat_interval_s,
install_height_m,
orientation_deg,
description
FROM __mock_gateways g
ON CONFLICT (id) DO UPDATE
SET
name = EXCLUDED.name,
system_code = EXCLUDED.system_code,
status = EXCLUDED.status,
campus_code = EXCLUDED.campus_code,
area_name = EXCLUDED.area_name,
building_name = EXCLUDED.building_name,
floor_label = EXCLUDED.floor_label,
room_label = EXCLUDED.room_label,
latitude = EXCLUDED.latitude,
longitude = EXCLUDED.longitude,
coordinate_system = EXCLUDED.coordinate_system,
coverage_radius_m = EXCLUDED.coverage_radius_m,
heartbeat_interval_s = EXCLUDED.heartbeat_interval_s,
install_height_m = EXCLUDED.install_height_m,
orientation_deg = EXCLUDED.orientation_deg,
description = EXCLUDED.description,
updated_at = NOW();
-- Clear any previous mock data for the device to keep reruns idempotent.
WITH actor AS (SELECT device_id FROM __mock_actor)
DELETE FROM wearable_location_snapshots s
WHERE s.device_id = (SELECT device_id FROM actor);
-- Configuration and synthetic data generation (5-second cadence for 10 minutes).
WITH params AS (
SELECT
NOW() - INTERVAL '10 minutes' AS window_start,
INTERVAL '5 seconds' AS cadence_interval,
120 AS sample_count,
22.0 AS base_radius_m,
0.92 AS base_confidence
),
timeline AS (
SELECT
actor.device_id,
actor.user_id,
params.window_start + params.cadence_interval * seq AS estimated_at,
seq
FROM __mock_actor actor,
params,
generate_series(0, (SELECT sample_count FROM params) - 1) AS seq
),
path AS (
SELECT
t.device_id,
t.user_id,
t.estimated_at,
t.seq,
31.230300 + 0.00025 * SIN(2 * PI() * t.seq / 40.0) AS latitude,
121.473800 + 0.00035 * COS(2 * PI() * t.seq / 40.0) AS longitude,
6.5 + 0.2 * SIN(2 * PI() * t.seq / 20.0) AS altitude_m,
(SELECT base_radius_m FROM params) + 1.5 * RANDOM() AS uncertainty_radius_m,
LEAST(0.99, (SELECT base_confidence FROM params) + 0.05 * RANDOM()) AS confidence
FROM timeline t
),
observation_samples AS (
SELECT
p.device_id,
p.user_id,
p.estimated_at,
g.gateway_id,
g.name AS gateway_name,
g.system_code,
g.latitude,
g.longitude,
g.orientation_deg,
g.description,
(g.base_rssi_dbm + (-3 + RANDOM() * 6))::NUMERIC(5, 2) AS rssi_dbm,
(g.base_snr_db + (-0.5 + RANDOM()))::NUMERIC(5, 2) AS snr_db,
(18 + RANDOM() * 6)::NUMERIC(7, 2) AS distance_m,
(11 + (RANDOM() * 2))::INT AS channel,
ROUND((2402 + RANDOM() * 4)::NUMERIC, 2) AS frequency_mhz,
(1000 + FLOOR(RANDOM() * 2000))::INT AS packet_sequence,
(5 + FLOOR(RANDOM() * 8))::INT AS latency_ms,
'gw-firmware-mock-1.0' AS firmware_version,
(3.75 + RANDOM() * 0.05)::NUMERIC(6, 3) AS battery_voltage,
(25.5 + RANDOM() * 1.5)::NUMERIC(4, 1) AS temperature_c
FROM path p
CROSS JOIN __mock_gateways g
),
snapshot_payload AS (
SELECT
p.device_id,
p.user_id,
p.estimated_at,
p.latitude,
p.longitude,
p.altitude_m,
p.uncertainty_radius_m,
p.confidence,
p.seq,
COUNT(os.gateway_id)::INT AS observation_count,
jsonb_agg(
jsonb_build_object(
'gateway_id', os.gateway_id,
'name', os.gateway_name,
'system_code', os.system_code,
'rssi_dbm', os.rssi_dbm,
'snr_db', os.snr_db,
'distance_m', os.distance_m,
'battery_voltage', os.battery_voltage,
'temperature_c', os.temperature_c
) ORDER BY os.gateway_id
) AS observations_json,
jsonb_build_object(
'path_index', p.seq,
'source', 'mock_location.sql',
'mean_distance_m', ROUND(AVG(os.distance_m)::NUMERIC, 2),
'mean_snr_db', ROUND(AVG(os.snr_db)::NUMERIC, 2)
) AS diagnostics_json
FROM path p
JOIN observation_samples os
ON os.device_id = p.device_id
AND os.user_id = p.user_id
AND os.estimated_at = p.estimated_at
GROUP BY
p.device_id,
p.user_id,
p.estimated_at,
p.latitude,
p.longitude,
p.altitude_m,
p.uncertainty_radius_m,
p.confidence,
p.seq
),
inserted_snapshots AS (
INSERT INTO wearable_location_snapshots (
device_id,
user_id,
estimated_at,
window_started_at,
window_ended_at,
latitude,
longitude,
altitude_m,
coordinate_system,
uncertainty_radius_m,
confidence,
method,
algorithm_version,
observation_count,
campus_code,
area_name,
building_name,
floor_label,
room_label,
observations,
diagnostics,
created_at,
updated_at
)
SELECT
sp.device_id,
sp.user_id,
sp.estimated_at,
sp.estimated_at - INTERVAL '2 seconds',
sp.estimated_at + INTERVAL '2 seconds',
sp.latitude,
sp.longitude,
sp.altitude_m,
'WGS84',
sp.uncertainty_radius_m,
sp.confidence,
'trilateration',
'mock-v1',
sp.observation_count,
'main',
'demo-zone',
'演示大楼',
'3F',
'走廊',
sp.observations_json,
sp.diagnostics_json,
NOW(),
NOW()
FROM snapshot_payload sp
RETURNING id, device_id, user_id, estimated_at
)
INSERT INTO wearable_location_observations (
snapshot_id,
gateway_id,
received_at,
gateway_time,
rssi_dbm,
snr_db,
distance_m,
channel,
frequency_mhz,
packet_sequence,
latency_ms,
firmware_version,
payload,
created_at
)
SELECT
s.id,
os.gateway_id,
os.estimated_at,
os.estimated_at,
os.rssi_dbm,
os.snr_db,
os.distance_m,
os.channel,
os.frequency_mhz,
os.packet_sequence,
os.latency_ms,
os.firmware_version,
jsonb_build_object(
'device_id', os.device_id,
'user_id', os.user_id,
'gateway_name', os.gateway_name,
'system_code', os.system_code,
'battery_voltage', os.battery_voltage,
'temperature_c', os.temperature_c,
'latitude', os.latitude,
'longitude', os.longitude,
'orientation_deg', os.orientation_deg,
'description', os.description
),
NOW()
FROM inserted_snapshots s
JOIN observation_samples os
ON os.device_id = s.device_id
AND os.user_id = s.user_id
AND os.estimated_at = s.estimated_at;
COMMIT;