@golemio/pid 2.14.2-dev.1360232209 → 2.14.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/integration-engine/vehicle-positions/ioc/Di.js +9 -14
  2. package/dist/integration-engine/vehicle-positions/ioc/Di.js.map +1 -1
  3. package/dist/integration-engine/vehicle-positions/ioc/VPContainerToken.d.ts +0 -3
  4. package/dist/integration-engine/vehicle-positions/ioc/VPContainerToken.js +0 -3
  5. package/dist/integration-engine/vehicle-positions/ioc/VPContainerToken.js.map +1 -1
  6. package/dist/integration-engine/vehicle-positions/workers/gtfs-rt/tasks/GenerateFilesTask.js +2 -3
  7. package/dist/integration-engine/vehicle-positions/workers/gtfs-rt/tasks/GenerateFilesTask.js.map +1 -1
  8. package/dist/integration-engine/vehicle-positions/workers/runs/helpers/regional-bus/RegionalBusMessageFilter.d.ts +4 -1
  9. package/dist/integration-engine/vehicle-positions/workers/runs/helpers/regional-bus/RegionalBusMessageFilter.js +27 -11
  10. package/dist/integration-engine/vehicle-positions/workers/runs/helpers/regional-bus/RegionalBusMessageFilter.js.map +1 -1
  11. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/data-access/PositionsRepository.js +3 -11
  12. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/data-access/PositionsRepository.js.map +1 -1
  13. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/helpers/PositionsManager.d.ts +13 -16
  14. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/helpers/PositionsManager.js +399 -427
  15. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/helpers/PositionsManager.js.map +1 -1
  16. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/helpers/ValidToCalculator.d.ts +6 -15
  17. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/helpers/ValidToCalculator.js +45 -71
  18. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/helpers/ValidToCalculator.js.map +1 -1
  19. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/helpers/regional-bus/RegionalBusPositionsManager.d.ts +10 -13
  20. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/helpers/regional-bus/RegionalBusPositionsManager.js +347 -366
  21. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/helpers/regional-bus/RegionalBusPositionsManager.js.map +1 -1
  22. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/interfaces/VPInterfaces.d.ts +2 -2
  23. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/tasks/ProcessRegionalBusPositionsTask.d.ts +0 -1
  24. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/tasks/ProcessRegionalBusPositionsTask.js +5 -8
  25. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/tasks/ProcessRegionalBusPositionsTask.js.map +1 -1
  26. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/tasks/UpdateDelayTask.d.ts +0 -1
  27. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/tasks/UpdateDelayTask.js +6 -5
  28. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/tasks/UpdateDelayTask.js.map +1 -1
  29. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/transformations/MpvMessageTransformation.d.ts +6 -3
  30. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/transformations/MpvMessageTransformation.js +20 -11
  31. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/transformations/MpvMessageTransformation.js.map +1 -1
  32. package/dist/output-gateway/public/routers/v1/PublicDeparturesRouter.js +1 -1
  33. package/dist/output-gateway/public/routers/v1/helpers/CustomStopIdGroupValidator.d.ts +0 -1
  34. package/dist/output-gateway/public/routers/v1/helpers/CustomStopIdGroupValidator.js +16 -38
  35. package/dist/output-gateway/public/routers/v1/helpers/CustomStopIdGroupValidator.js.map +1 -1
  36. package/docs/openapi-output.yaml +2 -4
  37. package/package.json +3 -3
  38. package/db/migrations/postgresql/20240612135256-fix-delay-in-view-future-stop-times.js +0 -53
  39. package/db/migrations/postgresql/20240701093122-fix-delay-in-view-v-public-vehiclepositions-past-stop-times.js +0 -53
  40. package/db/migrations/postgresql/sqls/20240612135256-fix-delay-in-view-future-stop-times-down.sql +0 -82
  41. package/db/migrations/postgresql/sqls/20240612135256-fix-delay-in-view-future-stop-times-up.sql +0 -93
  42. package/db/migrations/postgresql/sqls/20240701093122-fix-delay-in-view-v-public-vehiclepositions-past-stop-times-down.sql +0 -31
  43. package/db/migrations/postgresql/sqls/20240701093122-fix-delay-in-view-v-public-vehiclepositions-past-stop-times-up.sql +0 -38
@@ -15,12 +15,6 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
15
15
  }) : function(o, v) {
16
16
  o["default"] = v;
17
17
  });
