diff --git a/src/repositories/shuttle/eta/InMemorySelfUpdatingETARepository.ts b/src/repositories/shuttle/eta/InMemorySelfUpdatingETARepository.ts index 14a6a1a..5503939 100644 --- a/src/repositories/shuttle/eta/InMemorySelfUpdatingETARepository.ts +++ b/src/repositories/shuttle/eta/InMemorySelfUpdatingETARepository.ts @@ -1,7 +1,7 @@ import { SelfUpdatingETARepository } from "./SelfUpdatingETARepository"; import { ShuttleGetterRepository, ShuttleRepositoryEvent, ShuttleStopArrival, ShuttleTravelTimeDataIdentifier, ShuttleTravelTimeDateFilterArguments } from "../ShuttleGetterRepository"; import { BaseInMemoryETARepository } from "./BaseInMemoryETARepository"; -import { IShuttle } from "../../../entities/ShuttleRepositoryEntities"; +import { IOrderedStop, IShuttle } from "../../../entities/ShuttleRepositoryEntities"; export class InMemorySelfUpdatingETARepository extends BaseInMemoryETARepository implements SelfUpdatingETARepository { private referenceTime: Date | null = null; @@ -54,6 +54,11 @@ export class InMemorySelfUpdatingETARepository extends BaseInMemoryETARepository this.shuttleRepository.addListener(ShuttleRepositoryEvent.SHUTTLE_WILL_ARRIVE_AT_STOP, this.handleShuttleWillArriveAtStop); } + stopListeningForUpdates(): void { + this.shuttleRepository.removeListener(ShuttleRepositoryEvent.SHUTTLE_UPDATED, this.handleShuttleUpdate); + this.shuttleRepository.removeListener(ShuttleRepositoryEvent.SHUTTLE_WILL_ARRIVE_AT_STOP, this.handleShuttleWillArriveAtStop); + } + private async getAverageTravelTimeSecondsWithFallbacks( identifier: ShuttleTravelTimeDataIdentifier, dateFilters: ShuttleTravelTimeDateFilterArguments[] @@ -72,20 +77,43 @@ export class InMemorySelfUpdatingETARepository extends BaseInMemoryETARepository if (!lastStop) return; const lastOrderedStop = await this.shuttleRepository.getOrderedStopByRouteAndStopId(shuttle.routeId, lastStop.stopId); - const nextStop = lastOrderedStop?.nextStop; + + await this.updateCascadingEta({ + shuttle, + currentStop: lastOrderedStop, + originalStopArrival: lastStop, + }); + } + + private async updateCascadingEta({ + shuttle, + currentStop, + originalStopArrival, + runningTravelTimeSeconds = 0 + }: { + shuttle: IShuttle; + currentStop: IOrderedStop | null; + originalStopArrival: ShuttleStopArrival; + runningTravelTimeSeconds?: number; + }) { + if (!currentStop) return; + const nextStop = currentStop?.nextStop; if (!nextStop) return; + // In case the system we have loops around + if (nextStop.stopId === originalStopArrival.stopId) return; let referenceCurrentTime = new Date(); if (this.referenceTime != null) { referenceCurrentTime = this.referenceTime; } + const oneWeekAgo = new Date(referenceCurrentTime.getTime() - (60 * 60 * 24 * 7 * 1000)); const oneDayAgo = new Date(referenceCurrentTime.getTime() - (60 * 60 * 24 * 1000)); const oneHourAgo = new Date(referenceCurrentTime.getTime() - (60 * 60 * 1000)); const travelTimeSeconds = await this.getAverageTravelTimeSecondsWithFallbacks({ routeId: shuttle.routeId, - fromStopId: lastStop.stopId, + fromStopId: currentStop.stopId, toStopId: nextStop.stopId, }, [ { @@ -104,8 +132,8 @@ export class InMemorySelfUpdatingETARepository extends BaseInMemoryETARepository if (travelTimeSeconds == undefined) return; - const elapsedTimeMs = referenceCurrentTime.getTime() - lastStop.timestamp.getTime(); - const predictedTimeSeconds = travelTimeSeconds - (elapsedTimeMs / 1000); + const elapsedTimeMs = referenceCurrentTime.getTime() - originalStopArrival.timestamp.getTime(); + const predictedTimeSeconds = travelTimeSeconds - (elapsedTimeMs / 1000) + runningTravelTimeSeconds; await this.addOrUpdateEta({ secondsRemaining: predictedTimeSeconds, @@ -114,6 +142,16 @@ export class InMemorySelfUpdatingETARepository extends BaseInMemoryETARepository systemId: nextStop.systemId, updatedTime: new Date(), }); + + const nextStopWithNextNextStop = await this.shuttleRepository.getOrderedStopByRouteAndStopId(shuttle.routeId, nextStop.stopId); + await this.updateCascadingEta( + { + shuttle, + currentStop: nextStopWithNextNextStop, + originalStopArrival, + runningTravelTimeSeconds: runningTravelTimeSeconds + travelTimeSeconds, + }, + ) } private async handleShuttleWillArriveAtStop(shuttleArrival: ShuttleStopArrival): Promise {