incyclist-services 1.5.3 → 1.5.5

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.
@@ -129,9 +129,11 @@ let DeviceRideService = (() => {
129
129
  return this.rideAdapters;
130
130
  this.rideAdapters = this.adapters = this.getConfiguredAdapters(true);
131
131
  const controlDevice = (_b = (_a = this.adapters.find(d => d.capabilities.includes('control'))) !== null && _a !== void 0 ? _a : this.adapters.find(d => d.capabilities.includes('power'))) !== null && _b !== void 0 ? _b : this.adapters.find(d => d.capabilities.includes('speed'));
132
- this.rideAdapters.forEach(ai => {
133
- ai.isControl = (ai.udid === controlDevice.udid);
134
- });
132
+ if (controlDevice) {
133
+ this.rideAdapters.forEach(ai => {
134
+ ai.isControl = (ai.udid === controlDevice.udid);
135
+ });
136
+ }
135
137
  if (!this.simulatorEnforced)
136
138
  return this.adapters;
137
139
  const adapter = incyclist_devices_1.AdapterFactory.create({ interface: 'simulator', name: 'Simulator' });
@@ -130,7 +130,7 @@ let RideDisplayService = (() => {
130
130
  this.logEvent({ message: 'overlay shown', overlay: 'start overlay' });
131
131
  }
132
132
  catch (err) {
133
- this.logError(err, 'stop');
133
+ this.logError(err, 'start');
134
134
  }
135
135
  }
136
136
  startWithMissingSensors() {
@@ -409,7 +409,7 @@ let RideDisplayService = (() => {
409
409
  this.state = 'Idle';
410
410
  }
411
411
  catch (err) {
412
- this.logError(err, 'stop');
412
+ this.logError(err, 'stopRide');
413
413
  }
414
414
  delete this.type;
415
415
  delete this.displayService;
@@ -164,9 +164,8 @@ let GpxDisplayService = (() => {
164
164
  const rideView = this.getUserSettings().get('preferences.rideView', 'sv');
165
165
  if (rideView === 'sv') {
166
166
  const updatePending = (Date.now() - ((_b = this.tsPrevSVUpdate) !== null && _b !== void 0 ? _b : 0)) > this.getDefaultUpdateFrequency();
167
- const stillBusy = (!this.tsPositionUpdateConfirmed && (Date.now() - ((_c = this.tsPrevSVUpdate) !== null && _c !== void 0 ? _c : 0)) < 500) || (Date.now() - this.tsLastSVEvent) < this.getMinimalPause();
167
+ const stillBusy = (!this.tsPositionUpdateConfirmed && (Date.now() - ((_c = this.tsPrevSVUpdate) !== null && _c !== void 0 ? _c : 0)) < 500) || (Date.now() - ((_d = this.tsLastSVEvent) !== null && _d !== void 0 ? _d : 0)) < this.getMinimalPause();
168
168
  const updatePossible = this.tsPositionUpdateConfirmed && (Date.now() - this.tsLastSVEvent) > this.getBestCaseUpdateFrequency();
169
- console.log('# service.onPositionUpdate', rideView, this.tsLastSVEvent - ((_d = this.tsPrevSVUpdate) !== null && _d !== void 0 ? _d : 0), Object.assign({ stillBusy, updatePossible, updatePending }, state));
170
169
  if ((!stillBusy || !this.tsPrevSVUpdate) && (updatePending || updatePossible)) {
171
170
  if (position) {
172
171
  const freq = this.tsPrevSVUpdate ? Date.now() - this.tsPrevSVUpdate : undefined;
@@ -180,6 +179,17 @@ let GpxDisplayService = (() => {
180
179
  delete this.tsLastSVEvent;
181
180
  }
182
181
  }
182
+ else {
183
+ const duration = this.tsPrevSVUpdate ? Date.now() - this.tsPrevSVUpdate : undefined;
184
+ if (stillBusy) {
185
+ this.logEvent({ message: 'street view position update skipped', reason: 'busy', duration });
186
+ if (duration > 10000) {
187
+ delete this.tsPrevSVUpdate;
188
+ delete this.tsPositionUpdateConfirmed;
189
+ delete this.tsLastSVEvent;
190
+ }
191
+ }
192
+ }
183
193
  }
184
194
  else {
185
195
  console.log('# position update', this.position);
@@ -58,8 +58,14 @@ class IncyclistXMLParser extends xml_1.XMLParser {
58
58
  try {
59
59
  if (context.data['autoCorrect']) {
60
60
  const gpx = yield new gpx_1.GPXParser({ addTime: true, keepZero: true }).import(gpxFile);
61
- route.points = gpx.details.points;
62
- (0, points_1.fixAnomalies)(route.points);
61
+ const points = [...gpx.details.points];
62
+ const { fixed, time, error } = (0, points_1.fixAnomalies)(points);
63
+ this.logger.logEvent({ message: 'gpx file autocorrect', cntFixed: fixed, duration: time, error: error === null || error === void 0 ? void 0 : error.message });
64
+ if (error) {
65
+ route.points = gpx.details.points;
66
+ }
67
+ route.points = route.points.filter(p => p.distance !== 0);
68
+ route.points.forEach((p, idx) => { p.cnt = idx; });
63
69
  (0, utils_2.validateRoute)(route);
64
70
  }
65
71
  else {
@@ -12,6 +12,7 @@ interface Anomaly {
12
12
  retry?: number;
13
13
  }
14
14
  export declare const calculateSpeed: (points: RoutePoint[], idx: number) => number;
15
+ export declare const getPointsWithSpeed: (points: RoutePoint[]) => RoutePointEval[];
15
16
  export declare const detectAnomalies: (points: RoutePoint[], maxCount?: number) => Anomaly[];
16
17
  export declare const fixAnomaly: (anomaly: Anomaly, points: RoutePoint[]) => void;
17
18
  export declare const fixAnomalies: (points: RoutePoint[], maxAttempts?: number) => {
@@ -1,7 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fixAnomalies = exports.fixAnomaly = exports.detectAnomalies = exports.calculateSpeed = void 0;
4
- const MAX_LOOKUP = 50;
3
+ exports.fixAnomalies = exports.fixAnomaly = exports.detectAnomalies = exports.getPointsWithSpeed = exports.calculateSpeed = void 0;
4
+ const MAX_LOOKUP = 100;
5
+ const isJump = (v1, v2) => {
6
+ if (v2 === v1 || v1 < 0.2 || v2 < 0.2)
7
+ return false;
8
+ return (Math.abs(v2 - v1) > 3) ||
9
+ (v2 > v1 * 1.4 && Math.abs(v2 - v1) > 2);
10
+ };
5
11
  const calculateSpeed = (points, idx) => {
6
12
  const p = points === null || points === void 0 ? void 0 : points[idx];
7
13
  if (!p || idx === 0)
@@ -11,51 +17,73 @@ const calculateSpeed = (points, idx) => {
11
17
  return speed;
12
18
  };
13
19
  exports.calculateSpeed = calculateSpeed;
14
- const detectAnomalies = (points, maxCount) => {
15
- if (points[0].time === undefined)
16
- return [];
17
- const data = points.map((p, idx) => {
20
+ const getPointsWithSpeed = (points) => {
21
+ return points.map((p, idx) => {
18
22
  const d = Object.assign({}, p);
19
23
  d.speed = (0, exports.calculateSpeed)(points, idx);
20
24
  return d;
21
25
  });
26
+ };
27
+ exports.getPointsWithSpeed = getPointsWithSpeed;
28
+ const detectAnomalies = (points, maxCount) => {
29
+ if (points[0].time === undefined)
30
+ return [];
31
+ const data = (0, exports.getPointsWithSpeed)(points);
22
32
  const anomalies = [];
23
33
  let lastValidSpeedIdx = 0;
24
34
  let done = false;
25
35
  const isSpeedJump = (p1, p2) => {
26
- return p2.speed && p1.speed &&
27
- p2.speed > p1.speed * 1.5 && p2.speed > 2 ||
28
- Math.abs(p2.speed - p1.speed) > 3;
36
+ return isJump(p1.speed, p2.speed);
29
37
  };
30
- data.forEach((point, idx) => {
31
- var _a;
32
- if (idx === 0 || done)
38
+ const isCloseToNull = (p) => {
39
+ return (p.distance < 0.5 || p.speed < 0.5);
40
+ };
41
+ const onNullValueDetected = (point, idx) => {
42
+ const speedPrev = data[idx - 1].speed;
43
+ if (speedPrev < 2)
33
44
  return;
34
- if (point.speed === 0 || point.distance === 0) {
35
- let nextJumpIdx;
36
- const idxPrev = lastValidSpeedIdx === 0 ? idx - 1 : lastValidSpeedIdx;
37
- for (let j = idx + 1; j < idx + MAX_LOOKUP && nextJumpIdx === undefined; j++) {
38
- if (isSpeedJump(data[idxPrev], data[j])) {
39
- nextJumpIdx = j;
40
- }
45
+ const speedNext = data[idx + 1].speed;
46
+ if (speedPrev > speedNext && !isJump(speedPrev / 2, speedNext)) {
47
+ anomalies.push({ idx: idx - 1, type: 'speedJump', point, nextNullIdx: idx });
48
+ }
49
+ let nextJumpIdx;
50
+ const idxPrev = lastValidSpeedIdx === 0 ? idx - 1 : lastValidSpeedIdx;
51
+ for (let j = idx + 1; j < idx + MAX_LOOKUP && nextJumpIdx === undefined; j++) {
52
+ if (isSpeedJump(data[idxPrev], data[j])) {
53
+ nextJumpIdx = j;
41
54
  }
42
- anomalies.push({ idx, type: 'noDistance', point, nextJumpIdx });
43
55
  }
56
+ anomalies.push({ idx, type: 'noDistance', point, nextJumpIdx });
57
+ };
58
+ const onSpeedJumpDetected = (point, idx) => {
44
59
  const prevPoint = data[lastValidSpeedIdx];
45
- const speedJump = isSpeedJump(prevPoint, point);
46
- if (speedJump) {
47
- if (prevPoint.speed === 0) {
48
- anomalies.push({ idx: lastValidSpeedIdx, type: 'noDistance', point });
49
- }
50
- else {
51
- let nextNullIdx;
52
- for (let j = idx + 1; j < idx + MAX_LOOKUP && nextNullIdx === undefined; j++) {
53
- if (data[j].distance < 0.5 || data[j].speed < 0.5) {
54
- nextNullIdx = j;
55
- }
60
+ let nextJumpIdx, nextNullIdx;
61
+ if (prevPoint.speed === 0) {
62
+ anomalies.push({ idx: lastValidSpeedIdx, type: 'noDistance', point });
63
+ }
64
+ else {
65
+ let nextJumpFound = false;
66
+ for (let j = idx + 1; j < idx + MAX_LOOKUP && !nextJumpFound && nextNullIdx === undefined; j++) {
67
+ if (isCloseToNull(data[j])) {
68
+ nextNullIdx = j;
69
+ }
70
+ else if (j > idx + 1 && isSpeedJump(data[j], data[j - 1])) {
71
+ nextJumpFound = true;
72
+ continue;
56
73
  }
57
- anomalies.push({ idx, type: 'speedJump', point, nextNullIdx });
58
74
  }
75
+ anomalies.push({ idx, type: 'speedJump', point, nextNullIdx, nextJumpIdx });
76
+ }
77
+ };
78
+ data.forEach((point, idx) => {
79
+ var _a;
80
+ if (idx === 0 || done)
81
+ return;
82
+ if (point.speed === 0 || point.distance === 0) {
83
+ onNullValueDetected(point, idx);
84
+ }
85
+ else if (isSpeedJump(data[lastValidSpeedIdx], point)) {
86
+ onSpeedJumpDetected(point, idx);
59
87
  }
60
88
  if (!!maxCount && anomalies.length > maxCount - 1) {
61
89
  done = true;
@@ -70,119 +98,10 @@ const detectAnomalies = (points, maxCount) => {
70
98
  exports.detectAnomalies = detectAnomalies;
71
99
  const fixAnomaly = (anomaly, points) => {
72
100
  if (anomaly.type === 'speedJump') {
73
- if (anomaly.nextNullIdx !== undefined && anomaly.retry > 0) {
74
- const idx = anomaly.idx;
75
- const idxNull = anomaly.nextNullIdx;
76
- let offset;
77
- if (idxNull === idx + 1) {
78
- offset = ((0, exports.calculateSpeed)(points, idx) - (0, exports.calculateSpeed)(points, idxNull)) / 2;
79
- const t = points[idx].time - points[idx - 1].time;
80
- const vPrev = (0, exports.calculateSpeed)(points, idx);
81
- const v = vPrev - offset;
82
- const d = v * t;
83
- const dOffset = points[idx].distance - d;
84
- points[idx].distance = d;
85
- points[idx].routeDistance -= dOffset;
86
- const t0 = points[idxNull].time - points[idxNull - 1].time;
87
- const v0 = offset;
88
- points[idxNull].distance += v0 * t0;
89
- }
90
- else {
91
- const offsetEnd = (0, exports.calculateSpeed)(points, idxNull - 1) - (0, exports.calculateSpeed)(points, idx - 1);
92
- const offsetStart = (0, exports.calculateSpeed)(points, idx) - (0, exports.calculateSpeed)(points, idx - 1);
93
- const offsetAverage = (offsetStart + offsetEnd) / 2;
94
- }
95
- }
96
- if (anomaly.nextNullIdx !== undefined) {
97
- const idx = anomaly.idx;
98
- const idxNull = anomaly.nextNullIdx;
99
- const offsetNext = idxNull === idx + 1 ? (0, exports.calculateSpeed)(points, idx) / 2 : (0, exports.calculateSpeed)(points, idxNull - 1);
100
- const offsetPrev = idxNull === idx + 1 ? offsetNext : (0, exports.calculateSpeed)(points, idx) - (0, exports.calculateSpeed)(points, idx - 1);
101
- const offset = (offsetNext + offsetPrev) / 2;
102
- const t = points[idx].time - points[idx - 1].time;
103
- const vPrev = (0, exports.calculateSpeed)(points, idx);
104
- const v = vPrev - offset;
105
- const d = v * t;
106
- const dOffset = points[idx].distance - d;
107
- points[idx].distance = d;
108
- points[idx].routeDistance -= dOffset;
109
- for (let j = idx + 1; j < idxNull; j++) {
110
- points[j].routeDistance -= dOffset;
111
- }
112
- const t0 = points[idxNull].time - points[idxNull - 1].time;
113
- const v0 = offset;
114
- points[idxNull].distance = v0 * t0;
115
- }
116
- else {
117
- const idx = anomaly.idx;
118
- const next = points === null || points === void 0 ? void 0 : points[idx + 1];
119
- const prev = points === null || points === void 0 ? void 0 : points[idx - 1];
120
- if (next && prev) {
121
- next.distance = next.routeDistance - prev.routeDistance;
122
- }
123
- points.splice(idx, 1);
124
- }
101
+ fixSpeedJump(anomaly, points);
125
102
  }
126
103
  else if (anomaly.type === 'noDistance') {
127
- const idx = anomaly.idx;
128
- const next = points === null || points === void 0 ? void 0 : points[idx + 1];
129
- const prev = points === null || points === void 0 ? void 0 : points[idx - 1];
130
- if (anomaly.nextJumpIdx !== undefined) {
131
- const endIdx = anomaly.nextJumpIdx;
132
- const vEnd = (0, exports.calculateSpeed)(points, endIdx);
133
- const vStart = (0, exports.calculateSpeed)(points, endIdx - 1);
134
- if (endIdx === idx + 1) {
135
- const offset = vEnd / 2;
136
- const t0 = points[idx].time - points[idx - 1].time;
137
- const v0 = offset;
138
- const d0 = v0 * t0;
139
- points[idx].distance = d0;
140
- points[idx].routeDistance += d0;
141
- const t = points[endIdx].time - points[endIdx - 1].time;
142
- const vPrev = (0, exports.calculateSpeed)(points, endIdx);
143
- const v = vPrev - offset;
144
- const d = v * t;
145
- points[endIdx].distance = d;
146
- }
147
- else {
148
- const offset = vEnd - vStart;
149
- for (let j = anomaly.idx; j < endIdx; j++) {
150
- const t = points[j].time - points[j - 1].time;
151
- const vPrev = (0, exports.calculateSpeed)(points, j);
152
- const v = vPrev + offset;
153
- const d = v * t;
154
- const dOffset = points[j].distance - d;
155
- points[j].distance = d;
156
- points[j].routeDistance += dOffset;
157
- }
158
- const t = points[endIdx].time - points[endIdx - 1].time;
159
- const vPrev = (0, exports.calculateSpeed)(points, endIdx);
160
- const v = vPrev - offset;
161
- const d = v * t;
162
- points[endIdx].distance = d;
163
- }
164
- }
165
- else {
166
- if (idx > 2 && idx < points.length - 2) {
167
- const s = points[idx + 2].routeDistance - points[idx - 2].routeDistance;
168
- const t = points[idx + 2].time - points[idx - 2].time;
169
- const avgSpeed = s / t;
170
- const nextSpeed = (0, exports.calculateSpeed)(points, idx + 1);
171
- const prevSpeed = (0, exports.calculateSpeed)(points, idx - 1);
172
- if ((Math.abs(prevSpeed - avgSpeed) - Math.abs(nextSpeed - avgSpeed)) > 0.5) {
173
- points[idx].distance = points[idx].routeDistance - points[idx - 2].routeDistance;
174
- points.splice(idx - 1, 1);
175
- }
176
- else {
177
- next.distance = next.routeDistance - prev.routeDistance;
178
- points.splice(idx, 1);
179
- }
180
- }
181
- else {
182
- next.distance = next.routeDistance - prev.routeDistance;
183
- points.splice(idx, 1);
184
- }
185
- }
104
+ fixNoDistance(anomaly, points);
186
105
  }
187
106
  };
188
107
  exports.fixAnomaly = fixAnomaly;
@@ -194,19 +113,23 @@ const fixAnomalies = (points, maxAttempts = 1000) => {
194
113
  let prev;
195
114
  let stuck = false;
196
115
  let error;
116
+ let retry = 0;
197
117
  const fixedRecords = [];
198
118
  try {
199
119
  do {
200
120
  anomalies = (0, exports.detectAnomalies)(points, 1);
201
121
  if (anomalies.length > 0) {
202
122
  const current = anomalies[0];
203
- if ((prev === null || prev === void 0 ? void 0 : prev.idx) === current.idx) {
123
+ if ((prev === null || prev === void 0 ? void 0 : prev.idx) === current.idx && ++retry > 5) {
204
124
  stuck = true;
205
125
  continue;
206
126
  }
207
- else if ((prev === null || prev === void 0 ? void 0 : prev.type) === current.type && (prev === null || prev === void 0 ? void 0 : prev.type) === 'speedJump' && current.idx > (prev === null || prev === void 0 ? void 0 : prev.idx) && current.idx < (prev === null || prev === void 0 ? void 0 : prev.nextNullIdx)) {
208
- current.nextNullIdx = prev.nextNullIdx;
209
- current.retry = ((_a = prev.retry) !== null && _a !== void 0 ? _a : 0) + 1;
127
+ else {
128
+ retry = 0;
129
+ if ((prev === null || prev === void 0 ? void 0 : prev.type) === current.type && (prev === null || prev === void 0 ? void 0 : prev.type) === 'speedJump' && current.idx > (prev === null || prev === void 0 ? void 0 : prev.idx) && current.idx < (prev === null || prev === void 0 ? void 0 : prev.nextNullIdx)) {
130
+ current.nextNullIdx = prev.nextNullIdx;
131
+ current.retry = ((_a = prev.retry) !== null && _a !== void 0 ? _a : 0) + 1;
132
+ }
210
133
  }
211
134
  (0, exports.fixAnomaly)(anomalies[0], points);
212
135
  fixedRecords.push(anomalies[0]);
@@ -223,5 +146,161 @@ const fixAnomalies = (points, maxAttempts = 1000) => {
223
146
  return { fixed, time: tsStop - tsStart, fixedRecords, error };
224
147
  };
225
148
  exports.fixAnomalies = fixAnomalies;
226
- const detectCuts = (points) => {
149
+ const fixNoDistance = (anomaly, points) => {
150
+ const idx = anomaly.idx;
151
+ const next = points === null || points === void 0 ? void 0 : points[idx + 1];
152
+ const prev = points === null || points === void 0 ? void 0 : points[idx - 1];
153
+ const fixNullWithoutJump = () => {
154
+ if (idx > 2 && idx < points.length - 2) {
155
+ const s = points[idx + 2].routeDistance - points[idx - 2].routeDistance;
156
+ const t = points[idx + 2].time - points[idx - 2].time;
157
+ const avgSpeed = s / t;
158
+ const nextSpeed = (0, exports.calculateSpeed)(points, idx + 1);
159
+ const prevSpeed = (0, exports.calculateSpeed)(points, idx - 1);
160
+ if ((Math.abs(prevSpeed - avgSpeed) - Math.abs(nextSpeed - avgSpeed)) > 0.5) {
161
+ points[idx].distance = points[idx].routeDistance - points[idx - 2].routeDistance;
162
+ points.splice(idx - 1, 1);
163
+ }
164
+ else {
165
+ next.distance = next.routeDistance - prev.routeDistance;
166
+ points.splice(idx, 1);
167
+ }
168
+ }
169
+ else {
170
+ next.distance = next.routeDistance - prev.routeDistance;
171
+ points.splice(idx, 1);
172
+ }
173
+ };
174
+ const fixNullFollowedByJump = () => {
175
+ const endIdx = anomaly.nextJumpIdx;
176
+ const vEnd = (0, exports.calculateSpeed)(points, endIdx);
177
+ const vStart = (0, exports.calculateSpeed)(points, endIdx - 1);
178
+ if (endIdx === idx + 1) {
179
+ const offset = vEnd / 2;
180
+ const t0 = points[idx].time - points[idx - 1].time;
181
+ const v0 = offset;
182
+ const d0 = v0 * t0;
183
+ points[idx].distance = d0;
184
+ points[idx].routeDistance += d0;
185
+ const t = points[endIdx].time - points[endIdx - 1].time;
186
+ const vPrev = (0, exports.calculateSpeed)(points, endIdx);
187
+ const v = vPrev - offset;
188
+ const d = v * t;
189
+ points[endIdx].distance = d;
190
+ }
191
+ else {
192
+ const offset = vEnd - vStart;
193
+ for (let j = anomaly.idx; j < endIdx; j++) {
194
+ const t = points[j].time - points[j - 1].time;
195
+ const vPrev = (0, exports.calculateSpeed)(points, j);
196
+ const v = vPrev + offset;
197
+ const d = v * t;
198
+ const dOffset = points[j].distance - d;
199
+ points[j].distance = d;
200
+ points[j].routeDistance += dOffset;
201
+ }
202
+ const t = points[endIdx].time - points[endIdx - 1].time;
203
+ const vPrev = (0, exports.calculateSpeed)(points, endIdx);
204
+ const v = vPrev - offset;
205
+ const d = v * t;
206
+ points[endIdx].distance = d;
207
+ }
208
+ };
209
+ if (anomaly.nextJumpIdx !== undefined) {
210
+ fixNullFollowedByJump();
211
+ }
212
+ else {
213
+ fixNullWithoutJump();
214
+ }
215
+ };
216
+ const fixSpeedJump = (anomaly, points) => {
217
+ const fixJumpFollowedByNull = () => {
218
+ const idx = anomaly.idx;
219
+ const idxNull = anomaly.nextNullIdx;
220
+ const vIssue = (0, exports.calculateSpeed)(points, idx);
221
+ let offset;
222
+ if (idx + 1 === idxNull) {
223
+ offset = vIssue / 2;
224
+ }
225
+ else {
226
+ const vEnd = (0, exports.calculateSpeed)(points, idxNull - 1);
227
+ const vStart = (0, exports.calculateSpeed)(points, idx - 1);
228
+ let vTarget;
229
+ const v1 = vIssue - vStart;
230
+ const v2 = vIssue - v1;
231
+ if (vStart > vEnd) {
232
+ vTarget = Math.max(v1, v2);
233
+ }
234
+ else {
235
+ vTarget = isJump(v1, v2) ? (v1 + v2) / 2 : Math.min(v1, v2);
236
+ }
237
+ offset = vIssue - vTarget;
238
+ }
239
+ let t = points[idx].time - points[idx - 1].time;
240
+ let v = vIssue - offset;
241
+ let s = v * t;
242
+ let dOffset = points[idx].distance - s;
243
+ points[idx].distance = s;
244
+ points[idx].routeDistance -= dOffset;
245
+ v = offset;
246
+ for (let j = idx + 1; j < idxNull; j++) {
247
+ t = points[j].time - points[j - 1].time;
248
+ s = v * t;
249
+ v = offset = (0, exports.calculateSpeed)(points, j);
250
+ dOffset = points[j].distance - s;
251
+ points[j].distance = s;
252
+ points[j].routeDistance -= dOffset;
253
+ }
254
+ t = points[idxNull].time - points[idxNull - 1].time;
255
+ v = offset;
256
+ points[idxNull].distance = v * t;
257
+ };
258
+ const fixJumpFollowedByNullRetry = () => {
259
+ const idx = anomaly.idx;
260
+ const idxNull = anomaly.nextNullIdx;
261
+ let offset;
262
+ if (idxNull === idx + 1) {
263
+ offset = ((0, exports.calculateSpeed)(points, idx) - (0, exports.calculateSpeed)(points, idxNull)) / 2;
264
+ const t = points[idx].time - points[idx - 1].time;
265
+ const vPrev = (0, exports.calculateSpeed)(points, idx);
266
+ const v = vPrev - offset;
267
+ const d = v * t;
268
+ const dOffset = points[idx].distance - d;
269
+ points[idx].distance = d;
270
+ points[idx].routeDistance -= dOffset;
271
+ const t0 = points[idxNull].time - points[idxNull - 1].time;
272
+ const v0 = offset;
273
+ points[idxNull].distance += v0 * t0;
274
+ }
275
+ };
276
+ const fixJumpFollowedByJump = () => {
277
+ const idx = anomaly.idx;
278
+ const current = points === null || points === void 0 ? void 0 : points[idx];
279
+ const next = points === null || points === void 0 ? void 0 : points[idx + 1];
280
+ const v = ((0, exports.calculateSpeed)(points, idx) + (0, exports.calculateSpeed)(points, idx + 1)) / 2;
281
+ const t = points[idx].time - points[idx - 1].time;
282
+ const d = v * t;
283
+ const offset = current.distance - d;
284
+ current.distance = d;
285
+ current.routeDistance -= offset;
286
+ next.distance += offset;
287
+ };
288
+ if (anomaly.nextNullIdx !== undefined && anomaly.retry > 0) {
289
+ fixJumpFollowedByNullRetry();
290
+ }
291
+ else if (anomaly.nextNullIdx !== undefined) {
292
+ fixJumpFollowedByNull();
293
+ }
294
+ else if (anomaly.nextJumpIdx !== undefined) {
295
+ fixJumpFollowedByJump();
296
+ }
297
+ else {
298
+ const idx = anomaly.idx;
299
+ const next = points === null || points === void 0 ? void 0 : points[idx + 1];
300
+ const prev = points === null || points === void 0 ? void 0 : points[idx - 1];
301
+ if (next && prev) {
302
+ next.distance = next.routeDistance - prev.routeDistance;
303
+ }
304
+ points.splice(idx, 1);
305
+ }
227
306
  };
@@ -37,4 +37,5 @@ export declare class WorkoutCard extends BaseCard implements Card<Workout> {
37
37
  protected getRepo(): WorkoutsDbLoader;
38
38
  protected emitUpdate(): void;
39
39
  protected isSelected(): boolean;
40
+ protected getRouteList(): import("../../../routes").RouteListService;
40
41
  }
@@ -1,4 +1,38 @@
1
1
  "use strict";
2
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
3
+ var useValue = arguments.length > 2;
4
+ for (var i = 0; i < initializers.length; i++) {
5
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
6
+ }
7
+ return useValue ? value : void 0;
8
+ };
9
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
10
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
11
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
12
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
13
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
14
+ var _, done = false;
15
+ for (var i = decorators.length - 1; i >= 0; i--) {
16
+ var context = {};
17
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
18
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
19
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
20
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
21
+ if (kind === "accessor") {
22
+ if (result === void 0) continue;
23
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
24
+ if (_ = accept(result.get)) descriptor.get = _;
25
+ if (_ = accept(result.set)) descriptor.set = _;
26
+ if (_ = accept(result.init)) initializers.unshift(_);
27
+ }
28
+ else if (_ = accept(result)) {
29
+ if (kind === "field") initializers.unshift(_);
30
+ else descriptor[key] = _;
31
+ }
32
+ }
33
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
34
+ done = true;
35
+ };
2
36
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
37
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
38
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -18,200 +52,223 @@ const settings_1 = require("../../../settings");
18
52
  const service_1 = require("../service");
19
53
  const utils_1 = require("../../../utils");
20
54
  const valid_1 = require("../../../utils/valid");
21
- class WorkoutCard extends base_1.BaseCard {
22
- constructor(workout, props) {
23
- super();
24
- this.cardObserver = new observer_1.Observer();
25
- const { list } = props || {};
26
- this.workout = workout;
27
- this.list = list;
28
- this.deleteable = true;
29
- this.initialized = false;
30
- this.logger = new gd_eventlog_1.EventLogger('WorkoutCard');
31
- }
32
- openSettings() {
33
- var _a, _b;
34
- let settings, canStart, duration, ftpRequired, categories = [], category, canStartWorkoutOnly;
35
- try {
36
- const service = (0, service_1.useWorkoutList)();
37
- canStart = service.canDisplayStart();
38
- canStartWorkoutOnly = !canStart;
39
- duration = this.calculateDuration();
40
- ftpRequired = (0, valid_1.valid)(this.workout.steps.find(s => {
41
- var _a;
42
- return (s.type == 'step' && ((_a = s.power) === null || _a === void 0 ? void 0 : _a.type) === 'pct of FTP') ||
43
- (s.type == 'segment' && (s === null || s === void 0 ? void 0 : s.steps.find(s => { var _a; return (s.type == 'step' && ((_a = s.power) === null || _a === void 0 ? void 0 : _a.type) === 'pct of FTP'); })));
44
- }));
45
- settings = (_a = service.getStartSettings()) !== null && _a !== void 0 ? _a : {};
46
- categories = ((_b = service.getLists()) !== null && _b !== void 0 ? _b : []).map(l => l.getTitle());
47
- category = this.list.getTitle();
48
- }
49
- catch (err) {
50
- this.logError(err, 'openSettings');
51
- }
52
- return { settings, ftpRequired, canStart, canStartWorkoutOnly, duration, categories, category };
53
- }
54
- select(settings) {
55
- const service = (0, service_1.getWorkoutList)();
56
- service.selectCard(this);
57
- if (settings)
58
- service.setStartSettings(settings);
59
- this.emitUpdate();
60
- }
61
- unselect() {
62
- const service = (0, service_1.getWorkoutList)();
63
- service.unselect();
64
- this.emitUpdate();
65
- }
66
- move(targetListName) {
67
- if (!(targetListName === null || targetListName === void 0 ? void 0 : targetListName.length))
68
- return;
69
- const service = (0, service_1.getWorkoutList)();
70
- const newList = service.moveCard(this, this.list, targetListName);
71
- if (newList) {
72
- this.list = newList;
73
- }
74
- this.workout.category = { name: targetListName, index: newList === null || newList === void 0 ? void 0 : newList.length };
75
- this.save(true);
76
- }
77
- save() {
78
- return __awaiter(this, arguments, void 0, function* (enforce = false) {
79
- try {
80
- return yield this.getRepo().save(this.workout, enforce);
55
+ const routes_1 = require("../../../routes");
56
+ const decorators_1 = require("../../../base/decorators");
57
+ let WorkoutCard = (() => {
58
+ var _a;
59
+ let _classSuper = base_1.BaseCard;
60
+ let _instanceExtraInitializers = [];
61
+ let _getRouteList_decorators;
62
+ return _a = class WorkoutCard extends _classSuper {
63
+ constructor(workout, props) {
64
+ super();
65
+ this.workout = __runInitializers(this, _instanceExtraInitializers);
66
+ this.cardObserver = new observer_1.Observer();
67
+ const { list } = props || {};
68
+ this.workout = workout;
69
+ this.list = list;
70
+ this.deleteable = true;
71
+ this.initialized = false;
72
+ this.logger = new gd_eventlog_1.EventLogger('WorkoutCard');
81
73
  }
82
- catch (err) {
83
- this.logError(err, 'save');
74
+ openSettings() {
75
+ var _b, _c;
76
+ let settings, canStart, duration, ftpRequired, categories = [], category, canStartWorkoutOnly;
77
+ try {
78
+ const service = (0, service_1.useWorkoutList)();
79
+ canStart = service.canDisplayStart();
80
+ canStartWorkoutOnly = !canStart;
81
+ duration = this.calculateDuration();
82
+ ftpRequired = (0, valid_1.valid)(this.workout.steps.find(s => {
83
+ var _b;
84
+ return (s.type == 'step' && ((_b = s.power) === null || _b === void 0 ? void 0 : _b.type) === 'pct of FTP') ||
85
+ (s.type == 'segment' && (s === null || s === void 0 ? void 0 : s.steps.find(s => { var _b; return (s.type == 'step' && ((_b = s.power) === null || _b === void 0 ? void 0 : _b.type) === 'pct of FTP'); })));
86
+ }));
87
+ settings = (_b = service.getStartSettings()) !== null && _b !== void 0 ? _b : {};
88
+ categories = ((_c = service.getLists()) !== null && _c !== void 0 ? _c : []).map(l => l.getTitle());
89
+ category = this.list.getTitle();
90
+ }
91
+ catch (err) {
92
+ this.logError(err, 'openSettings');
93
+ }
94
+ return { settings, ftpRequired, canStart, canStartWorkoutOnly, duration, categories, category };
84
95
  }
85
- });
86
- }
87
- delete() {
88
- try {
89
- const service = (0, service_1.getWorkoutList)();
90
- service.unselectCard(this);
91
- if (this.deleteObserver)
92
- return this.deleteObserver;
93
- this.deleteObserver = new observer_1.PromiseObserver(this._delete());
94
- return this.deleteObserver;
95
- }
96
- catch (err) {
97
- this.logError(err, 'delete');
98
- }
99
- }
100
- getId() {
101
- return this.workout.id;
102
- }
103
- getTitle() {
104
- return this.workout.name;
105
- }
106
- update(workout) {
107
- if (!(0, valid_1.valid)(workout))
108
- return;
109
- try {
110
- this.workout = workout;
111
- this.save();
112
- this.emitUpdate();
113
- }
114
- catch (err) {
115
- this.logError(err, 'update');
116
- }
117
- }
118
- getData() {
119
- return this.workout;
120
- }
121
- getCardType() {
122
- return "Workout";
123
- }
124
- getDisplayProperties() {
125
- const userSettings = (0, settings_1.useUserSettings)();
126
- let user;
127
- try {
128
- user = userSettings.get('user', {});
129
- }
130
- catch (_a) {
131
- user = {};
132
- }
133
- const duration = this.calculateDuration();
134
- return { title: this.workout.name, workout: this.workout, ftp: user === null || user === void 0 ? void 0 : user.ftp, duration,
135
- canDelete: this.deleteable, visible: this.visible, selected: this.isSelected(),
136
- observer: this.cardObserver };
137
- }
138
- enableDelete(enabled = true) {
139
- this.deleteable = enabled;
140
- }
141
- canDelete() {
142
- return this.deleteable;
143
- }
144
- setVisible(visible) {
145
- try {
146
- const prev = this.visible;
147
- this.visible = visible;
148
- if (visible !== prev)
96
+ select(settings) {
97
+ const service = (0, service_1.getWorkoutList)();
98
+ service.selectCard(this);
99
+ if (settings)
100
+ service.setStartSettings(settings);
101
+ if (settings.noRoute) {
102
+ this.getRouteList().unselect();
103
+ }
149
104
  this.emitUpdate();
150
- }
151
- catch (err) {
152
- this.logError(err, 'setVisible');
153
- }
154
- }
155
- _delete() {
156
- return __awaiter(this, void 0, void 0, function* () {
157
- yield (0, utils_1.waitNextTick)();
158
- let deleted = false;
159
- try {
160
- this.deleteObserver.emit('started');
161
- yield this.getRepo().delete(this.workout);
162
- this.deleteFromUIList();
163
- (0, service_1.getWorkoutList)().emitLists('updated');
164
- deleted = true;
165
- }
166
- catch (_a) {
167
- deleted = false;
168
- }
169
- finally {
170
- this.deleteObserver.emit('done', deleted);
171
- (0, utils_1.waitNextTick)().then(() => {
172
- delete this.deleteObserver;
105
+ }
106
+ unselect() {
107
+ const service = (0, service_1.getWorkoutList)();
108
+ service.unselect();
109
+ this.emitUpdate();
110
+ }
111
+ move(targetListName) {
112
+ if (!(targetListName === null || targetListName === void 0 ? void 0 : targetListName.length))
113
+ return;
114
+ const service = (0, service_1.getWorkoutList)();
115
+ const newList = service.moveCard(this, this.list, targetListName);
116
+ if (newList) {
117
+ this.list = newList;
118
+ }
119
+ this.workout.category = { name: targetListName, index: newList === null || newList === void 0 ? void 0 : newList.length };
120
+ this.save(true);
121
+ }
122
+ save() {
123
+ return __awaiter(this, arguments, void 0, function* (enforce = false) {
124
+ try {
125
+ return yield this.getRepo().save(this.workout, enforce);
126
+ }
127
+ catch (err) {
128
+ this.logError(err, 'save');
129
+ }
173
130
  });
174
131
  }
175
- return deleted;
176
- });
177
- }
178
- calculateDuration() {
179
- const { duration } = this.workout;
180
- if (duration < 120)
181
- return `${duration.toFixed(0)}s`;
182
- if (duration % 60 === 0)
183
- return `${duration / 60}min`;
184
- const secVal = duration % 60;
185
- const minVal = (duration - secVal) / 60 % 60;
186
- const h = Math.floor((duration - secVal - minVal * 60) / 3600);
187
- const sec = secVal < 10 ? `0${secVal}` : secVal;
188
- const min = minVal < 10 ? `0${minVal}` : minVal;
189
- if (h > 0)
190
- return `${h}:${min}:${sec}`;
191
- else
192
- return `${min}:${sec}`;
193
- }
194
- deleteFromUIList() {
195
- if (this.list) {
196
- this.list.remove(this);
197
- }
198
- }
199
- logError(err, fn) {
200
- this.logger.logEvent({ message: 'error', error: err.message, fn, stack: err.stack });
201
- }
202
- getRepo() {
203
- return new db_1.WorkoutsDbLoader();
204
- }
205
- emitUpdate() {
206
- if (this.cardObserver)
207
- this.cardObserver.emit('update', this.getDisplayProperties());
208
- }
209
- isSelected() {
210
- const service = (0, service_1.getWorkoutList)();
211
- const selectedWorkout = service.getSelected();
212
- if ((selectedWorkout === null || selectedWorkout === void 0 ? void 0 : selectedWorkout.id) === this.workout.id)
213
- return true;
214
- return false;
215
- }
216
- }
132
+ delete() {
133
+ try {
134
+ const service = (0, service_1.getWorkoutList)();
135
+ service.unselectCard(this);
136
+ if (this.deleteObserver)
137
+ return this.deleteObserver;
138
+ this.deleteObserver = new observer_1.PromiseObserver(this._delete());
139
+ return this.deleteObserver;
140
+ }
141
+ catch (err) {
142
+ this.logError(err, 'delete');
143
+ }
144
+ }
145
+ getId() {
146
+ return this.workout.id;
147
+ }
148
+ getTitle() {
149
+ return this.workout.name;
150
+ }
151
+ update(workout) {
152
+ if (!(0, valid_1.valid)(workout))
153
+ return;
154
+ try {
155
+ this.workout = workout;
156
+ this.save();
157
+ this.emitUpdate();
158
+ }
159
+ catch (err) {
160
+ this.logError(err, 'update');
161
+ }
162
+ }
163
+ getData() {
164
+ return this.workout;
165
+ }
166
+ getCardType() {
167
+ return "Workout";
168
+ }
169
+ getDisplayProperties() {
170
+ const userSettings = (0, settings_1.useUserSettings)();
171
+ let user;
172
+ try {
173
+ user = userSettings.get('user', {});
174
+ }
175
+ catch (_b) {
176
+ user = {};
177
+ }
178
+ const duration = this.calculateDuration();
179
+ return { title: this.workout.name, workout: this.workout, ftp: user === null || user === void 0 ? void 0 : user.ftp, duration,
180
+ canDelete: this.deleteable, visible: this.visible, selected: this.isSelected(),
181
+ observer: this.cardObserver };
182
+ }
183
+ enableDelete(enabled = true) {
184
+ this.deleteable = enabled;
185
+ }
186
+ canDelete() {
187
+ return this.deleteable;
188
+ }
189
+ setVisible(visible) {
190
+ try {
191
+ const prev = this.visible;
192
+ this.visible = visible;
193
+ if (visible !== prev)
194
+ this.emitUpdate();
195
+ }
196
+ catch (err) {
197
+ this.logError(err, 'setVisible');
198
+ }
199
+ }
200
+ _delete() {
201
+ return __awaiter(this, void 0, void 0, function* () {
202
+ yield (0, utils_1.waitNextTick)();
203
+ let deleted = false;
204
+ try {
205
+ this.deleteObserver.emit('started');
206
+ yield this.getRepo().delete(this.workout);
207
+ this.deleteFromUIList();
208
+ (0, service_1.getWorkoutList)().emitLists('updated');
209
+ deleted = true;
210
+ }
211
+ catch (_b) {
212
+ deleted = false;
213
+ }
214
+ finally {
215
+ this.deleteObserver.emit('done', deleted);
216
+ (0, utils_1.waitNextTick)().then(() => {
217
+ delete this.deleteObserver;
218
+ });
219
+ }
220
+ return deleted;
221
+ });
222
+ }
223
+ calculateDuration() {
224
+ const { duration } = this.workout;
225
+ if (duration < 120)
226
+ return `${duration.toFixed(0)}s`;
227
+ if (duration % 60 === 0)
228
+ return `${duration / 60}min`;
229
+ const secVal = duration % 60;
230
+ const minVal = (duration - secVal) / 60 % 60;
231
+ const h = Math.floor((duration - secVal - minVal * 60) / 3600);
232
+ const sec = secVal < 10 ? `0${secVal}` : secVal;
233
+ const min = minVal < 10 ? `0${minVal}` : minVal;
234
+ if (h > 0)
235
+ return `${h}:${min}:${sec}`;
236
+ else
237
+ return `${min}:${sec}`;
238
+ }
239
+ deleteFromUIList() {
240
+ if (this.list) {
241
+ this.list.remove(this);
242
+ }
243
+ }
244
+ logError(err, fn) {
245
+ this.logger.logEvent({ message: 'error', error: err.message, fn, stack: err.stack });
246
+ }
247
+ getRepo() {
248
+ return new db_1.WorkoutsDbLoader();
249
+ }
250
+ emitUpdate() {
251
+ if (this.cardObserver)
252
+ this.cardObserver.emit('update', this.getDisplayProperties());
253
+ }
254
+ isSelected() {
255
+ const service = (0, service_1.getWorkoutList)();
256
+ const selectedWorkout = service.getSelected();
257
+ if ((selectedWorkout === null || selectedWorkout === void 0 ? void 0 : selectedWorkout.id) === this.workout.id)
258
+ return true;
259
+ return false;
260
+ }
261
+ getRouteList() {
262
+ return (0, routes_1.useRouteList)();
263
+ }
264
+ },
265
+ (() => {
266
+ var _b;
267
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_b = _classSuper[Symbol.metadata]) !== null && _b !== void 0 ? _b : null) : void 0;
268
+ _getRouteList_decorators = [decorators_1.Injectable];
269
+ __esDecorate(_a, null, _getRouteList_decorators, { kind: "method", name: "getRouteList", static: false, private: false, access: { has: obj => "getRouteList" in obj, get: obj => obj.getRouteList }, metadata: _metadata }, null, _instanceExtraInitializers);
270
+ if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
271
+ })(),
272
+ _a;
273
+ })();
217
274
  exports.WorkoutCard = WorkoutCard;
@@ -27,6 +27,7 @@ export interface ActiveImportProps {
27
27
  export interface WorkoutSettings {
28
28
  ftp?: number;
29
29
  useErgMode?: boolean;
30
+ noRoute?: boolean;
30
31
  }
31
32
  export interface WorkoutSettingsDisplayProps {
32
33
  settings: WorkoutSettings;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incyclist-services",
3
- "version": "1.5.3",
3
+ "version": "1.5.5",
4
4
  "peerDependencies": {
5
5
  "gd-eventlog": "^0.1.26"
6
6
  },