Compare commits
	
		
			2 Commits
		
	
	
		
			2520d03d13
			...
			0d7b384821
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 0d7b384821 | |
|  | d5362041ca | 
|  | @ -1,7 +1,22 @@ | |||
| const tam = require('./sources/tam'); | ||||
| const util = require('../util'); | ||||
| 
 | ||||
| /** | ||||
|  * Comparison function between two stop passings. | ||||
|  * | ||||
|  * @param passing1 First stop passing. | ||||
|  * @param passing2 Second stop passing. | ||||
|  * @return Negative value if passing1 is sooner than passing2, positive | ||||
|  * otherwise, zero if they occur at the same time. | ||||
|  */ | ||||
| const passingCompare = ({arrivalTime: time1}, {arrivalTime: time2}) => ( | ||||
|     time1 - time2 | ||||
| ); | ||||
| 
 | ||||
| // Time at which the course data needs to be updated next
 | ||||
| let nextUpdate = null; | ||||
| 
 | ||||
| // Current information about courses
 | ||||
| let currentCourses = null; | ||||
| 
 | ||||
| /** | ||||
|  | @ -14,10 +29,10 @@ let currentCourses = null; | |||
|  * | ||||
|  * - `id`: Unique identifier for the course. | ||||
|  * - `line`: Line number. | ||||
|  * - `nextStop`: Identifier of the next stop of the course. | ||||
|  * - `arrivalTime`: Timestamp at which the vehicle is predicted to arrive | ||||
|  *                  to the next stop. | ||||
|  * - `finalStop`: The final stop to which the course is headed. | ||||
|  * - `nextPassings`: Next passings of the vehicle, sorted by increasing | ||||
|  *   arrival time, containing both the stop identifier (`stopId`) and the | ||||
|  *   expected arrival timestamp (`arrivalTime`). | ||||
|  * | ||||
|  * @return Mapping from active course IDs to information about each course. | ||||
|  */ | ||||
|  | @ -42,6 +57,13 @@ const getCourses = () => new Promise((res, rej) => | |||
| 
 | ||||
|         if (!util.isObject(entry)) | ||||
|         { | ||||
|             // End of courses information stream. Sort next stops by increasing
 | ||||
|             // arrival time in each course then save result in memory cache
 | ||||
|             for (let course of Object.values(courses)) | ||||
|             { | ||||
|                 course.nextPassings.sort(passingCompare); | ||||
|             } | ||||
| 
 | ||||
|             currentCourses = courses; | ||||
|             res(currentCourses); | ||||
|             return; | ||||
|  | @ -49,6 +71,7 @@ const getCourses = () => new Promise((res, rej) => | |||
| 
 | ||||
|         if ('lastUpdate' in entry) | ||||
|         { | ||||
|             // Metadata header
 | ||||
|             lastUpdate = entry.lastUpdate; | ||||
|             nextUpdate = entry.nextUpdate; | ||||
|             return; | ||||
|  | @ -57,7 +80,7 @@ const getCourses = () => new Promise((res, rej) => | |||
|         const { | ||||
|             course: id, | ||||
|             routeShortName: line, | ||||
|             stopId: nextStop, | ||||
|             stopId, | ||||
|             destArCode: finalStop, | ||||
|         } = entry; | ||||
| 
 | ||||
|  | @ -65,14 +88,14 @@ const getCourses = () => new Promise((res, rej) => | |||
| 
 | ||||
|         if (!(id in courses)) | ||||
|         { | ||||
|             courses[id] = {id, line, nextStop, arrivalTime, finalStop}; | ||||
|             courses[id] = { | ||||
|                 id, line, finalStop, | ||||
|                 nextPassings: [{stopId, arrivalTime}], | ||||
|             }; | ||||
|         } | ||||
|         else if (arrivalTime < courses[id].arrivalTime) | ||||
|         else | ||||
|         { | ||||
|             // The stop where the next passing is soonest is assumed
 | ||||
|             // to be the next stop
 | ||||
|             courses[id].nextStop = nextStop; | ||||
|             courses[id].arrivalTime = arrivalTime; | ||||
|             courses[id].nextPassings.push({stopId, arrivalTime}); | ||||
|         } | ||||
|     }); | ||||
| }); | ||||
|  |  | |||
							
								
								
									
										195
									
								
								front/data.js
								
								
								
								
							
							
						
						
									
										195
									
								
								front/data.js
								
								
								
								
							|  | @ -1,32 +1,195 @@ | |||
| const axios = require('axios'); | ||||
| const network = require('../back/data/network.json'); | ||||
| 
 | ||||
| const server = 'http://localhost:4321'; | ||||
| 
 | ||||
| const arriveAtStop = (course, stop) => | ||||
| { | ||||
|     course.state = 'stopped'; | ||||
|     course.currentStop = stop; | ||||
|     delete course.departureStop; | ||||
|     delete course.arrivalStop; | ||||
|     delete course.arrivalTime; | ||||
|     delete course.traveledDistance; | ||||
|     delete course.speed; | ||||
| }; | ||||
| 
 | ||||
| const moveToStop = (course, stop, arrivalTime) => | ||||
| { | ||||
|     course.state = 'moving'; | ||||
|     course.departureStop = course.currentStop; | ||||
|     course.arrivalStop = stop; | ||||
|     course.arrivalTime = arrivalTime; | ||||
|     course.traveledDistance = 0; | ||||
|     course.speed = 0; | ||||
|     delete course.currentStop; | ||||
| 
 | ||||
|     const segment = `${course.departureStop}-${course.arrivalStop}`; | ||||
| 
 | ||||
|     if (!(segment in network.segments)) | ||||
|     { | ||||
|         // There is no segment between the two requested stops, jump
 | ||||
|         // directly to the arrival stop
 | ||||
|         arriveAtStop(course, course.arrivalStop); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         updateSpeed(course); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| const getCurrentSegment = course => | ||||
| { | ||||
|     if (course.state === 'stopped') | ||||
|     { | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     return network.segments[`${course.departureStop}-${course.arrivalStop}`]; | ||||
| }; | ||||
| 
 | ||||
| const updateSpeed = course => | ||||
| { | ||||
|     const segment = getCurrentSegment(course); | ||||
|     const length = segment.points[segment.points.length - 1].distance; | ||||
| 
 | ||||
|     const remainingTime = course.arrivalTime - Date.now(); | ||||
|     const remainingDistance = length - course.traveledDistance; | ||||
| 
 | ||||
|     if (remainingTime <= 0 || remainingDistance <= 0) | ||||
|     { | ||||
|         arriveAtStop(course, course.arrivalStop); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     course.speed = remainingDistance / remainingTime; | ||||
| }; | ||||
| 
 | ||||
| const updateFromTam = async (courses) => | ||||
| { | ||||
|     const currentCourses = await getCurrentCourses(); | ||||
|     const currentCourses = (await axios.get(`${server}/courses`)).data; | ||||
| 
 | ||||
|     for (let [id, course] of Object.entries(currentCourses)) | ||||
|     { | ||||
|         if (!(id in courses)) | ||||
|         // Only track selected lines
 | ||||
|         if (!['1', '2', '3', '4'].includes(course.line)) | ||||
|         { | ||||
|             course.arrivalTime = now() + course.eta; | ||||
|             console.log(`${displayNow(now())} - New course ${id} @ ${course.line} departing from stop ${course.stop} at ${displayNow(course.arrivalTime)}`); | ||||
|             courses[id] = course; | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         // Find out the next stop, ignoring the ones that are in the past
 | ||||
|         let nextStop = null; | ||||
|         let arrivalTime = null; | ||||
| 
 | ||||
|         for (let {stopId, arrivalTime: time} of course.nextPassings) | ||||
|         { | ||||
|             if (time > Date.now()) | ||||
|             { | ||||
|                 nextStop = stopId; | ||||
|                 arrivalTime = time; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (nextStop === null) | ||||
|         { | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         // Update an existing course
 | ||||
|         if (id in courses) | ||||
|         { | ||||
|             const prev = courses[id]; | ||||
| 
 | ||||
|             if (prev.state === 'stopped') | ||||
|             { | ||||
|                 if (prev.currentStop !== nextStop) | ||||
|                 { | ||||
|                     // Start traveling from the current stop to the next
 | ||||
|                     moveToStop(prev, nextStop, arrivalTime); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 // Update the ETA if we’re still headed to the same stop
 | ||||
|                 if (prev.arrivalStop === nextStop) | ||||
|                 { | ||||
|                     prev.arrivalTime = arrivalTime; | ||||
|                     updateSpeed(prev); | ||||
|                 } | ||||
|                 // Otherwise, we missed a stop, try to go directly to the
 | ||||
|                 // next segment
 | ||||
|                 else | ||||
|                 { | ||||
|                     arriveAtStop(prev, prev.arrivalStop); | ||||
|                     moveToStop(prev, nextStop, arrivalTime); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         // Create a new course
 | ||||
|         else | ||||
|         { | ||||
|             course.arrivalTime = now() + course.eta; | ||||
|             console.log(`${displayNow(now())} - Course ${id} @ ${course.line} will arrive to stop ${course.stop} at ${displayNow(course.arrivalTime)} (previously to stop ${courses[id].stop} at ${displayNow(courses[id].arrivalTime)})`); | ||||
|             courses[id] = course; | ||||
|             courses[id] = { | ||||
|                 id, | ||||
|                 line: course.line, | ||||
|                 finalStop: course.finalStop, | ||||
|             }; | ||||
| 
 | ||||
|             arriveAtStop(courses[id], nextStop); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Remove stale courses
 | ||||
|     for (let id of Object.keys(courses)) | ||||
|     { | ||||
|         if (!(id in currentCourses)) | ||||
|         { | ||||
|             delete courses[id]; | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| const sleep = time => new Promise(res => setTimeout(res, time)); | ||||
| const courses = {}; | ||||
| 
 | ||||
| const loop = async (courses = {}) => | ||||
| const updatePositions = (courses, time) => | ||||
| { | ||||
|     await updateFromTam(courses); | ||||
|     await sleep(30000); | ||||
|     return loop(courses); | ||||
|     for (let [id, course] of Object.entries(courses)) | ||||
|     { | ||||
|         if (course.state === 'moving') | ||||
|         { | ||||
|             const delta = course.speed * time; | ||||
| 
 | ||||
|             const segment = getCurrentSegment(course); | ||||
|             const length = segment.points[segment.points.length - 1].distance; | ||||
| 
 | ||||
|             if (course.traveledDistance + delta >= length) | ||||
|             { | ||||
|                 course.traveledDistance = length; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 course.traveledDistance += delta; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| loop(); | ||||
| const courses = {}; | ||||
| let lastFrame = null; | ||||
| let lastUpdate = null; | ||||
| 
 | ||||
| const loop = now => | ||||
| { | ||||
|     const time = lastFrame === null ? 0 : now - lastFrame; | ||||
| 
 | ||||
|     lastFrame = now; | ||||
|     updatePositions(courses, time); | ||||
| 
 | ||||
|     if (lastUpdate === null || lastUpdate + 5000 <= now) | ||||
|     { | ||||
|         lastUpdate = now; | ||||
|         updateFromTam(courses); | ||||
|     } | ||||
| 
 | ||||
|     requestAnimationFrame(loop); | ||||
| }; | ||||
| 
 | ||||
| requestAnimationFrame(loop); | ||||
|  |  | |||
|  | @ -1,4 +1,7 @@ | |||
| require('regenerator-runtime/runtime'); | ||||
| 
 | ||||
| const {createMap} = require('./map'); | ||||
| 
 | ||||
| require('./data'); | ||||
| 
 | ||||
| createMap(/* map = */ 'map'); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue