websocket

This commit is contained in:
2026-07-27 16:16:03 +08:00
parent 272f04bb5c
commit ea34f78438
3 changed files with 173 additions and 104 deletions
+166 -70
View File
@@ -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<string> = new Set();
monitor_all_aircraft_mode: boolean = false;
aircraft_change_versions: Map<string, string> = 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<string, Aircraft> = 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<string>();
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 (
<div> Data Source Center!</div>
@@ -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<string> {
const ret = new Set<string>();
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 = "";
+3 -11
View File
@@ -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() {
+4 -23
View File
@@ -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 {
<div style={{position: "absolute", left: 12, top: 92, zIndex: 2, minWidth: 170, maxWidth: 260, padding: "8px 10px", borderRadius: 4, background: "rgba(30, 30, 30, 0.72)", color: "#f6f6f6", fontSize: 12, lineHeight: 1.45, pointerEvents: "none"}}>
<div style={{fontWeight: 700, color: "#fff000"}}>{`FPS ${info.fps}`}</div>
<div>{`Frame ${info.frame_ms.toFixed(1)} ms`}</div>
<div>{`CPU估算 ${info.cpu_pressure}%`}</div>
<div>{`JS堆 ${info.js_heap_mb === null ? "N/A" : `${info.js_heap_mb.toFixed(1)} MB`}`}</div>
<div>{`Tiles ${info.tile_state || "N/A"}`}</div>
<div style={{color: "#ff8a33", wordBreak: "break-word"}}>{`GPU ${info.gpu_renderer || "N/A"}`}</div>