@golemio/pid 2.14.1 → 2.14.2-dev.1355461560

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 (54) hide show
  1. package/db/migrations/postgresql/20240612135256-fix-delay-in-view-future-stop-times.js +53 -0
  2. package/db/migrations/postgresql/sqls/20240612135256-fix-delay-in-view-future-stop-times-down.sql +82 -0
  3. package/db/migrations/postgresql/sqls/20240612135256-fix-delay-in-view-future-stop-times-up.sql +93 -0
  4. package/dist/integration-engine/data-retention/helpers/SelectRepositoryFactory.js +1 -2
  5. package/dist/integration-engine/data-retention/helpers/SelectRepositoryFactory.js.map +1 -1
  6. package/dist/integration-engine/vehicle-positions/ioc/Di.js +14 -9
  7. package/dist/integration-engine/vehicle-positions/ioc/Di.js.map +1 -1
  8. package/dist/integration-engine/vehicle-positions/ioc/VPContainerToken.d.ts +3 -0
  9. package/dist/integration-engine/vehicle-positions/ioc/VPContainerToken.js +3 -0
  10. package/dist/integration-engine/vehicle-positions/ioc/VPContainerToken.js.map +1 -1
  11. package/dist/integration-engine/vehicle-positions/workers/gtfs-rt/helpers/VehicleDescriptor.js +4 -1
  12. package/dist/integration-engine/vehicle-positions/workers/gtfs-rt/helpers/VehicleDescriptor.js.map +1 -1
  13. package/dist/integration-engine/vehicle-positions/workers/gtfs-rt/tasks/GenerateFilesTask.js +3 -2
  14. package/dist/integration-engine/vehicle-positions/workers/gtfs-rt/tasks/GenerateFilesTask.js.map +1 -1
  15. package/dist/integration-engine/vehicle-positions/workers/runs/helpers/regional-bus/RegionalBusMessageFilter.d.ts +1 -4
  16. package/dist/integration-engine/vehicle-positions/workers/runs/helpers/regional-bus/RegionalBusMessageFilter.js +11 -27
  17. package/dist/integration-engine/vehicle-positions/workers/runs/helpers/regional-bus/RegionalBusMessageFilter.js.map +1 -1
  18. package/dist/integration-engine/vehicle-positions/workers/vehicle-descriptors/data-access/DescriptorRepository.d.ts +5 -1
  19. package/dist/integration-engine/vehicle-positions/workers/vehicle-descriptors/data-access/DescriptorRepository.js +31 -2
  20. package/dist/integration-engine/vehicle-positions/workers/vehicle-descriptors/data-access/DescriptorRepository.js.map +1 -1
  21. package/dist/integration-engine/vehicle-positions/workers/vehicle-descriptors/tasks/RefreshDescriptorsTask.js +1 -2
  22. package/dist/integration-engine/vehicle-positions/workers/vehicle-descriptors/tasks/RefreshDescriptorsTask.js.map +1 -1
  23. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/data-access/PositionsRepository.js +11 -3
  24. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/data-access/PositionsRepository.js.map +1 -1
  25. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/data-access/TripsRepository.js +3 -3
  26. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/data-access/TripsRepository.js.map +1 -1
  27. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/helpers/DelayComputationManager.js +13 -3
  28. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/helpers/DelayComputationManager.js.map +1 -1
  29. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/helpers/PositionsManager.d.ts +15 -12
  30. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/helpers/PositionsManager.js +424 -399
  31. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/helpers/PositionsManager.js.map +1 -1
  32. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/helpers/ValidToCalculator.d.ts +15 -6
  33. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/helpers/ValidToCalculator.js +71 -45
  34. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/helpers/ValidToCalculator.js.map +1 -1
  35. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/helpers/regional-bus/RegionalBusPositionsManager.d.ts +12 -9
  36. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/helpers/regional-bus/RegionalBusPositionsManager.js +366 -347
  37. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/helpers/regional-bus/RegionalBusPositionsManager.js.map +1 -1
  38. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/tasks/ProcessRegionalBusPositionsTask.d.ts +1 -0
  39. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/tasks/ProcessRegionalBusPositionsTask.js +5 -4
  40. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/tasks/ProcessRegionalBusPositionsTask.js.map +1 -1
  41. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/tasks/UpdateDelayTask.d.ts +1 -0
  42. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/tasks/UpdateDelayTask.js +2 -5
  43. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/tasks/UpdateDelayTask.js.map +1 -1
  44. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/transformations/MpvMessageTransformation.d.ts +3 -6
  45. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/transformations/MpvMessageTransformation.js +8 -19
  46. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/transformations/MpvMessageTransformation.js.map +1 -1
  47. package/dist/output-gateway/vehicle-positions/data-access/helpers/PositionOutputMapper.js +3 -2
  48. package/dist/output-gateway/vehicle-positions/data-access/helpers/PositionOutputMapper.js.map +1 -1
  49. package/dist/output-gateway/vehicle-positions/data-access/views/TripWithLastPositionRepository.js +1 -1
  50. package/dist/output-gateway/vehicle-positions/data-access/views/TripWithLastPositionRepository.js.map +1 -1
  51. package/dist/schema-definitions/vehicle-positions/models/interfaces/IGtfsRtTripDto.d.ts +1 -0
  52. package/dist/schema-definitions/vehicle-positions/models/views/TripWithLastPositionModel.d.ts +1 -1
  53. package/docs/openapi-output.yaml +5 -0
  54. package/package.json +4 -4
@@ -15,6 +15,12 @@ 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
+ };
18
24
  var __importStar = (this && this.__importStar) || function (mod) {
19
25
  if (mod && mod.__esModule) return mod;
20
26
  var result = {};
@@ -22,6 +28,12 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
28
  __setModuleDefault(result, mod);
23
29
  return result;
24
30
  };
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
+ };
25
37
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
38
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
39
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -34,21 +46,419 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
34
46
  var __importDefault = (this && this.__importDefault) || function (mod) {
35
47
  return (mod && mod.__esModule) ? mod : { "default": mod };
36
48
  };
37
- var _a;
38
49
  Object.defineProperty(exports, "__esModule", { value: true });
39
- const turf = __importStar(require("@turf/turf"));
50
+ exports.PositionsManager = void 0;
51
+ const VPContainerToken_1 = require("../../../ioc/VPContainerToken");
40
52
  const moment_timezone_1 = __importDefault(require("@golemio/core/dist/shared/moment-timezone"));
53
+ const tsyringe_1 = require("@golemio/core/dist/shared/tsyringe");
54
+ const turf = __importStar(require("@turf/turf"));
41
55
  const const_1 = require("../../../../../const");
42
56
  const RouteTypeEnums_1 = require("../../../../../helpers/RouteTypeEnums");
43
57
  const DPPUtils_1 = __importDefault(require("./DPPUtils"));
44
58
  const DateTimeUtils_1 = require("./DateTimeUtils");
45
59
  const PositionHandlerEnum_1 = require("./PositionHandlerEnum");
46
- const ValidToCalculator_1 = __importDefault(require("./ValidToCalculator"));
60
+ const ValidToCalculator_1 = require("./ValidToCalculator");
47
61
  const AnchorPointSegmenter_1 = require("./anchor-points/AnchorPointSegmenter");
48
62
  const ComputeDelayHelper_1 = __importDefault(require("./compute-positions/ComputeDelayHelper"));
49
63
  const ONE_DAY_IN_SECONDS = 24 * 60 * 60;
50
64
  const ONE_SECOND_IN_MILLIS = 1000;
51
- class PositionsManager {
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
+ context.lastPositionState = (_l = positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) !== null && _l !== void 0 ? _l : position.state_position;
175
+ // set last known BEFORE_TRACK_DELAYED position
176
+ if (position.state_position === const_1.StatePositionEnum.BEFORE_TRACK_DELAYED) {
177
+ context.lastPositionBeforeTrackDelayed = {
178
+ delay: (_m = positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.delay) !== null && _m !== void 0 ? _m : position.delay,
179
+ origin_timestamp: (_o = positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.origin_timestamp) !== null && _o !== void 0 ? _o : position.origin_timestamp,
180
+ };
181
+ }
182
+ // set last position tracking (only for at_stop and on_track)
183
+ if ((positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) === const_1.StatePositionEnum.AT_STOP ||
184
+ (positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) === const_1.StatePositionEnum.ON_TRACK ||
185
+ position.state_position === const_1.StatePositionEnum.AT_STOP ||
186
+ position.state_position === const_1.StatePositionEnum.ON_TRACK) {
187
+ context.lastPositionTracking = turf.point([position.lng, position.lat], Object.assign(Object.assign({}, position), positionToUpdate));
188
+ }
189
+ // set new first position at stop streak if this stop seqence is set and it is not same as before
190
+ if (positionToUpdate
191
+ ? positionToUpdate.this_stop_sequence &&
192
+ context.atStopStreak.stop_sequence !== positionToUpdate.this_stop_sequence
193
+ : position.this_stop_sequence && context.atStopStreak.stop_sequence !== position.this_stop_sequence) {
194
+ context.atStopStreak.stop_sequence = positionToUpdate
195
+ ? positionToUpdate.this_stop_sequence
196
+ : position.this_stop_sequence;
197
+ context.atStopStreak.firstPositionTimestamp = position.origin_timestamp.getTime();
198
+ context.atStopStreak.firstPositionDelay = positionToUpdate ? positionToUpdate.delay : position.delay;
199
+ }
200
+ // IF currently valid updated position / position was processed before
201
+ // and it is NOT AT_STOP
202
+ // then disrupt atStopStreak
203
+ if (positionToUpdate &&
204
+ positionToUpdate.state_position !== const_1.StatePositionEnum.MISMATCHED &&
205
+ !positionToUpdate.this_stop_sequence) {
206
+ context.atStopStreak.stop_sequence = null;
207
+ }
208
+ else if (!positionToUpdate && !position.this_stop_sequence) {
209
+ context.atStopStreak.stop_sequence = null;
210
+ }
211
+ // duplicated or mismatched position should not be considered at all
212
+ if ((positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) !== const_1.StatePositionEnum.DUPLICATE &&
213
+ (positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) !== const_1.StatePositionEnum.INVISIBLE &&
214
+ (positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) !== const_1.StatePositionEnum.MISMATCHED) {
215
+ context.lastPositionId = position.id;
216
+ context.lastPositionCanceled = position.is_canceled;
217
+ context.lastPositionOriginTimestamp = position.origin_timestamp.getTime();
218
+ ComputeDelayHelper_1.default.updateContext(context, positionToUpdate, position, gtfsRouteType);
219
+ }
220
+ }
221
+ return {
222
+ context,
223
+ positions: computedPositions,
224
+ };
225
+ });
226
+ this.getCurrentContext = (tripPositions) => {
227
+ var _a;
228
+ const context = (_a = tripPositions.last_position_context) !== null && _a !== void 0 ? _a : {
229
+ atStopStreak: {
230
+ stop_sequence: null,
231
+ firstPositionTimestamp: null,
232
+ firstPositionDelay: null,
233
+ },
234
+ lastPositionLastStop: {
235
+ id: null,
236
+ sequence: null,
237
+ arrival_time: null,
238
+ arrival_delay: null,
239
+ departure_time: null,
240
+ departure_delay: null,
241
+ },
242
+ lastPositionDelay: null,
243
+ lastPositionId: null,
244
+ lastPositionOriginTimestamp: null,
245
+ lastPositionTracking: null,
246
+ lastPositionCanceled: null,
247
+ lastPositionBeforeTrackDelayed: null,
248
+ lastPositionState: null,
249
+ tripId: tripPositions.id,
250
+ };
251
+ return context;
252
+ };
253
+ /**
254
+ * Decide how to process input position data
255
+ *
256
+ * @param {IVPTripsPositionAttributes} position - Input vehiclepositions_positions row data
257
+ * @returns {PositionHandlerEnum} - Returns action handler enum
258
+ */
259
+ this.setPositionUpdateHandler = (position) => {
260
+ if (position.state_process === const_1.StateProcessEnum.PROCESSED)
261
+ return PositionHandlerEnum_1.PositionHandlerEnum.DO_NOTHING;
262
+ else if (position.is_canceled)
263
+ return PositionHandlerEnum_1.PositionHandlerEnum.CANCELED;
264
+ else if (position.is_tracked)
265
+ return PositionHandlerEnum_1.PositionHandlerEnum.TRACKING;
266
+ else
267
+ return PositionHandlerEnum_1.PositionHandlerEnum.NOT_TRACKING;
268
+ };
269
+ /**
270
+ * Returns estimate of point on shape, where the trip should be with appropriate delay
271
+ *
272
+ * @param {IShapeAnchorPoint[]} tripShapePoints - Precalculated trip shape equidistant points with scheduled times
273
+ * @param {Feature<Point, ICurrentPositionProperties>} currentPosition - Current position of trip
274
+ * @param {IVPTripsLastPositionContext | null} context - Context state, holds information about previous positions
275
+ * @param {number} startDayTimestamp - Unix timestamp of midnight before trip starts
276
+ * @returns {IPositionToUpdate} - Position object to update
277
+ */
278
+ this.getEstimatedPoint = (tripGtfsData, currentPosition, context, startDayTimestamp, gtfsRouteType) => {
279
+ var _a, _b, _c, _d, _e, _f, _g, _h;
280
+ const anchorPointSegmenter = new AnchorPointSegmenter_1.AnchorPointSegmenter(tripGtfsData.shapes_anchor_points, currentPosition);
281
+ const defaultStatePosition = const_1.StatePositionEnum.OFF_TRACK;
282
+ // Initial value
283
+ 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 &&
284
+ ((_a = context.lastPositionTracking) === null || _a === void 0 ? void 0 : _a.properties.last_stop_sequence) && {
285
+ shape_dist_traveled: (_b = context.lastPositionTracking) === null || _b === void 0 ? void 0 : _b.properties.shape_dist_traveled,
286
+ last_stop_arrival_time: ((_c = context.lastPositionTracking) === null || _c === void 0 ? void 0 : _c.properties.last_stop_arrival_time)
287
+ ? new Date((_d = context.lastPositionTracking) === null || _d === void 0 ? void 0 : _d.properties.last_stop_arrival_time)
288
+ : undefined,
289
+ last_stop_departure_time: ((_e = context.lastPositionTracking) === null || _e === void 0 ? void 0 : _e.properties.last_stop_departure_time)
290
+ ? new Date((_f = context.lastPositionTracking) === null || _f === void 0 ? void 0 : _f.properties.last_stop_departure_time)
291
+ : undefined,
292
+ last_stop_sequence: (_g = context.lastPositionTracking) === null || _g === void 0 ? void 0 : _g.properties.last_stop_sequence,
293
+ last_stop_id: (_h = context.lastPositionTracking) === null || _h === void 0 ? void 0 : _h.properties.last_stop_id,
294
+ }));
295
+ let lastStopSequence = null;
296
+ if ((context === null || context === void 0 ? void 0 : context.lastPositionState) === const_1.StatePositionEnum.BEFORE_TRACK) {
297
+ lastStopSequence = 1;
298
+ }
299
+ else if (context === null || context === void 0 ? void 0 : context.lastPositionTracking) {
300
+ lastStopSequence = context.lastPositionTracking.properties.last_stop_sequence;
301
+ }
302
+ const closesPoint = anchorPointSegmenter.getClosesPoint(lastStopSequence);
303
+ if (!closesPoint) {
304
+ return estimatedPoint;
305
+ }
306
+ return this.getClosestPoint(currentPosition, context, startDayTimestamp, closesPoint, tripGtfsData, gtfsRouteType);
307
+ };
308
+ /**
309
+ * Picks only one closest point for multiple possible points based on delay of last position and stop times
310
+ *
311
+ * @param {Feature<Point, ICurrentPositionProperties>} currentPosition - Feature Point of current position
312
+ * @param {IVPTripsLastPositionContext | null} context - Context state, holds information about previous positions
313
+ * @param {number} startDayTimestamp - Unix timestamp of start of the day
314
+ * @param {IShapeAnchorPoint[]} closestPts - All closest points of possible segments
315
+ * @param {IComputationTrip} tripGtfsData - GTFS data and all set of known positions
316
+ * @returns {IPositionToUpdate} - Result point as position to update in DB
317
+ */
318
+ this.getClosestPoint = (currentPosition, context, startDayTimestamp, thisClosestPoint, tripGtfsData, gtfsRouteType) => {
319
+ // want to find minimum difference of our prediction, where the bus should be
320
+ const tripShapePoints = tripGtfsData.shapes_anchor_points;
321
+ const tripStopTimes = tripGtfsData.stop_times;
322
+ const firstTripShapePoint = tripShapePoints[0];
323
+ const lastTripShapePoint = tripShapePoints[tripShapePoints.length - 1];
324
+ const timeScheduledTimestamp = DateTimeUtils_1.DateTimeUtils.getStopDateTimeForDayStart(
325
+ // take stop arrival time if is point at stop
326
+ thisClosestPoint.this_stop_sequence
327
+ ? tripStopTimes[thisClosestPoint.this_stop_sequence - 1].arrival_time_seconds
328
+ : thisClosestPoint.time_scheduled_seconds, startDayTimestamp);
329
+ let timeDelayInSeconds = Math.round((currentPosition.properties.origin_timestamp.getTime() - timeScheduledTimestamp.getTime()) / 1000);
330
+ const isBusOrTram = gtfsRouteType === RouteTypeEnums_1.GTFSRouteTypeEnum.BUS ||
331
+ gtfsRouteType === RouteTypeEnums_1.GTFSRouteTypeEnum.TROLLEYBUS ||
332
+ gtfsRouteType === RouteTypeEnums_1.GTFSRouteTypeEnum.TRAM;
333
+ // push trip into nearest at_stop shape anchor point if trip "P" event (TCP tram or bus)
334
+ if (isBusOrTram &&
335
+ currentPosition.properties.tcp_event === const_1.TCPEventEnum.ARRIVAL_ANNOUNCED &&
336
+ thisClosestPoint.this_stop_sequence === null) {
337
+ if (thisClosestPoint.last_stop_sequence === 1 && !(context === null || context === void 0 ? void 0 : context.lastPositionTracking)) {
338
+ thisClosestPoint = firstTripShapePoint;
339
+ }
340
+ else {
341
+ let movingShapePointIndex = thisClosestPoint.index;
342
+ while (thisClosestPoint.this_stop_sequence === null && movingShapePointIndex < lastTripShapePoint.index) {
343
+ movingShapePointIndex++;
344
+ thisClosestPoint = tripShapePoints[movingShapePointIndex];
345
+ }
346
+ }
347
+ }
348
+ // lets correct delay if it is at stop
349
+ if (thisClosestPoint.this_stop_sequence) {
350
+ const thisStopSequence = thisClosestPoint.this_stop_sequence;
351
+ timeDelayInSeconds = this.getCorrectedTimeDelay(timeDelayInSeconds, context, currentPosition, thisStopSequence, {
352
+ departureTime: tripStopTimes[thisStopSequence - 1].departure_time_seconds,
353
+ arrivalTime: tripStopTimes[thisStopSequence - 1].arrival_time_seconds,
354
+ });
355
+ // delay can not be negative if the vehicle is at the first stop
356
+ if (thisStopSequence === firstTripShapePoint.this_stop_sequence) {
357
+ timeDelayInSeconds = Math.max(timeDelayInSeconds, 0);
358
+ }
359
+ }
360
+ let { statePosition, thisStopSequence, lastStopSequence, nextStopSequence } = this.getStateAndStopSequences(gtfsRouteType, tripStopTimes, thisClosestPoint, currentPosition.properties, context);
361
+ // test if TCP trip with DEPARTURED event should not be at stop (not applied for last stop)
362
+ if (currentPosition.properties.tcp_event === const_1.TCPEventEnum.DEPARTURED &&
363
+ thisStopSequence !== lastTripShapePoint.this_stop_sequence) {
364
+ statePosition = const_1.StatePositionEnum.ON_TRACK;
365
+ }
366
+ if (isBusOrTram && this.isAfterTrack(lastStopSequence, lastTripShapePoint)) {
367
+ statePosition = const_1.StatePositionEnum.AFTER_TRACK;
368
+ thisStopSequence = lastTripShapePoint.this_stop_sequence;
369
+ lastStopSequence = lastTripShapePoint.last_stop_sequence;
370
+ nextStopSequence = lastTripShapePoint.next_stop_sequence;
371
+ timeDelayInSeconds = 0;
372
+ }
373
+ // save it for result
374
+ const estimatedPoint = {
375
+ id: currentPosition.properties.id,
376
+ bearing: thisClosestPoint.bearing,
377
+ shape_dist_traveled: thisClosestPoint.shape_dist_traveled,
378
+ next_stop_id: tripStopTimes[nextStopSequence - 1].stop_id,
379
+ last_stop_id: tripStopTimes[lastStopSequence - 1].stop_id,
380
+ next_stop_name: tripStopTimes[nextStopSequence - 1].stop.stop_name,
381
+ last_stop_name: tripStopTimes[lastStopSequence - 1].stop.stop_name,
382
+ next_stop_sequence: nextStopSequence,
383
+ last_stop_sequence: lastStopSequence,
384
+ next_stop_arrival_time: DateTimeUtils_1.DateTimeUtils.getStopDateTimeForDayStart(tripStopTimes[nextStopSequence - 1].arrival_time_seconds, startDayTimestamp),
385
+ last_stop_arrival_time: DateTimeUtils_1.DateTimeUtils.getStopDateTimeForDayStart(tripStopTimes[lastStopSequence - 1].arrival_time_seconds, startDayTimestamp),
386
+ next_stop_departure_time: DateTimeUtils_1.DateTimeUtils.getStopDateTimeForDayStart(tripStopTimes[nextStopSequence - 1].departure_time_seconds, startDayTimestamp),
387
+ last_stop_departure_time: DateTimeUtils_1.DateTimeUtils.getStopDateTimeForDayStart(tripStopTimes[lastStopSequence - 1].departure_time_seconds, startDayTimestamp),
388
+ delay: timeDelayInSeconds,
389
+ this_stop_id: (statePosition === const_1.StatePositionEnum.AT_STOP &&
390
+ thisStopSequence &&
391
+ tripStopTimes[thisStopSequence - 1].stop_id) ||
392
+ undefined,
393
+ this_stop_name: (statePosition === const_1.StatePositionEnum.AT_STOP &&
394
+ thisStopSequence &&
395
+ tripStopTimes[thisStopSequence - 1].stop.stop_name) ||
396
+ undefined,
397
+ this_stop_sequence: (statePosition === const_1.StatePositionEnum.AT_STOP && thisStopSequence) || undefined,
398
+ last_stop_headsign: tripStopTimes[lastStopSequence - 1].stop_headsign || undefined,
399
+ state_position: statePosition,
400
+ state_process: const_1.StateProcessEnum.PROCESSED,
401
+ valid_to: this.validToCalculator.getDefaultValidToAttribute(currentPosition.properties.origin_timestamp),
402
+ is_tracked: statePosition !== const_1.StatePositionEnum.AFTER_TRACK && statePosition !== const_1.StatePositionEnum.MISMATCHED,
403
+ };
404
+ return estimatedPoint;
405
+ };
406
+ /**
407
+ * Corrects time delay at stop with dwelling time
408
+ *
409
+ * @param {number} timeDelay - Initial computed delay in seconds, can be negative for trip ahead
410
+ * @param {IVPTripsLastPositionContext | null} context - Context state, holds information about previous positions
411
+ * @param {Feature<Point, ICurrentPositionProperties>} currentPosition - Feature Point of current position
412
+ * @param {IShapeAnchorPoint} thisClosestPoint - Closest point of shape anchors
413
+ * @param { departureTime: number; arrivalTime: number } stopTimes - departure and arrival stop times in seconds
414
+ * @returns {number} - Result delay in seconds, can be negative for trip ahead
415
+ */
416
+ this.getCorrectedTimeDelay = (timeDelay, context, currentPosition, thisStopSequence, stopTimes) => {
417
+ // compute dwell time in stop, most common is zero
418
+ const stopDwellTimeSeconds = stopTimes.departureTime - stopTimes.arrivalTime;
419
+ // if dwell time is sheduled as zero, return initial computed delay
420
+ if (stopDwellTimeSeconds <= 0) {
421
+ return timeDelay;
422
+ }
423
+ // if last position was not in this same stop or there is no last position at all
424
+ if (!context || context.atStopStreak.stop_sequence !== thisStopSequence) {
425
+ // timeDelay >= 0 trip is DELAYED
426
+ // we presume it will lower delay by shortening its scheduled dwell time,
427
+ // cant go under zero of course, trip should not go ahead
428
+ // else trip is AHeAD
429
+ // left computed delay as it was
430
+ return timeDelay >= 0 ? Math.max(timeDelay - stopDwellTimeSeconds, 0) : timeDelay;
431
+ }
432
+ // we presume that first position at same stop is real arrival time
433
+ if (context.atStopStreak.firstPositionDelay >= 0) {
434
+ // trip was DELAYED before
435
+ // we presume it will lower delay by shortening its scheduled dwell time,
436
+ // cant go under zero of course, trip should not go ahead
437
+ return Math.max(timeDelay - stopDwellTimeSeconds, 0);
438
+ }
439
+ // trip was AHEAD before
440
+ // real dwell time so far
441
+ const realDwellTimeSeconds = Math.round((currentPosition.properties.origin_timestamp.getTime() - context.atStopStreak.firstPositionTimestamp) / 1000);
442
+ // if real dwell is longer than scheduled, then add to negative delay time
443
+ return context.atStopStreak.firstPositionDelay + Math.max(realDwellTimeSeconds - stopDwellTimeSeconds, 0);
444
+ };
445
+ /**
446
+ * Compute UTC timestamp of start of day when trip starts
447
+ *
448
+ * @param {number} startTimestamp - Unix timestamp of start of the trip
449
+ * @param {number} firstStopTimeScheduledSeconds - Number of seconds from midnight of first stop departure
450
+ * @returns {number} - Returns unix timestamp in milliseconds.
451
+ */
452
+ this.getStartDayTimestamp = (startTimestamp, firstStopTimeScheduledSeconds) => {
453
+ let startDayTimestamp = moment_timezone_1.default.utc(startTimestamp).tz("Europe/Prague").startOf("day");
454
+ const stopTimeDayOverflow = Math.floor(firstStopTimeScheduledSeconds / ONE_DAY_IN_SECONDS);
455
+ // if trip has 24+ stop times set real startDay to yesterday
456
+ if (stopTimeDayOverflow > 0) {
457
+ startDayTimestamp.subtract(1, "day");
458
+ }
459
+ return startDayTimestamp.valueOf();
460
+ };
461
+ }
52
462
  /**
53
463
  * Get delay in seconds for positions before track to ensure correct delay propagation
54
464
  *
@@ -60,7 +470,7 @@ class PositionsManager {
60
470
  * @param positionOriginUnixTimestamp The Unix Epoch timestamp of the current position, in milliseconds
61
471
  * @param startDayUnixTimestamp The Unix Epoch timestamp of the start of the service day, in milliseconds
62
472
  */
