From ea34f78438dfb588abead75887e07773443bc19f Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Mon, 27 Jul 2026 16:16:03 +0800 Subject: [PATCH] websocket --- src/Data_Source/Data_Source.tsx | 236 ++++++++++++++++++++++---------- src/Map/Aircraft.tsx | 14 +- src/Map/Cesium_Map.tsx | 27 +--- 3 files changed, 173 insertions(+), 104 deletions(-) diff --git a/src/Data_Source/Data_Source.tsx b/src/Data_Source/Data_Source.tsx index 03b4878..7cb90cb 100644 --- a/src/Data_Source/Data_Source.tsx +++ b/src/Data_Source/Data_Source.tsx @@ -2,7 +2,7 @@ import enhance from "../core/enhance.tsx"; import {Button, InputNumber, message, Select, Space, Switch, Tag, Typography} from "antd"; import axios from "axios"; -import {baseURL, darkenColor, lightenColor, Prefix, row_style, StateHolder} from "../Global.tsx"; +import {baseURL, darkenColor, lightenColor, Prefix, row_style, server_url, StateHolder} from "../Global.tsx"; import {Base_Station} from "../Map/Base_Station.tsx"; import {Aircraft, Aircraft_Track_Point} from "../Map/Aircraft.tsx"; import {app} from "../App.tsx"; @@ -35,10 +35,6 @@ export type Data_Source_Map_Display_Config = { map2d: Data_Source_Map_Display_Data map3d: Data_Source_Map_Display_Data } -type Aircraft_Track_Request_Item = { - icao: string - last_size: number -} export function default_map_display_data(): Data_Source_Map_Display_Data { return { base_station_show: true, @@ -121,6 +117,7 @@ export class Data_Source extends enhance.Base { active_aircraft: Aircraft = null; manual_track_icao_set: Set = new Set(); monitor_all_aircraft_mode: boolean = false; + aircraft_change_versions: Map = new Map(); connect_list: [] = [] first: boolean = true; @@ -163,10 +160,10 @@ export class Data_Source extends enhance.Base { if (this.key) { await this.refresh_base_station_location(); } - await this.loadList(); + aircraft_stream.ensure_open(); + aircraft_stream.schedule_subscribe(); if (this.active_aircraft && !this.is_aircraft_track_monitored(this.active_aircraft.icao)) { - // console.log(`air:${this.active_aircraft.icao} show path`) - this.active_aircraft.append_path(); + aircraft_stream.schedule_subscribe(); } } manual_track_icao_list(): string[] { @@ -178,6 +175,9 @@ export class Data_Source extends enhance.Base { is_aircraft_track_monitored(icao: string): boolean { return this.monitor_all_aircraft_mode || this.is_manual_tracking_aircraft(icao); } + request_aircraft_stream_update() { + aircraft_stream.send_subscription(); + } async set_manual_tracking_aircraft(icao: string, enabled: boolean) { if (enabled) { this.manual_track_icao_set.add(icao); @@ -186,7 +186,7 @@ export class Data_Source extends enhance.Base { const aircraft = this.aircraftMap.get(icao); aircraft?.hide_path(); } - await this.refresh_manual_tracked_aircraft(); + aircraft_stream.send_subscription(); this.refresh_manual_tracking_views(); } async set_monitor_all_aircraft_mode(enabled: boolean) { @@ -198,7 +198,7 @@ export class Data_Source extends enhance.Base { } } } - await this.refresh_manual_tracked_aircraft(); + aircraft_stream.send_subscription(); this.refresh_manual_tracking_views(); } clear_manual_tracking_aircraft(icao?: string) { @@ -211,6 +211,7 @@ export class Data_Source extends enhance.Base { } this.manual_track_icao_set.clear(); } + aircraft_stream.send_subscription(); this.refresh_manual_tracking_views(); } clear_all_aircraft_tracking() { @@ -219,38 +220,9 @@ export class Data_Source extends enhance.Base { aircraft.hide_path(); } this.manual_track_icao_set.clear(); + aircraft_stream.send_subscription(); this.refresh_manual_tracking_views(); } - tracked_aircraft_track_request_items(): Aircraft_Track_Request_Item[] { - const tracked_icaos = this.monitor_all_aircraft_mode ? Array.from(this.aircraftMap.keys()) : this.manual_track_icao_list(); - const ret: Aircraft_Track_Request_Item[] = []; - tracked_icaos.forEach((icao) => { - const aircraft = this.aircraftMap.get(icao); - if (aircraft) { - ret.push({icao, last_size: aircraft.last_size}); - } - }); - return ret; - } - async refresh_manual_tracked_aircraft() { - const items = this.tracked_aircraft_track_request_items(); - if (items.length === 0) { - return; - } - const res = await axios.post(`${baseURL}/get_aircraft_track_list_batch`, { - data_source_key: this.key, - items - }); - const list = Array.isArray(res.data?.list) ? res.data.list : []; - list.forEach((item: any) => { - const aircraft = this.aircraftMap.get(item.icao); - if (!aircraft) { - return; - } - aircraft.is_show_path = true; - aircraft.append_path_response(item); - }); - } refresh_manual_tracking_views() { app.cesium_map?.sync_data_sources(); app.leaflet_map?.data_source_show?.flush(); @@ -321,44 +293,51 @@ export class Data_Source extends enhance.Base { } return this.aircraftMap.get(icao)!; } - - - loadList = async () => { - if (!this.enable) return; - const ret = await axios.post(`${baseURL}/aircraft_change_list`, { - data_source_key: this.key + create_or_get_aircraft(item: any): Aircraft { + let aircraft: Aircraft = this.get_aircraft(item.icao); + if (aircraft == null) { + aircraft = new Aircraft(item.icao, this); + aircraft.refresh(); + } + return aircraft; + } + apply_aircraft_items(items: any[]) { + items.forEach((item: any) => { + const aircraft = this.create_or_get_aircraft(item); + aircraft.refresh_from_change_list(item); + this.aircraftMap.set(item.icao, aircraft); + this.aircraft_change_versions.set(item.icao, String(item.change_version || "")); }); - + this.refresh_aircraft_range(); + } + remove_aircraft(icao: string) { + const aircraft = this.aircraftMap.get(icao); + if (!aircraft) return; + aircraft.hide_track() + aircraft.pos_marker.remove(); + this.aircraftMap.delete(icao); + this.aircraft_change_versions.delete(icao); + if (this.active_aircraft === aircraft) { + this.active_aircraft = null; + } + } + refresh_aircraft_range() { let station = this.base_station; - let new_map: Map = new Map(); let max_distance = 0; let max_plane_pos: L.LatLng | null = null; let max_aircraft_icao = ""; - ret.data.forEach((item: any) => { - let aircraft: Aircraft = this.get_aircraft(item.icao); - if (aircraft == null) { - aircraft = new Aircraft(item.icao, this); - aircraft.refresh(); - } - aircraft.refresh_from_change_list(item); - new_map.set(item.icao, aircraft); - let lat = item.latitude; - let lon = item.longitude; + this.aircraftMap.forEach((aircraft) => { + let lat = aircraft.data_model.lat; + let lon = aircraft.data_model.lon; if (station.pos) { let distance = station.get_distance_from_base_station(lat, lon); if (distance > max_distance) { max_distance = distance; max_plane_pos = new L.LatLng(lat, lon); - max_aircraft_icao = item.icao; + max_aircraft_icao = aircraft.icao; } } }); - this.aircraftMap.forEach((aircraft) => { - if (!new_map.has(aircraft.icao)) { - aircraft.hide_track() - aircraft.pos_marker.remove(); - } - }); if (max_plane_pos) { station.update_current_range(max_distance, max_plane_pos, max_aircraft_icao); if (!this.keep_mode || !station.max_plane_pos || max_distance > station.maxDistance) { @@ -372,11 +351,32 @@ export class Data_Source extends enhance.Base { station.clear_range(); } } - this.aircraftMap = new_map; - await this.refresh_manual_tracked_aircraft(); - }; - - + } + apply_aircraft_delta(change_list: any[], removed_icaos: string[]) { + this.apply_aircraft_items(change_list); + removed_icaos.forEach((icao) => this.remove_aircraft(icao)); + this.refresh_aircraft_range(); + } + apply_aircraft_snapshot(items: any[]) { + const alive = new Set(); + items.forEach((item) => alive.add(item.icao)); + for (const icao of Array.from(this.aircraftMap.keys())) { + if (!alive.has(icao)) { + this.remove_aircraft(icao); + } + } + this.apply_aircraft_items(items); + } + apply_track_stream_list(list: any[]) { + list.forEach((item: any) => { + const aircraft = this.aircraftMap.get(item.icao); + if (!aircraft) { + return; + } + aircraft.is_show_path = true; + aircraft.append_path_response(item); + }); + } center_with_add(): React.JSX.Element { return (
不应该出现的 Data Source Center!
@@ -455,6 +455,102 @@ export class Data_Source extends enhance.Base { } } +class Aircraft_Stream_Client { + ws: WebSocket | null = null; + subscribe_timer: number | null = null; + reconnect_timer: number | null = null; + ensure_open() { + if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) { + return; + } + this.ws = new WebSocket(`ws://${server_url}/ws/aircraft_stream`); + this.ws.onopen = () => this.send_subscription(); + this.ws.onmessage = (event) => this.handle_message(event); + this.ws.onclose = () => this.schedule_reconnect(); + this.ws.onerror = () => this.ws?.close(); + } + schedule_reconnect() { + this.ws = null; + if (this.reconnect_timer !== null) { + return; + } + this.reconnect_timer = window.setTimeout(() => { + this.reconnect_timer = null; + this.ensure_open(); + this.schedule_subscribe(); + }, 1000); + } + schedule_subscribe() { + this.ensure_open(); + if (this.subscribe_timer !== null) { + return; + } + this.subscribe_timer = window.setTimeout(() => { + this.subscribe_timer = null; + this.send_subscription(); + }, 50); + } + send_subscription() { + if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { + return; + } + this.ws.send(JSON.stringify({ + type: "subscribe", + sources: this.source_payloads() + })); + } + source_payloads() { + const list = app.setting?.data_source_config.list.list || []; + return list.filter((ds: Data_Source) => ds.enable).map((ds: Data_Source) => this.source_payload(ds)); + } + source_payload(ds: Data_Source) { + return { + key: ds.key, + monitor_all_aircraft_mode: ds.monitor_all_aircraft_mode, + manual_track_icaos: Array.from(this.track_icaos(ds)), + aircraft_versions: Object.fromEntries(ds.aircraft_change_versions), + track_last_size: Object.fromEntries(Array.from(this.track_icaos(ds)).map((icao) => [icao, ds.aircraftMap.get(icao)?.last_size || 0])) + }; + } + track_icaos(ds: Data_Source): Set { + const ret = new Set(); + if (ds.monitor_all_aircraft_mode) { + for (const icao of ds.aircraftMap.keys()) { + ret.add(icao); + } + } else { + for (const icao of ds.manual_track_icao_set) { + ret.add(icao); + } + } + if (ds.active_aircraft) { + ret.add(ds.active_aircraft.icao); + } + const selected = app.cesium_map?.selected_aircraft as Aircraft | null | undefined; + if (selected?.data_source === ds) { + ret.add(selected.icao); + } + return ret; + } + handle_message(event: MessageEvent) { + const data = JSON.parse(String(event.data)); + if (data.type !== "aircraft_update" || !Array.isArray(data.sources)) { + return; + } + for (const source of data.sources) { + const ds = app.get_Data_Source(source.key); + if (!ds) { + continue; + } + ds.apply_aircraft_delta(Array.isArray(source.change_list) ? source.change_list : [], Array.isArray(source.removed_icaos) ? source.removed_icaos : []); + ds.apply_track_stream_list(Array.isArray(source.tracks) ? source.tracks : []); + } + app.cesium_map?.sync_data_sources(); + app.leaflet_map?.data_source_show?.flush(); + } +} +const aircraft_stream = new Aircraft_Stream_Client(); + export class Serial_Data_Source extends Data_Source { port_name: string = ""; diff --git a/src/Map/Aircraft.tsx b/src/Map/Aircraft.tsx index dd3661b..65fde8d 100644 --- a/src/Map/Aircraft.tsx +++ b/src/Map/Aircraft.tsx @@ -475,15 +475,6 @@ export class Aircraft { } ] }*/ - async load_track_data() { - const res = await axios.post(`${baseURL}/get_aircraft_track_list`, { - data_source_key: this.data_source.key, - icao: this.icao, - last_size: this.last_size - }) - return this.apply_track_data(res.data) - } - apply_track_data(data: any) { if (!data) { return [] @@ -508,7 +499,8 @@ export class Aircraft { } append_path() { - return this.load_track_data().then(list => this.append_loaded_path(list)) + this.data_source.request_aircraft_stream_update(); + return Promise.resolve(); } append_loaded_path(list: any[]) { @@ -548,7 +540,7 @@ export class Aircraft { show_path() { if (this.is_show_path == true) return; this.is_show_path = true; - this.append_path() + this.data_source.request_aircraft_stream_update(); } hide_path() { diff --git a/src/Map/Cesium_Map.tsx b/src/Map/Cesium_Map.tsx index 4111c52..4fd0a5c 100644 --- a/src/Map/Cesium_Map.tsx +++ b/src/Map/Cesium_Map.tsx @@ -77,7 +77,6 @@ type Aircraft_Position = { type Performance_Info = { fps: number frame_ms: number - cpu_pressure: number js_heap_mb: number | null gpu_renderer: string tile_state: string @@ -120,9 +119,7 @@ export class Cesium_Map extends enhance.Base { performance_frame_count = 0; performance_frame_time_total = 0; performance_last_render_time = 0; - performance_long_task_total = 0; - performance_observer: PerformanceObserver | null = null; - performance_info: Performance_Info = {fps: 0, frame_ms: 0, cpu_pressure: 0, js_heap_mb: null, gpu_renderer: "", tile_state: ""}; + performance_info: Performance_Info = {fps: 0, frame_ms: 0, js_heap_mb: null, gpu_renderer: "", tile_state: ""}; context_menu: Cesium_Context_Menu | null = null; track_point_selection_entity: Cesium.Entity | null = null; prevent_context_menu_listener: ((event: Event) => void) | null = null; @@ -176,8 +173,6 @@ export class Cesium_Map extends enhance.Base { window.clearInterval(this.performance_timer); this.performance_timer = null; } - this.performance_observer?.disconnect(); - this.performance_observer = null; if (this.viewer && this.post_render_listener) { this.viewer.scene.postRender.removeEventListener(this.post_render_listener); this.post_render_listener = null; @@ -430,16 +425,6 @@ export class Cesium_Map extends enhance.Base { this.post_render_listener = () => this.capture_performance_frame(); this.viewer.scene.postRender.addEventListener(this.post_render_listener); this.performance_timer = window.setInterval(() => this.flush_performance_info(), 1000); - try { - this.performance_observer = new PerformanceObserver((list) => { - for (const entry of list.getEntries()) { - this.performance_long_task_total += entry.duration; - } - }); - this.performance_observer.observe({entryTypes: ["longtask"]}); - } catch { - this.performance_observer = null; - } } capture_performance_frame() { const now = performance.now(); @@ -455,14 +440,12 @@ export class Cesium_Map extends enhance.Base { this.performance_info = { fps: frame_count, frame_ms, - cpu_pressure: Math.min(100, Math.round(this.performance_long_task_total / 10)), js_heap_mb: this.read_js_heap_mb(), gpu_renderer: this.performance_info.gpu_renderer, tile_state: this.viewer?.scene.globe.tilesLoaded ? "loaded" : "loading" }; this.performance_frame_count = 0; this.performance_frame_time_total = 0; - this.performance_long_task_total = 0; this.flush(); } read_js_heap_mb(): number | null { @@ -1266,10 +1249,9 @@ export class Cesium_Map extends enhance.Base { info.drawer.toggleOpen(); } aircraft.refresh_base_information(); - aircraft.load_track_data().then(() => { - if (!this.selected_aircraft || this.entity_key(this.selected_aircraft.data_source.key, this.selected_aircraft.icao) !== key) return; - this.sync_aircraft(key, aircraft); - }); + aircraft.is_show_path = true; + aircraft.data_source.request_aircraft_stream_update(); + this.sync_aircraft(key, aircraft); this.flush(); } clear_selected_aircraft() { @@ -1410,7 +1392,6 @@ export class Cesium_Map extends enhance.Base {
{`FPS ${info.fps}`}
{`Frame ${info.frame_ms.toFixed(1)} ms`}
-
{`CPU估算 ${info.cpu_pressure}%`}
{`JS堆 ${info.js_heap_mb === null ? "N/A" : `${info.js_heap_mb.toFixed(1)} MB`}`}
{`Tiles ${info.tile_state || "N/A"}`}
{`GPU ${info.gpu_renderer || "N/A"}`}