1- import { promisify } from "node:util" ;
2- import * as zlib from "node:zlib" ;
31import type { AirportDelta , AirportLong , AirportShort , PilotLong } from "@sr24/types/vatsim" ;
4- import axios from "axios" ;
5- import { parseStringPromise } from "xml2js" ;
6-
7- interface MetarXML {
8- response ?: {
9- data ?: Array < {
10- METAR ?: Array < {
11- station_id ?: string [ ] ;
12- raw_text ?: string [ ] ;
13- } > ;
14- } > ;
15- } ;
16- }
17-
18- interface TafXML {
19- response ?: {
20- data ?: Array < {
21- TAF ?: Array < {
22- station_id ?: string [ ] ;
23- raw_text ?: string [ ] ;
24- } > ;
25- } > ;
26- } ;
27- }
28-
29- const METAR_URL = "https://aviationweather.gov/data/cache/metars.cache.xml.gz" ;
30- const TAF_URL = "https://aviationweather.gov/data/cache/tafs.cache.xml.gz" ;
31- const WEATHER_FETCH_INTERVAL = 600_000 ;
322
333let cached : AirportLong [ ] = [ ] ;
344let updated : AirportShort [ ] = [ ] ;
355let added : Required < AirportShort > [ ] = [ ] ;
366
377export async function mapAirports ( pilotsLong : PilotLong [ ] ) : Promise < AirportLong [ ] > {
38- await updateWeather ( ) ;
39-
408 const airportRecord : Record < string , AirportLong > = { } ;
419 const routeRecord : Record < string , Map < string , number > > = { } ;
4210
@@ -179,8 +147,6 @@ function initAirportRecord(icao: string): AirportLong {
179147 arr_traffic : { traffic_count : 0 , average_delay : 0 , flights_delayed : 0 } ,
180148 busiest : { departure : "-" , arrival : "-" } ,
181149 unique : { departures : 0 , arrivals : 0 } ,
182- metar : getMetar ( icao ) ,
183- taf : getTaf ( icao ) ,
184150 } ;
185151}
186152
@@ -199,72 +165,3 @@ function calculateArrivalDelay(pilot: PilotLong): number {
199165
200166 return Math . min ( Math . max ( delay_min , 0 ) , 120 ) ;
201167}
202-
203- const gunzip = promisify ( zlib . gunzip ) ;
204- let metarCache : Map < string , string > = new Map ( ) ;
205- let tafCache : Map < string , string > = new Map ( ) ;
206- let lastWeatherFetch = 0 ;
207-
208- async function fetchWeather ( url : string ) : Promise < MetarXML | TafXML > {
209- const response = await axios . get < Buffer > ( url , {
210- responseType : "arraybuffer" ,
211- } ) ;
212-
213- const decompressed = await gunzip ( response . data ) ;
214- const xml = decompressed . toString ( "utf-8" ) ;
215-
216- const parsed = ( await parseStringPromise ( xml ) ) as MetarXML | TafXML ;
217-
218- return parsed ;
219- }
220-
221- async function updateWeather ( ) : Promise < void > {
222- if ( Date . now ( ) - lastWeatherFetch < WEATHER_FETCH_INTERVAL ) {
223- return ;
224- }
225- lastWeatherFetch = Date . now ( ) ;
226-
227- try {
228- const parsedMetar = ( await fetchWeather ( METAR_URL ) ) as MetarXML ;
229- const parsedTaf = ( await fetchWeather ( TAF_URL ) ) as TafXML ;
230-
231- const metars = parsedMetar ?. response ?. data ?. [ 0 ] ?. METAR || [ ] ;
232- const tafs = parsedTaf ?. response ?. data ?. [ 0 ] ?. TAF || [ ] ;
233-
234- const newMetarCache = new Map < string , string > ( ) ;
235- const newTafCache = new Map < string , string > ( ) ;
236-
237- for ( const metar of metars ) {
238- const icao = metar . station_id ?. [ 0 ] ;
239- const raw = metar . raw_text ?. [ 0 ] ;
240-
241- if ( icao && raw ) {
242- newMetarCache . set ( icao , raw ) ;
243- }
244- }
245-
246- for ( const taf of tafs ) {
247- const icao = taf . station_id ?. [ 0 ] ;
248- const raw = taf . raw_text ?. [ 0 ] ;
249-
250- if ( icao && raw ) {
251- newTafCache . set ( icao , raw ) ;
252- }
253- }
254-
255- metarCache = newMetarCache ;
256- tafCache = newTafCache ;
257-
258- // console.log(`✅ Updated ${metarCache.size} METAR entries and ${tafCache.size} TAF entries`);
259- } catch ( error ) {
260- console . error ( "❌ Error fetching weather data:" , error instanceof Error ? error . message : error ) ;
261- }
262- }
263-
264- export function getMetar ( icao : string ) : string | null {
265- return metarCache . get ( icao ) || null ;
266- }
267-
268- export function getTaf ( icao : string ) : string | null {
269- return tafCache . get ( icao ) || null ;
270- }
0 commit comments