@golemio/pid 2.13.10-dev.1342209321 → 2.14.0

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