423 lines
11 KiB
PL/PgSQL
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; |