18
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
- 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;
22
- return c > 3 && r && Object.defineProperty(target, key, r), r;
23
- };
24
18
  var __importStar = (this && this.__importStar) || function (mod) {
25
19
  if (mod && mod.__esModule) return mod;
26
20
  var result = {};
@@ -28,12 +22,6 @@ var __importStar = (this && this.__importStar) || function (mod) {
28
22
  __setModuleDefault(result, mod);
29
23
  return result;
30
24
  };
31
- var __metadata = (this && this.__metadata) || function (k, v) {
32
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
33
- };
34
- var __param = (this && this.__param) || function (paramIndex, decorator) {
35
- return function (target, key) { decorator(target, key, paramIndex); }
36
- };
37
25
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
38
26
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
39
27
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -46,422 +34,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
46
34
  var __importDefault = (this && this.__importDefault) || function (mod) {
47
35
  return (mod && mod.__esModule) ? mod : { "default": mod };
48
36
  };
37
+ var _a;
49
38
  Object.defineProperty(exports, "__esModule", { value: true });
50
- exports.PositionsManager = void 0;
51
- const VPContainerToken_1 = require("../../../ioc/VPContainerToken");
52
- const moment_timezone_1 = __importDefault(require("@golemio/core/dist/shared/moment-timezone"));
53
- const tsyringe_1 = require("@golemio/core/dist/shared/tsyringe");
54
39
  const turf = __importStar(require("@turf/turf"));
40
+ const moment_timezone_1 = __importDefault(require("@golemio/core/dist/shared/moment-timezone"));
55
41
  const const_1 = require("../../../../../const");
56
42
  const RouteTypeEnums_1 = require("../../../../../helpers/RouteTypeEnums");
57
43
  const DPPUtils_1 = __importDefault(require("./DPPUtils"));
58
44
  const DateTimeUtils_1 = require("./DateTimeUtils");
59
45
  const PositionHandlerEnum_1 = require("./PositionHandlerEnum");
60
- const ValidToCalculator_1 = require("./ValidToCalculator");
46
+ const ValidToCalculator_1 = __importDefault(require("./ValidToCalculator"));
61
47
  const AnchorPointSegmenter_1 = require("./anchor-points/AnchorPointSegmenter");
62
48
  const ComputeDelayHelper_1 = __importDefault(require("./compute-positions/ComputeDelayHelper"));
63
49
  const ONE_DAY_IN_SECONDS = 24 * 60 * 60;
64
50
  const ONE_SECOND_IN_MILLIS = 1000;
65
- let PositionsManager = exports.PositionsManager = class PositionsManager {
66
- constructor(validToCalculator) {
67
- this.validToCalculator = validToCalculator;
68
- /**
69
- * Compute positions and return computed positions
70
- *
71
- * @param {ITripPositionsWithGTFS} tripPositions - Trip positions with shape anchors data
72
- * @returns {Promise<IProcessedPositions>} - Returns computed/updated positions
73
- */
74
- this.computePositions = (tripPositions, schedule) => __awaiter(this, void 0, void 0, function* () {
75
- const startTimestamp = tripPositions.start_timestamp.getTime();
76
- const startDayTimestamp = this.getStartDayTimestamp(startTimestamp, tripPositions.gtfsData.shapes_anchor_points[0].time_scheduled_seconds);
77
- const gtfsRouteType = tripPositions.gtfs_route_type;
78
- const context = this.getCurrentContext(tripPositions);
79
- const computedPositions = [];
80
- const endTimestamp = tripPositions.end_timestamp;
81
- return this.updatePositions({ tripPositions, startTimestamp, endTimestamp, startDayTimestamp, context, computedPositions, gtfsRouteType }, schedule);
82
- });
83
- /**
84
- * Takes position one by one, set proper handler for type of position, and do the process of position
85
- *
86
- * @param {number} i - Iteration
87
- * @param {IUpdatePositionsIteratorOptions} options - Initial options
88
- * @param {number} cb - Callback function of iterator
89
- * @returns {void} - void
90
- */
91
- this.updatePositions = (options, schedule) => __awaiter(this, void 0, void 0, function* () {
92
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
93
- const { tripPositions, startDayTimestamp, startTimestamp, endTimestamp, context, computedPositions, gtfsRouteType } = options;
94
- for (let i = 0; i < tripPositions.positions.length; i++) {
95
- const position = tripPositions.positions[i];
96
- let positionToUpdate = null;
97
- // situations
98
- switch (this.setPositionUpdateHandler(position)) {
99
- case PositionHandlerEnum_1.PositionHandlerEnum.TRACKING:
100
- const currentPosition = turf.point([position.lng, position.lat], {
101
- id: position.id,
102
- origin_time: position.origin_time,
103
- origin_timestamp: position.origin_timestamp,
104
- scheduled_timestamp: position.scheduled_timestamp,
105
- this_stop_id: position.this_stop_id,
106
- tcp_event: position.tcp_event,
107
- });
108
- positionToUpdate = this.getEstimatedPoint(tripPositions.gtfsData, currentPosition, context, startDayTimestamp, tripPositions.gtfs_route_type);
109
- if (positionToUpdate.state_position !== const_1.StatePositionEnum.MISMATCHED) {
110
- positionToUpdate = ComputeDelayHelper_1.default.updatePositionToUpdate(context, positionToUpdate, position, gtfsRouteType);
111
- }
112
- positionToUpdate.bearing = (_a = position.bearing) !== null && _a !== void 0 ? _a : positionToUpdate.bearing;
113
- break;
114
- case PositionHandlerEnum_1.PositionHandlerEnum.NOT_TRACKING:
115
- // if there is no previous positions with tracking status, set position as before_track
116
- const firstShapesAnchorPoint = tripPositions.gtfsData.shapes_anchor_points[0];
117
- if (context.lastPositionTracking === null &&
118
- (endTimestamp === null ||
119
- position.scheduled_timestamp === null ||
120
- endTimestamp > position.scheduled_timestamp)) {
121
- const firstStopTime = tripPositions.gtfsData.stop_times[0];
122
- // if there is propagated delay we can use it for new position
123
- const lastPositionDelayed = context.lastPositionBeforeTrackDelayed;
124
- // if there is no delay to duplicate and DPP trip is far from start then invisible
125
- const setAsInvisible = !lastPositionDelayed &&
126
- DPPUtils_1.default.isInvisible(tripPositions.agency_name_scheduled, position.origin_timestamp, startTimestamp, [position.lng, position.lat], firstShapesAnchorPoint.coordinates, schedule, tripPositions.gtfs_trip_id, context.lastPositionTracking, position.is_tracked);
127
- positionToUpdate = Object.assign({ id: position.id, next_stop_arrival_time: new Date(startDayTimestamp + firstStopTime.arrival_time_seconds * 1000), next_stop_departure_time: new Date(startDayTimestamp + firstStopTime.departure_time_seconds * 1000), next_stop_id: firstStopTime.stop_id, next_stop_sequence: firstStopTime.stop_sequence, next_stop_name: firstStopTime.stop.stop_name, shape_dist_traveled: firstStopTime.shape_dist_traveled, state_position: setAsInvisible ? const_1.StatePositionEnum.INVISIBLE : const_1.StatePositionEnum.BEFORE_TRACK, state_process: const_1.StateProcessEnum.PROCESSED }, (firstStopTime.stop_headsign && {
128
- last_stop_headsign: firstStopTime.stop_headsign,
129
- }));
130
- if (positionToUpdate.state_position === const_1.StatePositionEnum.BEFORE_TRACK) {
131
- positionToUpdate.delay = this.getDelayBeforeTrack((_b = lastPositionDelayed === null || lastPositionDelayed === void 0 ? void 0 : lastPositionDelayed.delay) !== null && _b !== void 0 ? _b : position.delay, firstStopTime.departure_time_seconds, position.origin_timestamp.getTime(), startDayTimestamp);
132
- }
133
- }
134
- else {
135
- // if there is tracking 2 position with same origin_timestamp then this position is duplicate
136
- const statePosition = position.origin_timestamp.getTime() === context.lastPositionOriginTimestamp ||
137
- tripPositions.positions.findIndex((positionItem) => positionItem.origin_timestamp.getTime() === position.origin_timestamp.getTime() &&
138
- positionItem.is_tracked) >= 0
139
- ? const_1.StatePositionEnum.DUPLICATE
140
- : const_1.StatePositionEnum.AFTER_TRACK;
141
- const lastShapesAnchorPoint = tripPositions.gtfsData.shapes_anchor_points[tripPositions.gtfsData.shapes_anchor_points.length - 1];
142
- const lastStopTime = tripPositions.gtfsData.stop_times[tripPositions.gtfsData.stop_times.length - 1];
143
- // set as invisible if there are some AFTER_TRACK before
144
- const setAsInvisible = DPPUtils_1.default.isInvisible(tripPositions.agency_name_scheduled, position.origin_timestamp, startTimestamp, [position.lng, position.lat], firstShapesAnchorPoint.coordinates, schedule, tripPositions.gtfs_trip_id, context.lastPositionTracking, position.is_tracked) && statePosition === const_1.StatePositionEnum.AFTER_TRACK;
145
- positionToUpdate = Object.assign({ id: position.id, last_stop_arrival_time: new Date(startDayTimestamp + lastShapesAnchorPoint.time_scheduled_seconds * 1000), last_stop_departure_time: new Date(startDayTimestamp + lastShapesAnchorPoint.time_scheduled_seconds * 1000), last_stop_id: lastStopTime.stop_id, last_stop_sequence: lastShapesAnchorPoint.last_stop_sequence, shape_dist_traveled: lastShapesAnchorPoint.shape_dist_traveled, state_position: setAsInvisible ? const_1.StatePositionEnum.INVISIBLE : statePosition, state_process: const_1.StateProcessEnum.PROCESSED, delay: (_c = position.delay) !== null && _c !== void 0 ? _c : null }, (lastStopTime.stop_headsign && {
146
- last_stop_headsign: lastStopTime.stop_headsign,
147
- }));
148
- positionToUpdate = ComputeDelayHelper_1.default.updatePositionToUpdate(context, positionToUpdate, position, gtfsRouteType);
149
- }
150
- break;
151
- case PositionHandlerEnum_1.PositionHandlerEnum.CANCELED:
152
- positionToUpdate = {
153
- id: position.id,
154
- lat: (_f = (_d = position.lat) !== null && _d !== void 0 ? _d : (_e = context.lastPositionTracking) === null || _e === void 0 ? void 0 : _e.geometry.coordinates[1]) !== null && _f !== void 0 ? _f : null,
155
- lng: (_j = (_g = position.lng) !== null && _g !== void 0 ? _g : (_h = context.lastPositionTracking) === null || _h === void 0 ? void 0 : _h.geometry.coordinates[0]) !== null && _j !== void 0 ? _j : null,
156
- state_position: const_1.StatePositionEnum.CANCELED,
157
- state_process: const_1.StateProcessEnum.PROCESSED,
158
- };
159
- if (positionToUpdate.lat === null || positionToUpdate.lng === null) {
160
- positionToUpdate = null;
161
- }
162
- break;
163
- case PositionHandlerEnum_1.PositionHandlerEnum.DO_NOTHING:
164
- break;
165
- default:
166
- break;
167
- }
168
- // if not null push to update
169
- if (positionToUpdate) {
170
- (_k = positionToUpdate.is_tracked) !== null && _k !== void 0 ? _k : (positionToUpdate.is_tracked = position.is_tracked);
171
- positionToUpdate.valid_to = this.validToCalculator.getValidToAttribute(positionToUpdate, position, gtfsRouteType, tripPositions.gtfsData, startTimestamp);
172
- computedPositions.push(positionToUpdate);
173
- }
174
- else {
175
- computedPositions.push(null);
176
- }
177
- context.lastPositionState = (_l = positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) !== null && _l !== void 0 ? _l : position.state_position;
178
- // set last known BEFORE_TRACK_DELAYED position
179
- if (position.state_position === const_1.StatePositionEnum.BEFORE_TRACK_DELAYED) {
180
- context.lastPositionBeforeTrackDelayed = {
181
- delay: (_m = positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.delay) !== null && _m !== void 0 ? _m : position.delay,
182
- origin_timestamp: (_o = positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.origin_timestamp) !== null && _o !== void 0 ? _o : position.origin_timestamp,
183
- };
184
- }
185
- // set last position tracking (only for at_stop and on_track)
186
- if ((positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) === const_1.StatePositionEnum.AT_STOP ||
187
- (positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) === const_1.StatePositionEnum.ON_TRACK ||
188
- position.state_position === const_1.StatePositionEnum.AT_STOP ||
189
- position.state_position === const_1.StatePositionEnum.ON_TRACK) {
190
- context.lastPositionTracking = turf.point([position.lng, position.lat], Object.assign(Object.assign({}, position), positionToUpdate));
191
- }
192
- // set new first position at stop streak if this stop seqence is set and it is not same as before
193
- if (positionToUpdate
194
- ? positionToUpdate.this_stop_sequence &&
195
- context.atStopStreak.stop_sequence !== positionToUpdate.this_stop_sequence
196
- : position.this_stop_sequence && context.atStopStreak.stop_sequence !== position.this_stop_sequence) {
197
- context.atStopStreak.stop_sequence = positionToUpdate
198
- ? positionToUpdate.this_stop_sequence
199
- : position.this_stop_sequence;
200
- context.atStopStreak.firstPositionTimestamp = position.origin_timestamp.getTime();
201
- context.atStopStreak.firstPositionDelay = positionToUpdate ? positionToUpdate.delay : position.delay;
202
- }
203
- // IF currently valid updated position / position was processed before
204
- // and it is NOT AT_STOP
205
- // then disrupt atStopStreak
206
- if (positionToUpdate &&
207
- positionToUpdate.state_position !== const_1.StatePositionEnum.MISMATCHED &&
208
- !positionToUpdate.this_stop_sequence) {
209
- context.atStopStreak.stop_sequence = null;
210
- }
211
- else if (!positionToUpdate && !position.this_stop_sequence) {
212
- context.atStopStreak.stop_sequence = null;
213
- }
214
- // duplicated or mismatched position should not be considered at all
215
- if ((positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) !== const_1.StatePositionEnum.DUPLICATE &&
216
- (positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) !== const_1.StatePositionEnum.INVISIBLE &&
217
- (positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) !== const_1.StatePositionEnum.MISMATCHED) {
218
- context.lastPositionId = position.id;
219
- context.lastPositionCanceled = position.is_canceled;
220
- context.lastPositionOriginTimestamp = position.origin_timestamp.getTime();
221
- ComputeDelayHelper_1.default.updateContext(context, positionToUpdate, position, gtfsRouteType);
222
- }
223
- }
224
- return {
225
- context,
226
- positions: computedPositions,
227
- };
228
- });
229
- this.getCurrentContext = (tripPositions) => {
230
- var _a;
231
- const context = (_a = tripPositions.last_position_context) !== null && _a !== void 0 ? _a : {
232
- atStopStreak: {
233
- stop_sequence: null,
234
- firstPositionTimestamp: null,
235
- firstPositionDelay: null,
236
- },
237
- lastPositionLastStop: {
238
- id: null,
239
- sequence: null,
240
- arrival_time: null,
241
- arrival_delay: null,
242
- departure_time: null,
243
- departure_delay: null,
244
- },
245
- lastPositionDelay: null,
246
- lastPositionId: null,
247
- lastPositionOriginTimestamp: null,
248
- lastPositionTracking: null,
249
- lastPositionCanceled: null,
250
- lastPositionBeforeTrackDelayed: null,
251
- lastPositionState: null,
252
- tripId: tripPositions.id,
253
- };
254
- return context;
255
- };
256
- /**
257
- * Decide how to process input position data
258
- *
259
- * @param {IVPTripsPositionAttributes} position - Input vehiclepositions_positions row data
260
- * @returns {PositionHandlerEnum} - Returns action handler enum
261
- */
262
- this.setPositionUpdateHandler = (position) => {
263
- if (position.state_process === const_1.StateProcessEnum.PROCESSED)
264
- return PositionHandlerEnum_1.PositionHandlerEnum.DO_NOTHING;
265
- else if (position.is_canceled)
266
- return PositionHandlerEnum_1.PositionHandlerEnum.CANCELED;
267
- else if (position.is_tracked)
268
- return PositionHandlerEnum_1.PositionHandlerEnum.TRACKING;
269
- else
270
- return PositionHandlerEnum_1.PositionHandlerEnum.NOT_TRACKING;
271
- };
272
- /**
273
- * Returns estimate of point on shape, where the trip should be with appropriate delay
274
- *
275
- * @param {IShapeAnchorPoint[]} tripShapePoints - Precalculated trip shape equidistant points with scheduled times
276
- * @param {Feature<Point, ICurrentPositionProperties>} currentPosition - Current position of trip
277
- * @param {IVPTripsLastPositionContext | null} context - Context state, holds information about previous positions
278
- * @param {number} startDayTimestamp - Unix timestamp of midnight before trip starts
279
- * @returns {IPositionToUpdate} - Position object to update
280
- */
281
- this.getEstimatedPoint = (tripGtfsData, currentPosition, context, startDayTimestamp, gtfsRouteType) => {
282
- var _a, _b, _c, _d, _e, _f, _g, _h;
283
- const anchorPointSegmenter = new AnchorPointSegmenter_1.AnchorPointSegmenter(tripGtfsData.shapes_anchor_points, currentPosition);
284
- const defaultStatePosition = const_1.StatePositionEnum.OFF_TRACK;
285
- // Initial value
286
- let estimatedPoint = Object.assign({ id: currentPosition.properties.id, state_position: defaultStatePosition, state_process: const_1.StateProcessEnum.PROCESSED, tcp_event: currentPosition.properties.tcp_event, valid_to: this.validToCalculator.getDefaultValidToAttribute(currentPosition.properties.origin_timestamp) }, (context &&
287
- ((_a = context.lastPositionTracking) === null || _a === void 0 ? void 0 : _a.properties.last_stop_sequence) && {
288
- shape_dist_traveled: (_b = context.lastPositionTracking) === null || _b === void 0 ? void 0 : _b.properties.shape_dist_traveled,
289
- last_stop_arrival_time: ((_c = context.lastPositionTracking) === null || _c === void 0 ? void 0 : _c.properties.last_stop_arrival_time)
290
- ? new Date((_d = context.lastPositionTracking) === null || _d === void 0 ? void 0 : _d.properties.last_stop_arrival_time)
291
- : undefined,
292
- last_stop_departure_time: ((_e = context.lastPositionTracking) === null || _e === void 0 ? void 0 : _e.properties.last_stop_departure_time)
293
- ? new Date((_f = context.lastPositionTracking) === null || _f === void 0 ? void 0 : _f.properties.last_stop_departure_time)
294
- : undefined,
295
- last_stop_sequence: (_g = context.lastPositionTracking) === null || _g === void 0 ? void 0 : _g.properties.last_stop_sequence,
296
- last_stop_id: (_h = context.lastPositionTracking) === null || _h === void 0 ? void 0 : _h.properties.last_stop_id,
297
- }));
298
- let lastStopSequence = null;
299
- if ((context === null || context === void 0 ? void 0 : context.lastPositionState) === const_1.StatePositionEnum.BEFORE_TRACK) {
300
- lastStopSequence = 1;
301
- }
302
- else if (context === null || context === void 0 ? void 0 : context.lastPositionTracking) {
303
- lastStopSequence = context.lastPositionTracking.properties.last_stop_sequence;
304
- }
305
- const closesPoint = anchorPointSegmenter.getClosesPoint(lastStopSequence);
306
- if (!closesPoint) {
307
- return estimatedPoint;
308
- }
309
- return this.getClosestPoint(currentPosition, context, startDayTimestamp, closesPoint, tripGtfsData, gtfsRouteType);
310
- };
311
- /**
312
- * Picks only one closest point for multiple possible points based on delay of last position and stop times
313
- *
314
- * @param {Feature<Point, ICurrentPositionProperties>} currentPosition - Feature Point of current position
315
- * @param {IVPTripsLastPositionContext | null} context - Context state, holds information about previous positions
316
- * @param {number} startDayTimestamp - Unix timestamp of start of the day
317
- * @param {IShapeAnchorPoint[]} closestPts - All closest points of possible segments
318
- * @param {IComputationTrip} tripGtfsData - GTFS data and all set of known positions
319
- * @returns {IPositionToUpdate} - Result point as position to update in DB
320
- */
321
- this.getClosestPoint = (currentPosition, context, startDayTimestamp, thisClosestPoint, tripGtfsData, gtfsRouteType) => {
322
- // want to find minimum difference of our prediction, where the bus should be
323
- const tripShapePoints = tripGtfsData.shapes_anchor_points;
324
- const tripStopTimes = tripGtfsData.stop_times;
325
- const firstTripShapePoint = tripShapePoints[0];
326
- const lastTripShapePoint = tripShapePoints[tripShapePoints.length - 1];
327
- const timeScheduledTimestamp = DateTimeUtils_1.DateTimeUtils.getStopDateTimeForDayStart(
328
- // take stop arrival time if is point at stop
329
- thisClosestPoint.this_stop_sequence
330
- ? tripStopTimes[thisClosestPoint.this_stop_sequence - 1].arrival_time_seconds
331
- : thisClosestPoint.time_scheduled_seconds, startDayTimestamp);
332
- let timeDelayInSeconds = Math.round((currentPosition.properties.origin_timestamp.getTime() - timeScheduledTimestamp.getTime()) / 1000);
333
- const isBusOrTram = gtfsRouteType === RouteTypeEnums_1.GTFSRouteTypeEnum.BUS ||
334
- gtfsRouteType === RouteTypeEnums_1.GTFSRouteTypeEnum.TROLLEYBUS ||
335
- gtfsRouteType === RouteTypeEnums_1.GTFSRouteTypeEnum.TRAM;
336
- // push trip into nearest at_stop shape anchor point if trip "P" event (TCP tram or bus)
337
- if (isBusOrTram &&
338
- currentPosition.properties.tcp_event === const_1.TCPEventEnum.ARRIVAL_ANNOUNCED &&
339
- thisClosestPoint.this_stop_sequence === null) {
340
- if (thisClosestPoint.last_stop_sequence === 1 && !(context === null || context === void 0 ? void 0 : context.lastPositionTracking)) {
341
- thisClosestPoint = firstTripShapePoint;
342
- }
343
- else {
344
- let movingShapePointIndex = thisClosestPoint.index;
345
- while (thisClosestPoint.this_stop_sequence === null && movingShapePointIndex < lastTripShapePoint.index) {
346
- movingShapePointIndex++;
347
- thisClosestPoint = tripShapePoints[movingShapePointIndex];
348
- }
349
- }
350
- }
351
- // lets correct delay if it is at stop
352
- if (thisClosestPoint.this_stop_sequence) {
353
- const thisStopSequence = thisClosestPoint.this_stop_sequence;
354
- timeDelayInSeconds = this.getCorrectedTimeDelay(timeDelayInSeconds, context, currentPosition, thisStopSequence, {
355
- departureTime: tripStopTimes[thisStopSequence - 1].departure_time_seconds,
356
- arrivalTime: tripStopTimes[thisStopSequence - 1].arrival_time_seconds,
357
- });
358
- // delay can not be negative if the vehicle is at the first stop
359
- if (thisStopSequence === firstTripShapePoint.this_stop_sequence) {
360
- timeDelayInSeconds = Math.max(timeDelayInSeconds, 0);
361
- }
362
- }
363
- let { statePosition, thisStopSequence, lastStopSequence, nextStopSequence } = this.getStateAndStopSequences(gtfsRouteType, tripStopTimes, thisClosestPoint, currentPosition.properties, context);
364
- // test if TCP trip with DEPARTURED event should not be at stop (not applied for last stop)
365
- if (currentPosition.properties.tcp_event === const_1.TCPEventEnum.DEPARTURED &&
366
- thisStopSequence !== lastTripShapePoint.this_stop_sequence) {
367
- statePosition = const_1.StatePositionEnum.ON_TRACK;
368
- }
369
- if (isBusOrTram && this.isAfterTrack(lastStopSequence, lastTripShapePoint)) {
370
- statePosition = const_1.StatePositionEnum.AFTER_TRACK;
371
- thisStopSequence = lastTripShapePoint.this_stop_sequence;
372
- lastStopSequence = lastTripShapePoint.last_stop_sequence;
373
- nextStopSequence = lastTripShapePoint.next_stop_sequence;
374
- timeDelayInSeconds = 0;
375
- }
376
- // save it for result
377
- const estimatedPoint = {
378
- id: currentPosition.properties.id,
379
- bearing: thisClosestPoint.bearing,
380
- shape_dist_traveled: thisClosestPoint.shape_dist_traveled,
381
- next_stop_id: tripStopTimes[nextStopSequence - 1].stop_id,
382
- last_stop_id: tripStopTimes[lastStopSequence - 1].stop_id,
383
- next_stop_name: tripStopTimes[nextStopSequence - 1].stop.stop_name,
384
- last_stop_name: tripStopTimes[lastStopSequence - 1].stop.stop_name,
385
- next_stop_sequence: nextStopSequence,
386
- last_stop_sequence: lastStopSequence,
387
- next_stop_arrival_time: DateTimeUtils_1.DateTimeUtils.getStopDateTimeForDayStart(tripStopTimes[nextStopSequence - 1].arrival_time_seconds, startDayTimestamp),
388
- last_stop_arrival_time: DateTimeUtils_1.DateTimeUtils.getStopDateTimeForDayStart(tripStopTimes[lastStopSequence - 1].arrival_time_seconds, startDayTimestamp),
389
- next_stop_departure_time: DateTimeUtils_1.DateTimeUtils.getStopDateTimeForDayStart(tripStopTimes[nextStopSequence - 1].departure_time_seconds, startDayTimestamp),
390
- last_stop_departure_time: DateTimeUtils_1.DateTimeUtils.getStopDateTimeForDayStart(tripStopTimes[lastStopSequence - 1].departure_time_seconds, startDayTimestamp),
391
- delay: timeDelayInSeconds,
392
- this_stop_id: (statePosition === const_1.StatePositionEnum.AT_STOP &&
393
- thisStopSequence &&
394
- tripStopTimes[thisStopSequence - 1].stop_id) ||
395
- undefined,
396
- this_stop_name: (statePosition === const_1.StatePositionEnum.AT_STOP &&
397
- thisStopSequence &&
398
- tripStopTimes[thisStopSequence - 1].stop.stop_name) ||
399
- undefined,
400
- this_stop_sequence: (statePosition === const_1.StatePositionEnum.AT_STOP && thisStopSequence) || undefined,
401
- last_stop_headsign: tripStopTimes[lastStopSequence - 1].stop_headsign || undefined,
402
- state_position: statePosition,
403
- state_process: const_1.StateProcessEnum.PROCESSED,
404
- valid_to: this.validToCalculator.getDefaultValidToAttribute(currentPosition.properties.origin_timestamp),
405
- is_tracked: statePosition !== const_1.StatePositionEnum.AFTER_TRACK && statePosition !== const_1.StatePositionEnum.MISMATCHED,
406
- };
407
- return estimatedPoint;
408
- };
409
- /**
410
- * Corrects time delay at stop with dwelling time
411
- *
412
- * @param {number} timeDelay - Initial computed delay in seconds, can be negative for trip ahead
413
- * @param {IVPTripsLastPositionContext | null} context - Context state, holds information about previous positions
414
- * @param {Feature<Point, ICurrentPositionProperties>} currentPosition - Feature Point of current position
415
- * @param {IShapeAnchorPoint} thisClosestPoint - Closest point of shape anchors
416
- * @param { departureTime: number; arrivalTime: number } stopTimes - departure and arrival stop times in seconds
417
- * @returns {number} - Result delay in seconds, can be negative for trip ahead
418
- */
419
- this.getCorrectedTimeDelay = (timeDelay, context, currentPosition, thisStopSequence, stopTimes) => {
420
- // compute dwell time in stop, most common is zero
421
- const stopDwellTimeSeconds = stopTimes.departureTime - stopTimes.arrivalTime;
422
- // if dwell time is sheduled as zero, return initial computed delay
423
- if (stopDwellTimeSeconds <= 0) {
424
- return timeDelay;
425
- }
426
- // if last position was not in this same stop or there is no last position at all
427
- if (!context || context.atStopStreak.stop_sequence !== thisStopSequence) {
428
- // timeDelay >= 0 trip is DELAYED
429
- // we presume it will lower delay by shortening its scheduled dwell time,
430
- // cant go under zero of course, trip should not go ahead
431
- // else trip is AHeAD
432
- // left computed delay as it was
433
- return timeDelay >= 0 ? Math.max(timeDelay - stopDwellTimeSeconds, 0) : timeDelay;
434
- }
435
- // we presume that first position at same stop is real arrival time
436
- if (context.atStopStreak.firstPositionDelay >= 0) {
437
- // trip was DELAYED before
438
- // we presume it will lower delay by shortening its scheduled dwell time,
439
- // cant go under zero of course, trip should not go ahead
440
- return Math.max(timeDelay - stopDwellTimeSeconds, 0);
441
- }
442
- // trip was AHEAD before
443
- // real dwell time so far
444
- const realDwellTimeSeconds = Math.round((currentPosition.properties.origin_timestamp.getTime() - context.atStopStreak.firstPositionTimestamp) / 1000);
445
- // if real dwell is longer than scheduled, then add to negative delay time
446
- return context.atStopStreak.firstPositionDelay + Math.max(realDwellTimeSeconds - stopDwellTimeSeconds, 0);
447
- };
448
- /**
449
- * Compute UTC timestamp of start of day when trip starts
450
- *
451
- * @param {number} startTimestamp - Unix timestamp of start of the trip
452
- * @param {number} firstStopTimeScheduledSeconds - Number of seconds from midnight of first stop departure
453
- * @returns {number} - Returns unix timestamp in milliseconds.
454
- */
455
- this.getStartDayTimestamp = (startTimestamp, firstStopTimeScheduledSeconds) => {
456
- let startDayTimestamp = moment_timezone_1.default.utc(startTimestamp).tz("Europe/Prague").startOf("day");
457
- const stopTimeDayOverflow = Math.floor(firstStopTimeScheduledSeconds / ONE_DAY_IN_SECONDS);
458
- // if trip has 24+ stop times set real startDay to yesterday
459
- if (stopTimeDayOverflow > 0) {
460
- startDayTimestamp.subtract(1, "day");
461
- }
462
- return startDayTimestamp.valueOf();
463
- };
464
- }
51
+ class PositionsManager {
465
52
  /**
466
53
  * Get delay in seconds for positions before track to ensure correct delay propagation
467
54
  *
@@ -473,7 +60,7 @@ let PositionsManager = exports.PositionsManager = class PositionsManager {
473
60
  * @param positionOriginUnixTimestamp The Unix Epoch timestamp of the current position, in milliseconds
474
61
  * @param startDayUnixTimestamp The Unix Epoch timestamp of the start of the service day, in milliseconds
475
62
  */
476
- getDelayBeforeTrack(lastPositionDelay, firstStopDepartureTime, positionOriginUnixTimestamp, startDayUnixTimestamp) {
63
+ static getDelayBeforeTrack(lastPositionDelay, firstStopDepartureTime, positionOriginUnixTimestamp, startDayUnixTimestamp) {
477
64
  if (typeof lastPositionDelay !== "number") {
478
65
  return null;
479
66
  }
@@ -485,8 +72,8 @@ let PositionsManager = exports.PositionsManager = class PositionsManager {
485
72
  const positionOriginTimeInSeconds = Math.floor(positionOriginUnixTimestamp / ONE_SECOND_IN_MILLIS);
486
73
  return Math.max(lastPositionDelay, positionOriginTimeInSeconds - departureTimeInSeconds);
487
74
  }
488
- getStateAndStopSequences(gtfsRouteType, tripStopTimes, thisClosestPoint, positionProperties, context) {
489
- var _a;
75
+ static getStateAndStopSequences(gtfsRouteType, tripStopTimes, thisClosestPoint, positionProperties, context) {
76
+ var _b;
490
77
  let statePosition = thisClosestPoint.this_stop_sequence ? const_1.StatePositionEnum.AT_STOP : const_1.StatePositionEnum.ON_TRACK;
491
78
  let thisStopSequence = thisClosestPoint.this_stop_sequence;
492
79
  let lastStopSequence = thisClosestPoint.last_stop_sequence;
@@ -496,7 +83,7 @@ let PositionsManager = exports.PositionsManager = class PositionsManager {
496
83
  positionProperties.tcp_event === const_1.TCPEventEnum.TIME &&
497
84
  (context === null || context === void 0 ? void 0 : context.lastPositionTracking)) {
498
85
  const previousPositionTcpEvent = context.lastPositionTracking.properties.tcp_event;
499
- const previousLastStopSequence = (_a = context.lastPositionTracking.properties.last_stop_sequence) !== null && _a !== void 0 ? _a : 0;
86
+ const previousLastStopSequence = (_b = context.lastPositionTracking.properties.last_stop_sequence) !== null && _b !== void 0 ? _b : 0;
500
87
  // if previous position was 'P' and current last stop sequence is lower than the previous last stop sequence
501
88
  // or if previous position was 'O', current position is at stop
502
89
  // and current last stop sequence is lower than the previous last stop sequence
@@ -526,14 +113,399 @@ let PositionsManager = exports.PositionsManager = class PositionsManager {
526
113
  nextStopSequence,
527
114
  };
528
115
  }
529
- isAfterTrack(lastStopSequence, lastTripShapePoint) {
116
+ static isAfterTrack(lastStopSequence, lastTripShapePoint) {
530
117
  // Trip is terminated OR the vehicle is close to/in the terminus
531
118
  return lastStopSequence >= lastTripShapePoint.last_stop_sequence;
532
119
  }
120
+ }
121
+ _a = PositionsManager;
122
+ /**
123
+ * Compute positions and return computed positions
124
+ *
125
+ * @param {ITripPositionsWithGTFS} tripPositions - Trip positions with shape anchors data
126
+ * @returns {Promise<IProcessedPositions>} - Returns computed/updated positions
127
+ */
128
+ PositionsManager.computePositions = (tripPositions, schedule) => __awaiter(void 0, void 0, void 0, function* () {
129
+ const startTimestamp = tripPositions.start_timestamp.getTime();
130
+ const startDayTimestamp = _a.getStartDayTimestamp(startTimestamp, tripPositions.gtfsData.shapes_anchor_points[0].time_scheduled_seconds);
131
+ const gtfsRouteType = tripPositions.gtfs_route_type;
132
+ const context = PositionsManager.getCurrentContext(tripPositions);
133
+ const computedPositions = [];
134
+ const endTimestamp = tripPositions.end_timestamp;
135
+ return PositionsManager.updatePositions({ tripPositions, startTimestamp, endTimestamp, startDayTimestamp, context, computedPositions, gtfsRouteType }, schedule);
136
+ });
137
+ /**
138
+ * Takes position one by one, set proper handler for type of position, and do the process of position
139
+ *
140
+ * @param {number} i - Iteration
141
+ * @param {IUpdatePositionsIteratorOptions} options - Initial options
142
+ * @param {number} cb - Callback function of iterator
143
+ * @returns {void} - void
144
+ */
145
+ PositionsManager.updatePositions = (options, schedule) => __awaiter(void 0, void 0, void 0, function* () {
146
+ var _b, _c, _d, _e, _f, _g, _h;
147
+ const { tripPositions, startDayTimestamp, startTimestamp, endTimestamp, context, computedPositions, gtfsRouteType } = options;
148
+ for (let i = 0; i < tripPositions.positions.length; i++) {
149
+ const position = tripPositions.positions[i];
150
+ let positionToUpdate = null;
151
+ // situations
152
+ switch (_a.setPositionUpdateHandler(position)) {
153
+ case PositionHandlerEnum_1.PositionHandlerEnum.TRACKING:
154
+ const currentPosition = turf.point([position.lng, position.lat], {
155
+ id: position.id,
156
+ origin_time: position.origin_time,
157
+ origin_timestamp: position.origin_timestamp,
158
+ scheduled_timestamp: position.scheduled_timestamp,
159
+ this_stop_id: position.this_stop_id,
160
+ tcp_event: position.tcp_event,
161
+ });
162
+ positionToUpdate = _a.getEstimatedPoint(tripPositions.gtfsData, currentPosition, context, startDayTimestamp, tripPositions.gtfs_route_type);
163
+ if (positionToUpdate.state_position !== const_1.StatePositionEnum.MISMATCHED) {
164
+ positionToUpdate = ComputeDelayHelper_1.default.updatePositionToUpdate(context, positionToUpdate, position, gtfsRouteType);
165
+ }
166
+ positionToUpdate.bearing = (_b = position.bearing) !== null && _b !== void 0 ? _b : positionToUpdate.bearing;
167
+ break;
168
+ case PositionHandlerEnum_1.PositionHandlerEnum.NOT_TRACKING:
169
+ // if there is no previous positions with tracking status, set position as before_track
170
+ const firstShapesAnchorPoint = tripPositions.gtfsData.shapes_anchor_points[0];
171
+ if (context.lastPositionTracking === null &&
172
+ (endTimestamp === null ||
173
+ position.scheduled_timestamp === null ||
174
+ endTimestamp > position.scheduled_timestamp)) {
175
+ const firstStopTime = tripPositions.gtfsData.stop_times[0];
176
+ // if there is propagated delay we can use it for new position
177
+ const lastPositionDelayed = context.lastPositionBeforeTrackDelayed;
178
+ // if there is no delay to duplicate and DPP trip is far from start then invisible
179
+ const setAsInvisible = !lastPositionDelayed &&
180
+ DPPUtils_1.default.isInvisible(tripPositions.agency_name_scheduled, position.origin_timestamp, startTimestamp, [position.lng, position.lat], firstShapesAnchorPoint.coordinates, schedule, tripPositions.gtfs_trip_id, context.lastPositionTracking, position.is_tracked);
181
+ positionToUpdate = Object.assign({ id: position.id, next_stop_arrival_time: new Date(startDayTimestamp + firstStopTime.arrival_time_seconds * 1000), next_stop_departure_time: new Date(startDayTimestamp + firstStopTime.departure_time_seconds * 1000), next_stop_id: firstStopTime.stop_id, next_stop_sequence: firstStopTime.stop_sequence, next_stop_name: firstStopTime.stop.stop_name, shape_dist_traveled: firstStopTime.shape_dist_traveled, state_position: setAsInvisible ? const_1.StatePositionEnum.INVISIBLE : const_1.StatePositionEnum.BEFORE_TRACK, state_process: const_1.StateProcessEnum.PROCESSED }, (firstStopTime.stop_headsign && {
182
+ last_stop_headsign: firstStopTime.stop_headsign,
183
+ }));
184
+ if (positionToUpdate.state_position === const_1.StatePositionEnum.BEFORE_TRACK) {
185
+ positionToUpdate.delay = _a.getDelayBeforeTrack((_c = lastPositionDelayed === null || lastPositionDelayed === void 0 ? void 0 : lastPositionDelayed.delay) !== null && _c !== void 0 ? _c : position.delay, firstStopTime.departure_time_seconds, position.origin_timestamp.getTime(), startDayTimestamp);
186
+ }
187
+ }
188
+ else {
189
+ // if there is tracking 2 position with same origin_timestamp then this position is duplicate
190
+ const statePosition = position.origin_timestamp.getTime() === context.lastPositionOriginTimestamp ||
191
+ tripPositions.positions.findIndex((positionItem) => positionItem.origin_timestamp.getTime() === position.origin_timestamp.getTime() &&
192
+ positionItem.is_tracked) >= 0
193
+ ? const_1.StatePositionEnum.DUPLICATE
194
+ : const_1.StatePositionEnum.AFTER_TRACK;
195
+ const lastShapesAnchorPoint = tripPositions.gtfsData.shapes_anchor_points[tripPositions.gtfsData.shapes_anchor_points.length - 1];
196
+ const lastStopTime = tripPositions.gtfsData.stop_times[tripPositions.gtfsData.stop_times.length - 1];
197
+ // set as invisible if there are some AFTER_TRACK before
198
+ const setAsInvisible = DPPUtils_1.default.isInvisible(tripPositions.agency_name_scheduled, position.origin_timestamp, startTimestamp, [position.lng, position.lat], firstShapesAnchorPoint.coordinates, schedule, tripPositions.gtfs_trip_id, context.lastPositionTracking, position.is_tracked) && statePosition === const_1.StatePositionEnum.AFTER_TRACK;
199
+ positionToUpdate = Object.assign({ id: position.id, last_stop_arrival_time: new Date(startDayTimestamp + lastShapesAnchorPoint.time_scheduled_seconds * 1000), last_stop_departure_time: new Date(startDayTimestamp + lastShapesAnchorPoint.time_scheduled_seconds * 1000), last_stop_id: lastStopTime.stop_id, last_stop_sequence: lastShapesAnchorPoint.last_stop_sequence, shape_dist_traveled: lastShapesAnchorPoint.shape_dist_traveled, state_position: setAsInvisible ? const_1.StatePositionEnum.INVISIBLE : statePosition, state_process: const_1.StateProcessEnum.PROCESSED, delay: (_d = position.delay) !== null && _d !== void 0 ? _d : null }, (lastStopTime.stop_headsign && {
200
+ last_stop_headsign: lastStopTime.stop_headsign,
201
+ }));
202
+ positionToUpdate = ComputeDelayHelper_1.default.updatePositionToUpdate(context, positionToUpdate, position, gtfsRouteType);
203
+ }
204
+ break;
205
+ case PositionHandlerEnum_1.PositionHandlerEnum.CANCELED:
206
+ positionToUpdate = {
207
+ id: position.id,
208
+ state_position: const_1.StatePositionEnum.CANCELED,
209
+ state_process: const_1.StateProcessEnum.PROCESSED,
210
+ };
211
+ break;
212
+ case PositionHandlerEnum_1.PositionHandlerEnum.DO_NOTHING:
213
+ break;
214
+ default:
215
+ break;
216
+ }
217
+ // if not null push to update
218
+ if (positionToUpdate) {
219
+ (_e = positionToUpdate.is_tracked) !== null && _e !== void 0 ? _e : (positionToUpdate.is_tracked = position.is_tracked);
220
+ positionToUpdate.valid_to = ValidToCalculator_1.default.getValidToAttribute(positionToUpdate, position, gtfsRouteType, tripPositions.gtfsData, startTimestamp, startDayTimestamp);
221
+ computedPositions.push(positionToUpdate);
222
+ }
223
+ context.lastPositionState = (_f = positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) !== null && _f !== void 0 ? _f : position.state_position;
224
+ // set last known BEFORE_TRACK_DELAYED position
225
+ if (position.state_position === const_1.StatePositionEnum.BEFORE_TRACK_DELAYED) {
226
+ context.lastPositionBeforeTrackDelayed = {
227
+ delay: (_g = positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.delay) !== null && _g !== void 0 ? _g : position.delay,
228
+ origin_timestamp: (_h = positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.origin_timestamp) !== null && _h !== void 0 ? _h : position.origin_timestamp,
229
+ };
230
+ }
231
+ // set last position tracking (only for at_stop and on_track)
232
+ if ((positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) === const_1.StatePositionEnum.AT_STOP ||
233
+ (positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) === const_1.StatePositionEnum.ON_TRACK ||
234
+ position.state_position === const_1.StatePositionEnum.AT_STOP ||
235
+ position.state_position === const_1.StatePositionEnum.ON_TRACK) {
236
+ context.lastPositionTracking = turf.point([position.lng, position.lat], Object.assign(Object.assign({}, position), positionToUpdate));
237
+ }
238
+ // set new first position at stop streak if this stop seqence is set and it is not same as before
239
+ if (positionToUpdate
240
+ ? positionToUpdate.this_stop_sequence &&
241
+ context.atStopStreak.stop_sequence !== positionToUpdate.this_stop_sequence
242
+ : position.this_stop_sequence && context.atStopStreak.stop_sequence !== position.this_stop_sequence) {
243
+ context.atStopStreak.stop_sequence = positionToUpdate
244
+ ? positionToUpdate.this_stop_sequence
245
+ : position.this_stop_sequence;
246
+ context.atStopStreak.firstPositionTimestamp = position.origin_timestamp.getTime();
247
+ context.atStopStreak.firstPositionDelay = positionToUpdate ? positionToUpdate.delay : position.delay;
248
+ }
249
+ // IF currently valid updated position / position was processed before
250
+ // and it is NOT AT_STOP
251
+ // then disrupt atStopStreak
252
+ if (positionToUpdate &&
253
+ positionToUpdate.state_position !== const_1.StatePositionEnum.MISMATCHED &&
254
+ !positionToUpdate.this_stop_sequence) {
255
+ context.atStopStreak.stop_sequence = null;
256
+ }
257
+ else if (!positionToUpdate && !position.this_stop_sequence) {
258
+ context.atStopStreak.stop_sequence = null;
259
+ }
260
+ // duplicated or mismatched position should not be considered at all
261
+ if ((positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) !== const_1.StatePositionEnum.DUPLICATE &&
262
+ (positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) !== const_1.StatePositionEnum.INVISIBLE &&
263
+ (positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) !== const_1.StatePositionEnum.MISMATCHED) {
264
+ context.lastPositionId = position.id;
265
+ context.lastPositionCanceled = position.is_canceled;
266
+ context.lastPositionOriginTimestamp = position.origin_timestamp.getTime();
267
+ ComputeDelayHelper_1.default.updateContext(context, positionToUpdate, position, gtfsRouteType);
268
+ }
269
+ }
270
+ return {
271
+ context,
272
+ positions: computedPositions,
273
+ };
274
+ });
275
+ PositionsManager.getCurrentContext = (tripPositions) => {
276
+ var _b;
277
+ const context = (_b = tripPositions.last_position_context) !== null && _b !== void 0 ? _b : {
278
+ atStopStreak: {
279
+ stop_sequence: null,
280
+ firstPositionTimestamp: null,
281
+ firstPositionDelay: null,
282
+ },
283
+ lastPositionLastStop: {
284
+ id: null,
285
+ sequence: null,
286
+ arrival_time: null,
287
+ arrival_delay: null,
288
+ departure_time: null,
289
+ departure_delay: null,
290
+ },
291
+ lastPositionDelay: null,
292
+ lastPositionId: null,
293
+ lastPositionOriginTimestamp: null,
294
+ lastPositionTracking: null,
295
+ lastPositionCanceled: null,
296
+ lastPositionBeforeTrackDelayed: null,
297
+ lastPositionState: null,
298
+ tripId: tripPositions.id,
299
+ };
300
+ return context;
301
+ };
302
+ /**
303
+ * Decide how to process input position data
304
+ *
305
+ * @param {IVPTripsPositionAttributes} position - Input vehiclepositions_positions row data
306
+ * @returns {PositionHandlerEnum} - Returns action handler enum
307
+ */
308
+ PositionsManager.setPositionUpdateHandler = (position) => {
309
+ if (position.state_process === const_1.StateProcessEnum.PROCESSED)
310
+ return PositionHandlerEnum_1.PositionHandlerEnum.DO_NOTHING;
311
+ else if (position.is_canceled)
312
+ return PositionHandlerEnum_1.PositionHandlerEnum.CANCELED;
313
+ else if (position.is_tracked)
314
+ return PositionHandlerEnum_1.PositionHandlerEnum.TRACKING;
315
+ else
316
+ return PositionHandlerEnum_1.PositionHandlerEnum.NOT_TRACKING;
317
+ };
318
+ /**
319
+ * Returns estimate of point on shape, where the trip should be with appropriate delay
320
+ *
321
+ * @param {IShapeAnchorPoint[]} tripShapePoints - Precalculated trip shape equidistant points with scheduled times
322
+ * @param {Feature<Point, ICurrentPositionProperties>} currentPosition - Current position of trip
323
+ * @param {IVPTripsLastPositionContext | null} context - Context state, holds information about previous positions
324
+ * @param {number} startDayTimestamp - Unix timestamp of midnight before trip starts
325
+ * @returns {IPositionToUpdate} - Position object to update
326
+ */
327
+ PositionsManager.getEstimatedPoint = (tripGtfsData, currentPosition, context, startDayTimestamp, gtfsRouteType) => {
328
+ var _b, _c, _d, _e, _f, _g, _h, _j;
329
+ const anchorPointSegmenter = new AnchorPointSegmenter_1.AnchorPointSegmenter(tripGtfsData.shapes_anchor_points, currentPosition);
330
+ const defaultStatePosition = const_1.StatePositionEnum.OFF_TRACK;
331
+ // Initial value
332
+ let estimatedPoint = Object.assign({ id: currentPosition.properties.id, state_position: defaultStatePosition, state_process: const_1.StateProcessEnum.PROCESSED, tcp_event: currentPosition.properties.tcp_event, valid_to: ValidToCalculator_1.default.getDefaultValidToAttribute(currentPosition.properties.origin_timestamp) }, (context &&
333
+ ((_b = context.lastPositionTracking) === null || _b === void 0 ? void 0 : _b.properties.last_stop_sequence) && {
334
+ shape_dist_traveled: (_c = context.lastPositionTracking) === null || _c === void 0 ? void 0 : _c.properties.shape_dist_traveled,
335
+ last_stop_arrival_time: ((_d = context.lastPositionTracking) === null || _d === void 0 ? void 0 : _d.properties.last_stop_arrival_time)
336
+ ? new Date((_e = context.lastPositionTracking) === null || _e === void 0 ? void 0 : _e.properties.last_stop_arrival_time)
337
+ : undefined,
338
+ last_stop_departure_time: ((_f = context.lastPositionTracking) === null || _f === void 0 ? void 0 : _f.properties.last_stop_departure_time)
339
+ ? new Date((_g = context.lastPositionTracking) === null || _g === void 0 ? void 0 : _g.properties.last_stop_departure_time)
340
+ : undefined,
341
+ last_stop_sequence: (_h = context.lastPositionTracking) === null || _h === void 0 ? void 0 : _h.properties.last_stop_sequence,
342
+ last_stop_id: (_j = context.lastPositionTracking) === null || _j === void 0 ? void 0 : _j.properties.last_stop_id,
343
+ }));
344
+ let lastStopSequence = null;
345
+ if ((context === null || context === void 0 ? void 0 : context.lastPositionState) === const_1.StatePositionEnum.BEFORE_TRACK) {
346
+ lastStopSequence = 1;
347
+ }
348
+ else if (context === null || context === void 0 ? void 0 : context.lastPositionTracking) {
349
+ lastStopSequence = context.lastPositionTracking.properties.last_stop_sequence;
350
+ }
351
+ const closesPoint = anchorPointSegmenter.getClosesPoint(lastStopSequence);
352
+ if (!closesPoint) {
353
+ return estimatedPoint;
354
+ }
355
+ return _a.getClosestPoint(currentPosition, context, startDayTimestamp, closesPoint, tripGtfsData, gtfsRouteType);
356
+ };
357
+ /**
358
+ * Picks only one closest point for multiple possible points based on delay of last position and stop times
359
+ *
360
+ * @param {Feature<Point, ICurrentPositionProperties>} currentPosition - Feature Point of current position
361
+ * @param {IVPTripsLastPositionContext | null} context - Context state, holds information about previous positions
362
+ * @param {number} startDayTimestamp - Unix timestamp of start of the day
363
+ * @param {IShapeAnchorPoint[]} closestPts - All closest points of possible segments
364
+ * @param {IComputationTrip} tripGtfsData - GTFS data and all set of known positions
365
+ * @returns {IPositionToUpdate} - Result point as position to update in DB
366
+ */
367
+ PositionsManager.getClosestPoint = (currentPosition, context, startDayTimestamp, thisClosestPoint, tripGtfsData, gtfsRouteType) => {
368
+ // want to find minimum difference of our prediction, where the bus should be
369
+ const tripShapePoints = tripGtfsData.shapes_anchor_points;
370
+ const tripStopTimes = tripGtfsData.stop_times;
371
+ const firstTripShapePoint = tripShapePoints[0];
372
+ const lastTripShapePoint = tripShapePoints[tripShapePoints.length - 1];
373
+ const timeScheduledTimestamp = DateTimeUtils_1.DateTimeUtils.getStopDateTimeForDayStart(
374
+ // take stop arrival time if is point at stop
375
+ thisClosestPoint.this_stop_sequence
376
+ ? tripStopTimes[thisClosestPoint.this_stop_sequence - 1].arrival_time_seconds
377
+ : thisClosestPoint.time_scheduled_seconds, startDayTimestamp);
378
+ let timeDelayInSeconds = Math.round((currentPosition.properties.origin_timestamp.getTime() - timeScheduledTimestamp.getTime()) / 1000);
379
+ const isBusOrTram = gtfsRouteType === RouteTypeEnums_1.GTFSRouteTypeEnum.BUS ||
380
+ gtfsRouteType === RouteTypeEnums_1.GTFSRouteTypeEnum.TROLLEYBUS ||
381
+ gtfsRouteType === RouteTypeEnums_1.GTFSRouteTypeEnum.TRAM;
382
+ // push trip into nearest at_stop shape anchor point if trip "P" event (TCP tram or bus)
383
+ if (isBusOrTram &&
384
+ currentPosition.properties.tcp_event === const_1.TCPEventEnum.ARRIVAL_ANNOUNCED &&
385
+ thisClosestPoint.this_stop_sequence === null) {
386
+ if (thisClosestPoint.last_stop_sequence === 1 && !(context === null || context === void 0 ? void 0 : context.lastPositionTracking)) {
387
+ thisClosestPoint = firstTripShapePoint;
388
+ }
389
+ else {
390
+ let movingShapePointIndex = thisClosestPoint.index;
391
+ while (thisClosestPoint.this_stop_sequence === null && movingShapePointIndex < lastTripShapePoint.index) {
392
+ movingShapePointIndex++;
393
+ thisClosestPoint = tripShapePoints[movingShapePointIndex];
394
+ }
395
+ }
396
+ }
397
+ // lets correct delay if it is at stop
398
+ if (thisClosestPoint.this_stop_sequence) {
399
+ const thisStopSequence = thisClosestPoint.this_stop_sequence;
400
+ timeDelayInSeconds = _a.getCorrectedTimeDelay(timeDelayInSeconds, context, currentPosition, thisStopSequence, {
401
+ departureTime: tripStopTimes[thisStopSequence - 1].departure_time_seconds,
402
+ arrivalTime: tripStopTimes[thisStopSequence - 1].arrival_time_seconds,
403
+ });
404
+ // delay can not be negative if the vehicle is at the first stop
405
+ if (thisStopSequence === firstTripShapePoint.this_stop_sequence) {
406
+ timeDelayInSeconds = Math.max(timeDelayInSeconds, 0);
407
+ }
408
+ }
409
+ let { statePosition, thisStopSequence, lastStopSequence, nextStopSequence } = PositionsManager.getStateAndStopSequences(gtfsRouteType, tripStopTimes, thisClosestPoint, currentPosition.properties, context);
410
+ // test if TCP trip with DEPARTURED event should not be at stop (not applied for last stop)
411
+ if (currentPosition.properties.tcp_event === const_1.TCPEventEnum.DEPARTURED &&
412
+ thisStopSequence !== lastTripShapePoint.this_stop_sequence) {
413
+ statePosition = const_1.StatePositionEnum.ON_TRACK;
414
+ }
415
+ if (isBusOrTram && PositionsManager.isAfterTrack(lastStopSequence, lastTripShapePoint)) {
416
+ statePosition = const_1.StatePositionEnum.AFTER_TRACK;
417
+ thisStopSequence = lastTripShapePoint.this_stop_sequence;
418
+ lastStopSequence = lastTripShapePoint.last_stop_sequence;
419
+ nextStopSequence = lastTripShapePoint.next_stop_sequence;
420
+ timeDelayInSeconds = 0;
421
+ }
422
+ // save it for result
423
+ const estimatedPoint = {
424
+ id: currentPosition.properties.id,
425
+ bearing: thisClosestPoint.bearing,
426
+ shape_dist_traveled: thisClosestPoint.shape_dist_traveled,
427
+ next_stop_id: tripStopTimes[nextStopSequence - 1].stop_id,
428
+ last_stop_id: tripStopTimes[lastStopSequence - 1].stop_id,
429
+ next_stop_name: tripStopTimes[nextStopSequence - 1].stop.stop_name,
430
+ last_stop_name: tripStopTimes[lastStopSequence - 1].stop.stop_name,
431
+ next_stop_sequence: nextStopSequence,
432
+ last_stop_sequence: lastStopSequence,
433
+ next_stop_arrival_time: DateTimeUtils_1.DateTimeUtils.getStopDateTimeForDayStart(tripStopTimes[nextStopSequence - 1].arrival_time_seconds, startDayTimestamp),
434
+ last_stop_arrival_time: DateTimeUtils_1.DateTimeUtils.getStopDateTimeForDayStart(tripStopTimes[lastStopSequence - 1].arrival_time_seconds, startDayTimestamp),
435
+ next_stop_departure_time: DateTimeUtils_1.DateTimeUtils.getStopDateTimeForDayStart(tripStopTimes[nextStopSequence - 1].departure_time_seconds, startDayTimestamp),
436
+ last_stop_departure_time: DateTimeUtils_1.DateTimeUtils.getStopDateTimeForDayStart(tripStopTimes[lastStopSequence - 1].departure_time_seconds, startDayTimestamp),
437
+ delay: timeDelayInSeconds,
438
+ this_stop_id: (statePosition === const_1.StatePositionEnum.AT_STOP &&
439
+ thisStopSequence &&
440
+ tripStopTimes[thisStopSequence - 1].stop_id) ||
441
+ undefined,
442
+ this_stop_name: (statePosition === const_1.StatePositionEnum.AT_STOP &&
443
+ thisStopSequence &&
444
+ tripStopTimes[thisStopSequence - 1].stop.stop_name) ||
445
+ undefined,
446
+ this_stop_sequence: (statePosition === const_1.StatePositionEnum.AT_STOP && thisStopSequence) || undefined,
447
+ last_stop_headsign: tripStopTimes[lastStopSequence - 1].stop_headsign || undefined,
448
+ state_position: statePosition,
449
+ state_process: const_1.StateProcessEnum.PROCESSED,
450
+ valid_to: ValidToCalculator_1.default.getDefaultValidToAttribute(currentPosition.properties.origin_timestamp),
451
+ is_tracked: statePosition !== const_1.StatePositionEnum.AFTER_TRACK && statePosition !== const_1.StatePositionEnum.MISMATCHED,
452
+ };
453
+ return estimatedPoint;
454
+ };
455
+ /**
456
+ * Corrects time delay at stop with dwelling time
457
+ *
458
+ * @param {number} timeDelay - Initial computed delay in seconds, can be negative for trip ahead
459
+ * @param {IVPTripsLastPositionContext | null} context - Context state, holds information about previous positions
460
+ * @param {Feature<Point, ICurrentPositionProperties>} currentPosition - Feature Point of current position
461
+ * @param {IShapeAnchorPoint} thisClosestPoint - Closest point of shape anchors
462
+ * @param { departureTime: number; arrivalTime: number } stopTimes - departure and arrival stop times in seconds
463
+ * @returns {number} - Result delay in seconds, can be negative for trip ahead
464
+ */
465
+ PositionsManager.getCorrectedTimeDelay = (timeDelay, context, currentPosition, thisStopSequence, stopTimes) => {
466
+ // compute dwell time in stop, most common is zero
467
+ const stopDwellTimeSeconds = stopTimes.departureTime - stopTimes.arrivalTime;
468
+ // if dwell time is sheduled as zero, return initial computed delay
469
+ if (stopDwellTimeSeconds <= 0) {
470
+ return timeDelay;
471
+ }
472
+ // if last position was not in this same stop or there is no last position at all
473
+ if (!context || context.atStopStreak.stop_sequence !== thisStopSequence) {
474
+ // timeDelay >= 0 trip is DELAYED
475
+ // we presume it will lower delay by shortening its scheduled dwell time,
476
+ // cant go under zero of course, trip should not go ahead
477
+ // else trip is AHeAD
478
+ // left computed delay as it was
479
+ return timeDelay >= 0 ? Math.max(timeDelay - stopDwellTimeSeconds, 0) : timeDelay;
480
+ }
481
+ // we presume that first position at same stop is real arrival time
482
+ if (context.atStopStreak.firstPositionDelay >= 0) {
483
+ // trip was DELAYED before
484
+ // we presume it will lower delay by shortening its scheduled dwell time,
485
+ // cant go under zero of course, trip should not go ahead
486
+ return Math.max(timeDelay - stopDwellTimeSeconds, 0);
487
+ }
488
+ // trip was AHEAD before
489
+ // real dwell time so far
490
+ const realDwellTimeSeconds = Math.round((currentPosition.properties.origin_timestamp.getTime() - context.atStopStreak.firstPositionTimestamp) / 1000);
491
+ // if real dwell is longer than scheduled, then add to negative delay time
492
+ return context.atStopStreak.firstPositionDelay + Math.max(realDwellTimeSeconds - stopDwellTimeSeconds, 0);
493
+ };
494
+ /**
495
+ * Compute UTC timestamp of start of day when trip starts
496
+ *
497
+ * @param {number} startTimestamp - Unix timestamp of start of the trip
498
+ * @param {number} firstStopTimeScheduledSeconds - Number of seconds from midnight of first stop departure
499
+ * @returns {number} - Returns unix timestamp in milliseconds.
500
+ */
501
+ PositionsManager.getStartDayTimestamp = (startTimestamp, firstStopTimeScheduledSeconds) => {
502
+ let startDayTimestamp = moment_timezone_1.default.utc(startTimestamp).tz("Europe/Prague").startOf("day");
503
+ const stopTimeDayOverflow = Math.floor(firstStopTimeScheduledSeconds / ONE_DAY_IN_SECONDS);
504
+ // if trip has 24+ stop times set real startDay to yesterday
505
+ if (stopTimeDayOverflow > 0) {
506
+ startDayTimestamp.subtract(1, "day");
507
+ }
508
+ return startDayTimestamp.valueOf();
533
509
  };
534
- exports.PositionsManager = PositionsManager = __decorate([
535
- (0, tsyringe_1.injectable)(),
536
- __param(0, (0, tsyringe_1.inject)(VPContainerToken_1.VPContainerToken.ValidToCalculator)),
537
- __metadata("design:paramtypes", [ValidToCalculator_1.ValidToCalculator])
538
- ], PositionsManager);
510
+ exports.default = PositionsManager;
539
511
  //# sourceMappingURL=PositionsManager.js.map