tracktracker/src/tam/realtime.js

101 lines
3.2 KiB
JavaScript

const tam = require("./sources/tam");
const network = require("./network.json");
// Time at which the course data needs to be updated next
let nextUpdate = null;
// Current information about courses
let currentCourses = null;
/**
* Information about the course of a vehicle.
* @typedef {Object} Course
* @property {string} id Unique identifier for this course.
* @property {string} line Transport line number.
* @property {string} finalStop Final stop to which the course is headed.
* @property {Array.<Array>} nextPassings Next stations to which
* the vehicle will stop, associated to the passing timestamp, ordered by
* increasing passing timestamp.
*/
/**
* Fetch real-time information about active courses in the TaM network.
*
* New data will only be fetched from the TaM server once every minute,
* otherwise pulling from the in-memory cache.
* @returns {Object.<string,Course>} Mapping from active course IDs to
* information about each course.
*/
const fetch = async() => {
if (nextUpdate === null || Date.now() >= nextUpdate) {
const courses = {};
const passings = tam.fetchRealtime();
const timing = (await passings.next()).value;
nextUpdate = timing.nextUpdate;
// Aggregate passings relative to the same course
for await (const passing of passings) {
const {
course: id,
routeShortName: line,
stopId,
destArCode: finalStop
} = passing;
const arrivalTime = (
timing.lastUpdate +
parseInt(passing.delaySec, 10) * 1000
);
if (!(id in courses)) {
courses[id] = {
id,
line,
finalStop,
// Initially accumulate passings in an object
// to prevent duplicates
nextPassings: { [stopId]: arrivalTime }
};
} else if (!(stopId in courses[id].nextPassings) ||
courses[id].nextPassings[stopId] < arrivalTime) {
// Only consider passings with an increased passing time
// or for stops not seen before
courses[id].nextPassings[stopId] = arrivalTime;
}
}
// Filter courses to only keep those referring to known data
for (const courseId of Object.keys(courses)) {
const course = courses[courseId];
if (!(course.line in network.lines)) {
delete courses[courseId];
} else {
for (const stopId of Object.keys(course.nextPassings)) {
if (!(stopId in network.stops)) {
delete courses[courseId];
break;
}
}
}
}
// Order next passings by increasing passing time
for (const courseId of Object.keys(courses)) {
courses[courseId].nextPassings = (
Object.entries(courses[courseId].nextPassings).sort(
([, time1], [, time2]) => time1 - time2
)
);
}
currentCourses = courses;
}
return currentCourses;
};
exports.fetch = fetch;