Compare commits
	
		
			No commits in common. "0d7b3848211fbd0f450bf3677911749c5a4fd6c1" and "2520d03d1385eef9013a4d902282248dae04f77d" have entirely different histories.
		
	
	
		
			0d7b384821
			...
			2520d03d13
		
	
		|  | @ -1,22 +1,7 @@ | ||||||
| const tam = require('./sources/tam'); | const tam = require('./sources/tam'); | ||||||
| const util = require('../util'); | 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; | let nextUpdate = null; | ||||||
| 
 |  | ||||||
| // Current information about courses
 |  | ||||||
| let currentCourses = null; | let currentCourses = null; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -29,10 +14,10 @@ let currentCourses = null; | ||||||
|  * |  * | ||||||
|  * - `id`: Unique identifier for the course. |  * - `id`: Unique identifier for the course. | ||||||
|  * - `line`: Line number. |  * - `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. |  * - `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. |  * @return Mapping from active course IDs to information about each course. | ||||||
|  */ |  */ | ||||||
|  | @ -57,13 +42,6 @@ const getCourses = () => new Promise((res, rej) => | ||||||
| 
 | 
 | ||||||
|         if (!util.isObject(entry)) |         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; |             currentCourses = courses; | ||||||
|             res(currentCourses); |             res(currentCourses); | ||||||
|             return; |             return; | ||||||
|  | @ -71,7 +49,6 @@ const getCourses = () => new Promise((res, rej) => | ||||||
| 
 | 
 | ||||||
|         if ('lastUpdate' in entry) |         if ('lastUpdate' in entry) | ||||||
|         { |         { | ||||||
|             // Metadata header
 |  | ||||||
|             lastUpdate = entry.lastUpdate; |             lastUpdate = entry.lastUpdate; | ||||||
|             nextUpdate = entry.nextUpdate; |             nextUpdate = entry.nextUpdate; | ||||||
|             return; |             return; | ||||||
|  | @ -80,7 +57,7 @@ const getCourses = () => new Promise((res, rej) => | ||||||
|         const { |         const { | ||||||
|             course: id, |             course: id, | ||||||
|             routeShortName: line, |             routeShortName: line, | ||||||
|             stopId, |             stopId: nextStop, | ||||||
|             destArCode: finalStop, |             destArCode: finalStop, | ||||||
|         } = entry; |         } = entry; | ||||||
| 
 | 
 | ||||||
|  | @ -88,14 +65,14 @@ const getCourses = () => new Promise((res, rej) => | ||||||
| 
 | 
 | ||||||
|         if (!(id in courses)) |         if (!(id in courses)) | ||||||
|         { |         { | ||||||
|             courses[id] = { |             courses[id] = {id, line, nextStop, arrivalTime, finalStop}; | ||||||
|                 id, line, finalStop, |  | ||||||
|                 nextPassings: [{stopId, arrivalTime}], |  | ||||||
|             }; |  | ||||||
|         } |         } | ||||||
|         else |         else if (arrivalTime < courses[id].arrivalTime) | ||||||
|         { |         { | ||||||
|             courses[id].nextPassings.push({stopId, arrivalTime}); |             // The stop where the next passing is soonest is assumed
 | ||||||
|  |             // to be the next stop
 | ||||||
|  |             courses[id].nextStop = nextStop; | ||||||
|  |             courses[id].arrivalTime = arrivalTime; | ||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
| }); | }); | ||||||
|  |  | ||||||
							
								
								
									
										191
									
								
								front/data.js
								
								
								
								
							
							
						
						
									
										191
									
								
								front/data.js
								
								
								
								
							|  | @ -1,195 +1,32 @@ | ||||||
| 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 updateFromTam = async (courses) => | ||||||
| { | { | ||||||
|     const currentCourses = (await axios.get(`${server}/courses`)).data; |     const currentCourses = await getCurrentCourses(); | ||||||
| 
 | 
 | ||||||
|     for (let [id, course] of Object.entries(currentCourses)) |     for (let [id, course] of Object.entries(currentCourses)) | ||||||
|     { |     { | ||||||
|         // Only track selected lines
 |         if (!(id in courses)) | ||||||
|         if (!['1', '2', '3', '4'].includes(course.line)) |  | ||||||
|         { |         { | ||||||
|             continue; |             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; | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         // 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 |         else | ||||||
|         { |         { | ||||||
|             courses[id] = { |             course.arrivalTime = now() + course.eta; | ||||||
|                 id, |             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)})`); | ||||||
|                 line: course.line, |             courses[id] = course; | ||||||
|                 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 updatePositions = (courses, time) => |  | ||||||
| { |  | ||||||
|     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; |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | const sleep = time => new Promise(res => setTimeout(res, time)); | ||||||
| const courses = {}; | const courses = {}; | ||||||
| let lastFrame = null; |  | ||||||
| let lastUpdate = null; |  | ||||||
| 
 | 
 | ||||||
| const loop = now => | const loop = async (courses = {}) => | ||||||
| { | { | ||||||
|     const time = lastFrame === null ? 0 : now - lastFrame; |     await updateFromTam(courses); | ||||||
| 
 |     await sleep(30000); | ||||||
|     lastFrame = now; |     return loop(courses); | ||||||
|     updatePositions(courses, time); |  | ||||||
| 
 |  | ||||||
|     if (lastUpdate === null || lastUpdate + 5000 <= now) |  | ||||||
|     { |  | ||||||
|         lastUpdate = now; |  | ||||||
|         updateFromTam(courses); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     requestAnimationFrame(loop); |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| requestAnimationFrame(loop); | loop(); | ||||||
|  |  | ||||||
|  | @ -1,7 +1,4 @@ | ||||||
| require('regenerator-runtime/runtime'); | require('regenerator-runtime/runtime'); | ||||||
| 
 | 
 | ||||||
| const {createMap} = require('./map'); | const {createMap} = require('./map'); | ||||||
| 
 |  | ||||||
| require('./data'); |  | ||||||
| 
 |  | ||||||
| createMap(/* map = */ 'map'); | createMap(/* map = */ 'map'); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue