import React, {useEffect, useState} from 'react'; import PanelWrapper from "../../meta/PanelWrapper"; import PlanElement from "./components/PlanElement"; import PanelTitle from "../../meta/PanelTitle"; import PanelContent from "../../meta/PanelContent"; import {StationResponse} from "./types/vrrfAPI"; import {Warning} from "phosphor-react"; export type FahrplanPanelDefinition = { stops: string[], filter: { types?: string[], destinations?: string[] } } type Route = { uid: string, identifier: string, heading: string, stops: { name: string, arrival: Date, delay?: number }[] } const FahrplanPanel = (props: {definition: FahrplanPanelDefinition}) => { const [routes, setRoutes] = useState<Route[]>([]); useEffect(() => { const update = async () => { const departures = (await Promise.all(props.definition.stops.map(getStopData))) .map(d => d.raw) .flat(); let newRoutes: Route[] = []; // Determine stop data from the departures for(let departure of departures) { // First throw away all data that belongs to a filtered category if((props.definition.filter.types ?? []).includes(departure.type)) { continue; } // Find existing route with same uid const existing_ind = newRoutes.findIndex(r => r.uid === departure.lineref.identifier) // Pre-compute values that will be needed regardless const delay = stringToDelay(departure.delay); const arrival = processArrival(departure.sched_date, departure.time); console.log(arrival) if(existing_ind === -1) { // If it does not exist, create a new route newRoutes.push({ uid: departure.key, heading: departure.lineref.direction, identifier: departure.line, stops: [ { name: departure.internal.stop, arrival, delay } ] }) } else { // If it doesn't, just add a stop to the existing route newRoutes[existing_ind].stops.push({ name: departure.internal.stop, arrival, delay: stringToDelay(departure.delay) }) } } // Sort the output // Write to the display setRoutes(newRoutes); } update(); const interval = setInterval(update, 2 * 60 * 1000); return () => { clearInterval(interval); } }, []); return ( <PanelWrapper> <PanelTitle title={"ÖPNV Monitor"}/> <PanelContent className={"flex flex-col"}> <div className={"flex-1 flex flex-col gap-3"}> {routes.map((route) => ( <PlanElement key={route.uid} tripId={route.uid} trainIdentifier={route.identifier} trainHeading={route.heading} stops={route.stops} /> ))} {routes.length === 0 && ( <div className={"flex-1 flex justify-center items-center"}> <div className={"mb-10 flex flex-col items-center"}> <Warning size={48} className={"mb-3"}/> <p className={"max-w-xs text-center text-zinc-400"}> Aktuell sind keine Abfahrtsdaten verfügbar. </p> </div> </div> )} </div> </PanelContent> </PanelWrapper> ); }; export default FahrplanPanel; async function getStopData(stop: string): Promise<StationResponse> { const request = await fetch(`https://vrrf.finalrewind.org/${encodeURIComponent(stop)}.json`); if(!request.ok) { throw new Error("Converting stop did not work"); } const data = await request.json(); if(data.error) { console.warn("Stop data for", stop, "could not be fetched"); } // Add internal reference data data.raw = data.raw.map((r: any) => ({ ...r, internal: { stop } })) console.log(data); return data as StationResponse; } function stringToDelay(input: string): number | undefined { try { const delay = parseInt(input); if(delay === 0) { return undefined; } return delay; } catch (e) { console.warn("While parsing delay, the string was not interpretable", input); return undefined; } } function processArrival(date: string, time: string): Date { const d_parts = date.split("."); console.log(date, time, "to", `${d_parts[2]}-${d_parts[1]}-${d_parts[0]} ${time}`); return new Date(`${d_parts[2]}-${d_parts[1]}-${d_parts[0]} ${time}`); } // function sortData(data: any) { // for (var i = 0; i < data.length; ++i) { // data[i]['stops'] = data[i]['stops'].sort(sortFn); // data[i]['timeValue'] = data[i]['stops'][0]['timeValue']; // } // return data.sort(sortFn); // } // // function sortFn(a: any, b: any) { // return a['timeValue'] - b['timeValue']; // } // // function calcDateValue(_year: string, _month: string, _day: string, _hour: string, _minute: string): number { // const year = parseInt(_year) * 12 * 31 * 24 * 60; // const month = parseInt(_month) * 31 * 24 * 60; // const day = parseInt(_day) * 24 * 60; // const hour = parseInt(_hour) * 60; // const minute = parseInt(_minute); // return year+month+day+hour+minute; // }