63
- static getDelayBeforeTrack(lastPositionDelay, firstStopDepartureTime, positionOriginUnixTimestamp, startDayUnixTimestamp) {
473
+ getDelayBeforeTrack(lastPositionDelay, firstStopDepartureTime, positionOriginUnixTimestamp, startDayUnixTimestamp) {
64
474
  if (typeof lastPositionDelay !== "number") {
65
475
  return null;
66
476
  }
@@ -72,8 +482,8 @@ class PositionsManager {
72
482
  const positionOriginTimeInSeconds = Math.floor(positionOriginUnixTimestamp / ONE_SECOND_IN_MILLIS);
73
483
  return Math.max(lastPositionDelay, positionOriginTimeInSeconds - departureTimeInSeconds);
74
484
  }
75
- static getStateAndStopSequences(gtfsRouteType, tripStopTimes, thisClosestPoint, positionProperties, context) {
76
- var _b;
485
+ getStateAndStopSequences(gtfsRouteType, tripStopTimes, thisClosestPoint, positionProperties, context) {
486
+ var _a;
77
487
  let statePosition = thisClosestPoint.this_stop_sequence ? const_1.StatePositionEnum.AT_STOP : const_1.StatePositionEnum.ON_TRACK;
78
488
  let thisStopSequence = thisClosestPoint.this_stop_sequence;
79
489
  let lastStopSequence = thisClosestPoint.last_stop_sequence;
@@ -83,7 +493,7 @@ class PositionsManager {
83
493
  positionProperties.tcp_event === const_1.TCPEventEnum.TIME &&
84
494
  (context === null || context === void 0 ? void 0 : context.lastPositionTracking)) {
85
495
  const previousPositionTcpEvent = context.lastPositionTracking.properties.tcp_event;
86
- const previousLastStopSequence = (_b = context.lastPositionTracking.properties.last_stop_sequence) !== null && _b !== void 0 ? _b : 0;
496
+ const previousLastStopSequence = (_a = context.lastPositionTracking.properties.last_stop_sequence) !== null && _a !== void 0 ? _a : 0;
87
497
  // if previous position was 'P' and current last stop sequence is lower than the previous last stop sequence
88
498
  // or if previous position was 'O', current position is at stop
89
499
  // and current last stop sequence is lower than the previous last stop sequence
@@ -113,399 +523,14 @@ class PositionsManager {
113
523
  nextStopSequence,
114
524
  };
115
525
  }
116
- static isAfterTrack(lastStopSequence, lastTripShapePoint) {
526
+ isAfterTrack(lastStopSequence, lastTripShapePoint) {
117
527
  // Trip is terminated OR the vehicle is close to/in the terminus
118
528
  return lastStopSequence >= lastTripShapePoint.last_stop_sequence;
119
529
  }
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: Math.round(thisClosestPoint.shape_dist_traveled * 1000) / 1000,
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();
509
530
  };
510
- exports.default = PositionsManager;
531
+ exports.PositionsManager = PositionsManager = __decorate([
532
+ (0, tsyringe_1.injectable)(),
533
+ __param(0, (0, tsyringe_1.inject)(VPContainerToken_1.VPContainerToken.ValidToCalculator)),
534
+ __metadata("design:paramtypes", [ValidToCalculator_1.ValidToCalculator])
535
+ ], PositionsManager);
511
536
  //# sourceMappingURL=PositionsManager.js.map