UNPKG

@golemio/pid

Version:
234 lines 15.4 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; var V4TransferBoardsController_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.V4TransferBoardsController = void 0; const GtfsStopParser_1 = require("../../../../helpers/GtfsStopParser"); const RouteTypeEnums_1 = require("../../../../helpers/RouteTypeEnums"); const DepartureBoardMapper_1 = __importDefault(require("../../helpers/DepartureBoardMapper")); const TransferBoardFilter_1 = require("../../helpers/TransferBoardFilter"); const TransferBoardIconsResolver_1 = require("../../helpers/TransferBoardIconsResolver"); const TransferBoardSorter_1 = require("../../helpers/TransferBoardSorter"); const OgPidToken_1 = require("../../ioc/OgPidToken"); const InfotextFacade_1 = require("../../service/facade/InfotextFacade"); const StopFacade_1 = require("../../service/facade/StopFacade"); const TransferFacade_1 = require("../../service/facade/TransferFacade"); const TransferDepartureCacheTransformation_1 = require("../../service/transformations/TransferDepartureCacheTransformation"); const shared_1 = require("../../../shared"); const CoreToken_1 = require("@golemio/core/dist/helpers/ioc/CoreToken"); const trace_provider_1 = require("@golemio/core/dist/monitoring/opentelemetry/trace-provider"); const express_validator_1 = require("@golemio/core/dist/shared/express-validator"); const moment_timezone_1 = __importDefault(require("@golemio/core/dist/shared/moment-timezone")); const tsyringe_1 = require("@golemio/core/dist/shared/tsyringe"); const METRO_TRAIN_ACCESSIBILITY = true; // metro trains are always considered wheelchair accessible vehicle const DEFAULT_VEHICLE_ACCESSIBILITY = null; // unknown, possibly due to not knowing the vehicle running the trip let V4TransferBoardsController = V4TransferBoardsController_1 = class V4TransferBoardsController { constructor(config, stopFacade, infotextFacade, transferFacade, transformation) { this.config = config; this.stopFacade = stopFacade; this.infotextFacade = infotextFacade; this.transferFacade = transferFacade; this.transformation = transformation; this.defaultTransferBoardsLimit = 8; this.getTransferBoardData = async (req, res, next) => { const queryData = (0, express_validator_1.matchedData)(req); const span = (0, trace_provider_1.createChildSpan)("V4TransferBoardsController.getTransferBoardData"); try { const params = this.parseTransferBoardsParams(queryData); span?.setAttributes(Object.fromEntries(Object.entries(params).filter(([_, value]) => value !== null))); const now = new Date(); let timeFrom = null; if (params.timeFrom !== null) { timeFrom = moment_timezone_1.default.tz(params.timeFrom, shared_1.RopidRouterUtils.TIMEZONE).toDate(); } //#region GTFS stops let stopIds = []; let transferCacheDto = null; let infotextsToInclude = []; let stopsInfo = []; let transferStop; let transferStopName; let sameNameStopIds; try { if ("aswId" in params && typeof params.aswId === "string") { stopIds = await this.stopFacade.getStopIdsForTransferBoardsByAswId(params.aswId); //#region Transfer departures and infotexts if (stopIds.length === 0) { return res.status(404).json(V4TransferBoardsController_1.NO_TRANSFERS); } stopsInfo = await this.stopFacade.getDetailDataFromCacheByStopId(stopIds); const normalizedStopId = GtfsStopParser_1.GtfsStopParser.normalizedStopId(params.aswId); transferStop = stopsInfo.find((stop) => GtfsStopParser_1.GtfsStopParser.normalizedStopId(stop.stop_id) === normalizedStopId); if (!transferStop) { return res.status(404).json(V4TransferBoardsController_1.NO_TRANSFERS); } transferStopName = transferStop.stop_name; // filter out stops with different stop_name sameNameStopIds = stopsInfo.filter((stop) => stop.stop_name === transferStopName).map((stop) => stop.stop_id); const transferCache = await this.transferFacade.getTransferCache(transferStop.stop_id, stopIds, params.limit, parseInt(params.vehicleRegistrationNumber ?? "0", 10), params.routeType, timeFrom || now, sameNameStopIds); transferCacheDto = transferCache; const infotexts = await this.infotextFacade.getInfotextsCache(stopIds, now, transferCache.plannedTimeFrom); infotextsToInclude = infotexts; //#endregion } else if ("cisId" in params && typeof params.cisId === "number") { // add support for trains // load stopIds from stopFacade using translation of params.cisId return res.status(501).json({ message: "EP is not ready for cisId+tripNumber yet" }); } else { return res.status(400).json({ message: "Either an aswId (and no cisId), or a cisId (and no aswId) must be provided", }); } } catch (error) { span?.recordException(error); throw error; } //#endregion if (!transferCacheDto.transfers?.length) { // No transfers, but there might be some infotexts. At least the transfer stop is valid return res.status(200).json({ stop_name: transferStop.stop_name, platform_code: transferStop.platform_code, icons: [], departures: [], infotexts: infotextsToInclude, }); } // add predictedDeparture date/timestamp to transfers // compute accessibility for available transfers this.enhanceInPlaceWithTimeAndInfo(transferCacheDto, stopsInfo); const guaranteedTransferTripIds = new Set(TransferBoardFilter_1.TransferBoardFilter.havingConnectionFromTripId(transferCacheDto.currentTripId, transferCacheDto.transfers, transferCacheDto.delayedTimeFrom).flatMap((t) => t.departure.trip_id)); transferCacheDto.transfers = TransferBoardFilter_1.TransferBoardFilter.minimalTransferTime(transferCacheDto.transfers, transferCacheDto.delayedTimeFrom, guaranteedTransferTripIds); transferCacheDto.transfers = TransferBoardFilter_1.TransferBoardFilter.sameStopNameTransfer(transferCacheDto.transfers, transferStop.stop_id, sameNameStopIds, guaranteedTransferTripIds, false); const tripIdsToFilter = new Set(TransferBoardFilter_1.TransferBoardFilter.filterOutKeepAlwaysTransfer(transferCacheDto.transfers).map((transfer) => transfer.departure.trip_id)); for (const guaranteedTripId of guaranteedTransferTripIds) { // Guaranteed transfers are "show always" — no need to filter their stop-lines tripIdsToFilter.delete(guaranteedTripId); } this.enhanceInPlaceWithGuaranteedTransfers(transferCacheDto, guaranteedTransferTripIds); TransferBoardSorter_1.TransferBoardSorter.sortDepartures(transferCacheDto.transfers); const tripIdsToKeep = new Set(await this.transferFacade.findRelevantTripIdsFromLines(tripIdsToFilter, transferCacheDto.currentStop, transferCacheDto.currentTripId, this.countPreviousStopsToAllow, this.isOppositeDirectionFilterStopNameStrict)); guaranteedTransferTripIds.forEach((guaranteedTripId) => { tripIdsToKeep.add(guaranteedTripId); }); transferCacheDto.transfers = TransferBoardFilter_1.TransferBoardFilter.keepAlwaysLinesAndTripIds(transferCacheDto.transfers, tripIdsToKeep); const transformedData = this.transformation.transformArray(transferCacheDto.transfers); const filteredData = TransferBoardFilter_1.TransferBoardFilter.filterDepartures(transformedData, params.limit, transferCacheDto.delayedTimeFrom.getTime(), now.getTime()); const output = { // possibly share arrival_datetime of the current vehicle? stop_name: transferStop.stop_name, platform_code: transferStop.platform_code, icons: TransferBoardIconsResolver_1.TransferBoardIconsResolver.headsignToIconsList(transferCacheDto.currentStop?.stop_icons, params.routeType, null), departures: filteredData, infotexts: infotextsToInclude, }; return res.status(200).json(output); } catch (err) { next(err); } finally { span?.end(); } }; this.countPreviousStopsToAllow = this.config.getValue("module.pid.vehicle-positions.transferboards.countPreviousStopsToAllow", 0); this.isOppositeDirectionFilterStopNameStrict = this.config.getBoolean("module.pid.vehicle-positions.transferboards.isOppositeDirectionFilterStopNameStrict", true); } accessibilityForStops(stopsInfo) { const accessibilityStopIdsMap = new Map(); for (const stopInfo of stopsInfo) { accessibilityStopIdsMap.set(stopInfo.stop_id, stopInfo.wheelchair_boarding); } return accessibilityStopIdsMap; } enhanceInPlaceWithTimeAndInfo(transferCacheDto, stopsInfo) { const accessibilityStopIdsMap = this.accessibilityForStops(stopsInfo); transferCacheDto.transfers.forEach((t) => { const accessibility = { route_type: t.departure.route_type, wheelchair_accessible: t.departure.wheelchair_accessible ?? null, real_wheelchair_accessible: null, wheelchair_boarding: accessibilityStopIdsMap.get(t.departure.stop_id) ?? null, }; // metro lines are not dependent on position of specific vehicle // regarding their accessibility, thus they only rely on station information if (t.departure.route_type === RouteTypeEnums_1.GTFSRouteTypeEnum.METRO) { accessibility.real_wheelchair_accessible = METRO_TRAIN_ACCESSIBILITY; } else { accessibility.real_wheelchair_accessible = t.position?.detailed_info?.is_wheelchair_accessible ?? DEFAULT_VEHICLE_ACCESSIBILITY; } t.departure.is_wheelchair_accessible = DepartureBoardMapper_1.default.resolveAccessibility(accessibility); t.departure.predictedDepartureDate = shared_1.DepartureCalculator.getPredictedDepartureTime(new Date(t.departure.departure_datetime), t.departure.arrival_datetime ? new Date(t.departure.arrival_datetime) : null, t.position?.delay ?? null); t.departure.predictedDepartureTimestamp = t.departure.predictedDepartureDate.getTime(); }); } enhanceInPlaceWithGuaranteedTransfers(transferCacheDto, tripIds) { // guaranteed transfers will show minutes as if they are departing at the time of current trip arrival const predictedArrivalDate = new Date(transferCacheDto.delayedTimeFrom); const predictedArrivalTimestamp = predictedArrivalDate.getTime(); transferCacheDto.transfers.forEach((t) => { t.departure.is_guaranteed_transfer = tripIds.has(t.departure.trip_id); if (t.departure.is_guaranteed_transfer) { // Move predicted departure date (in past) to current-trip's (later), so we show minutes as // if the vehicle departs at our time of arrival = people should exit the current trip's vehicle // Example: We arrive at 18:05, transfer departs at 18:03 (but waits up to 5 minutes) // ... will set predicted departure happening at 18:05 = same as our arrival if (predictedArrivalTimestamp > t.departure.predictedDepartureTimestamp) { t.departure.predictedDepartureDate = predictedArrivalDate; t.departure.predictedDepartureTimestamp = predictedArrivalTimestamp; } } }); } parseTransferBoardsParams(query) { return { aswId: (query.aswId ?? null), cisId: (query.cisId ?? null), tripNumber: (query.tripNumber ?? null), vehicleRegistrationNumber: (query.vehicleRegistrationNumber ?? null), routeType: query.routeType, timeFrom: (query.timeFrom ?? null), limit: (query.limit ?? this.defaultTransferBoardsLimit), }; } }; exports.V4TransferBoardsController = V4TransferBoardsController; V4TransferBoardsController.NO_TRANSFERS = { stop_name: null, platform_code: null, icons: [], departures: [], infotexts: [], }; exports.V4TransferBoardsController = V4TransferBoardsController = V4TransferBoardsController_1 = __decorate([ (0, tsyringe_1.injectable)(), __param(0, (0, tsyringe_1.inject)(CoreToken_1.CoreToken.SimpleConfig)), __param(1, (0, tsyringe_1.inject)(OgPidToken_1.OgPidToken.StopFacade)), __param(2, (0, tsyringe_1.inject)(OgPidToken_1.OgPidToken.InfotextFacade)), __param(3, (0, tsyringe_1.inject)(OgPidToken_1.OgPidToken.TransferFacade)), __param(4, (0, tsyringe_1.inject)(OgPidToken_1.OgPidToken.TransferDepartureCacheTransformation)), __metadata("design:paramtypes", [Object, StopFacade_1.StopFacade, InfotextFacade_1.InfotextFacade, TransferFacade_1.TransferFacade, TransferDepartureCacheTransformation_1.TransferDepartureCacheTransformation]) ], V4TransferBoardsController); //# sourceMappingURL=V4TransferBoardsController.js.map