diff --git a/src/hooks/use-traffic-monitor.ts b/src/hooks/use-traffic-monitor.ts index 97fe30f7c..242fedaa1 100644 --- a/src/hooks/use-traffic-monitor.ts +++ b/src/hooks/use-traffic-monitor.ts @@ -3,7 +3,6 @@ import { Traffic } from 'tauri-plugin-mihomo-api' import { useVisibility } from '@/hooks/use-visibility' import { debugLog } from '@/utils/debug' -import { TrafficDataSampler, isSameTrafficData } from '@/utils/traffic-sampler' // 引用计数管理器 class ReferenceCounter { @@ -32,119 +31,11 @@ const WORKER_CONFIG = { defaultRangeMinutes: 10, } -class InlineTrafficMonitor { - private config = { ...WORKER_CONFIG } - private sampler = new TrafficDataSampler(this.config) - private throttleTimer: ReturnType | null = null - private agingTimer: ReturnType | null = null - private currentRange = this.config.defaultRangeMinutes - private lastTimestamp: number | undefined - private lastBroadcast: ITrafficDataPoint[] = [] - - constructor( - private emit: (snapshot: ITrafficWorkerSnapshotMessage) => void, - ) {} - - start(rangeMinutes?: number) { - this.currentRange = rangeMinutes ?? this.currentRange - this.handle({ - type: 'init', - config: { - ...this.config, - defaultRangeMinutes: this.currentRange, - }, - }) - } - - stop() { - if (this.throttleTimer !== null) { - clearTimeout(this.throttleTimer) - this.throttleTimer = null - } - if (this.agingTimer !== null) { - clearInterval(this.agingTimer) - this.agingTimer = null - } - this.sampler.clear() - this.lastTimestamp = undefined - } - - handle(message: TrafficWorkerRequestMessage) { - switch (message.type) { - case 'init': { - this.config = { ...message.config } - this.sampler = new TrafficDataSampler(this.config) - this.currentRange = message.config.defaultRangeMinutes - // Re-broadcast when points age out of the range, not on every tick - this.agingTimer = setInterval(() => { - if (this.throttleTimer !== null) return - const slice = this.sampler.getDataForTimeRange(this.currentRange) - if (!isSameTrafficData(this.lastBroadcast, slice)) { - this.emitSnapshot() - } - }, 1000) - this.emitSnapshot() - break - } - case 'append': { - const timestamp = message.payload.timestamp ?? Date.now() - const dataPoint: ITrafficDataPoint = { - up: message.payload.up || 0, - down: message.payload.down || 0, - timestamp, - } - - this.lastTimestamp = timestamp - this.sampler.addDataPoint(dataPoint) - this.scheduleSnapshot() - break - } - case 'setRange': { - if (this.currentRange !== message.minutes) { - this.currentRange = message.minutes - this.emitSnapshot() - } - break - } - case 'requestSnapshot': { - this.emitSnapshot() - break - } - default: - break - } - } - - private emitSnapshot() { - const dataPoints = this.sampler.getDataForTimeRange(this.currentRange) - - this.emit({ - type: 'snapshot', - dataPoints, - samplerStats: this.sampler.getStats(), - lastTimestamp: this.lastTimestamp, - }) - this.lastBroadcast = dataPoints - } - - private scheduleSnapshot() { - if (this.throttleTimer !== null) return - this.throttleTimer = setTimeout(() => { - this.throttleTimer = null - this.emitSnapshot() - }, this.config.snapshotIntervalMs) - } -} - class TrafficWorkerClient { private worker: Worker | null = null - private inlineMonitor: InlineTrafficMonitor | null = null - private mode: 'worker' | 'inline' | null = null private listeners = new Set< (snapshot: ITrafficWorkerSnapshotMessage) => void >() - private pendingMessages: TrafficWorkerRequestMessage[] = [] - private ready = false private currentRange = WORKER_CONFIG.defaultRangeMinutes start(rangeMinutes?: number) { @@ -155,101 +46,52 @@ class TrafficWorkerClient { this.currentRange = rangeMinutes ?? this.currentRange - if (this.ready) return + if (this.worker) return const initMessage: TrafficWorkerRequestMessage = { type: 'init', - config: { - rawDataMinutes: WORKER_CONFIG.rawDataMinutes, - compressedDataMinutes: WORKER_CONFIG.compressedDataMinutes, - compressionRatio: WORKER_CONFIG.compressionRatio, - snapshotIntervalMs: WORKER_CONFIG.snapshotIntervalMs, - defaultRangeMinutes: this.currentRange, - }, + config: { ...WORKER_CONFIG, defaultRangeMinutes: this.currentRange }, } - if (typeof Worker !== 'undefined') { - try { - this.worker = new Worker( - new URL('../services/traffic-monitor-worker.ts', import.meta.url), - { type: 'module' }, - ) - this.mode = 'worker' + this.worker = new Worker( + new URL('../services/traffic-monitor-worker.ts', import.meta.url), + { type: 'module' }, + ) - this.worker.onmessage = ( - event: MessageEvent, - ) => { - const message = event.data - if (message.type === 'snapshot') { - if (import.meta.env.MODE === 'perf') { - document.documentElement.dataset.perfWorker = String( - message.lastTimestamp ?? 0, - ) - document.documentElement.dataset.perfPoints = String( - message.dataPoints.length, - ) - const last = message.dataPoints.at(-1) - document.documentElement.dataset.perfLastUp = String( - last?.up ?? 0, - ) - document.documentElement.dataset.perfLastDown = String( - last?.down ?? 0, - ) - } - this.listeners.forEach((listener) => { - listener(message) - }) - } + this.worker.onmessage = ( + event: MessageEvent, + ) => { + const message = event.data + if (message.type === 'snapshot') { + if (import.meta.env.MODE === 'perf') { + document.documentElement.dataset.perfWorker = String( + message.lastTimestamp ?? 0, + ) + document.documentElement.dataset.perfPoints = String( + message.dataPoints.length, + ) + const last = message.dataPoints.at(-1) + document.documentElement.dataset.perfLastUp = String(last?.up ?? 0) + document.documentElement.dataset.perfLastDown = String( + last?.down ?? 0, + ) } - - this.worker.onerror = (error) => { - debugLog(`[TrafficWorkerClient] Worker error: ${String(error)}`) - } - - this.ready = true - this.post(initMessage) - this.flushQueue() - return - } catch (error) { - debugLog( - `[TrafficWorkerClient] Worker initialization failed, falling back to inline sampler: ${String(error)}`, - ) - this.worker = null - this.mode = null + this.listeners.forEach((listener) => { + listener(message) + }) } - } else { - debugLog( - '[TrafficWorkerClient] Worker not supported, using inline sampler', - ) } - this.startInline(initMessage) - } + this.worker.onerror = (error) => { + debugLog(`[TrafficWorkerClient] Worker error: ${String(error)}`) + } - private startInline(initMessage: TrafficWorkerRequestMessage) { - this.inlineMonitor = new InlineTrafficMonitor((snapshot) => { - this.listeners.forEach((listener) => { - listener(snapshot) - }) - }) - this.mode = 'inline' - this.ready = true this.post(initMessage) - this.flushQueue() } stop() { - if (this.worker) { - this.worker.terminate() - } - if (this.inlineMonitor) { - this.inlineMonitor.stop() - } + this.worker?.terminate() this.worker = null - this.inlineMonitor = null - this.mode = null - this.ready = false - this.pendingMessages = [] } onSnapshot(listener: (snapshot: ITrafficWorkerSnapshotMessage) => void) { @@ -260,37 +102,11 @@ class TrafficWorkerClient { } private post(message: TrafficWorkerRequestMessage) { - if (!this.ready) { - this.pendingMessages.push(message) - return - } - - if (this.mode === 'worker' && this.worker) { - this.worker.postMessage(message) - return - } - - if (this.mode === 'inline' && this.inlineMonitor) { - this.inlineMonitor.handle(message) - return - } - - this.pendingMessages.push(message) - } - - private flushQueue() { - if (!this.ready || this.pendingMessages.length === 0) { - return - } - const queued = [...this.pendingMessages] - this.pendingMessages = [] - queued.forEach((message) => { - this.post(message) - }) + this.worker?.postMessage(message) } private ensureStarted() { - if (!this.ready) { + if (!this.worker) { this.start(this.currentRange) } }