-- 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;