incyclist-services 1.7.50 → 1.7.52
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.
- package/lib/cjs/activities/base/convert/fit/local-fit.js +37 -5
- package/lib/cjs/activities/base/convert/fit/remote-fit.js +2 -2
- package/lib/cjs/activities/base/convert/tcx/tcx.js +10 -1
- package/lib/cjs/activities/base/model/index.js +6 -1
- package/lib/cjs/activities/base/repo/db.js +1 -1
- package/lib/cjs/activities/base/utils/activity.js +2 -1
- package/lib/cjs/activities/base/utils/helpers.js +2 -1
- package/lib/cjs/activities/list/utils.js +1 -1
- package/lib/cjs/activities/ride/service.js +80 -42
- package/lib/cjs/api/rest/api.js +4 -1
- package/lib/cjs/api/rest/incyclist.js +1 -0
- package/lib/cjs/devices/ride/service.js +13 -2
- package/lib/cjs/ride/display/service.js +1 -1
- package/lib/cjs/ride/route/FreeRideDisplayService.js +35 -14
- package/lib/cjs/ride/route/RouteDisplayService.js +2 -2
- package/lib/cjs/routes/base/parsers/epm.js +6 -0
- package/lib/cjs/routes/base/parsers/factory.js +5 -0
- package/lib/cjs/routes/base/parsers/geometry.js +6 -0
- package/lib/cjs/routes/base/parsers/gpx.js +6 -0
- package/lib/cjs/routes/base/parsers/multixml.js +6 -0
- package/lib/cjs/routes/base/parsers/tacx/TacxParser.js +6 -0
- package/lib/cjs/routes/base/parsers/xml.js +6 -0
- package/lib/cjs/routes/free-ride/service.js +2 -2
- package/lib/cjs/routes/library/service.js +327 -0
- package/lib/cjs/routes/library/types.js +2 -0
- package/lib/cjs/services/overpass/overpass.js +3 -2
- package/lib/esm/activities/base/convert/fit/local-fit.js +26 -2
- package/lib/esm/api/rest/api.js +4 -1
- package/lib/esm/api/rest/incyclist.js +1 -0
- package/lib/esm/ride/display/service.js +1 -1
- package/lib/esm/ride/route/FreeRideDisplayService.js +32 -14
- package/lib/esm/routes/base/parsers/epm.js +6 -0
- package/lib/esm/routes/base/parsers/factory.js +5 -0
- package/lib/esm/routes/base/parsers/geometry.js +6 -0
- package/lib/esm/routes/base/parsers/gpx.js +6 -0
- package/lib/esm/routes/base/parsers/multixml.js +6 -0
- package/lib/esm/routes/base/parsers/tacx/TacxParser.js +6 -0
- package/lib/esm/routes/base/parsers/xml.js +6 -0
- package/lib/esm/routes/free-ride/service.js +2 -2
- package/lib/esm/routes/library/service.js +323 -0
- package/lib/esm/routes/library/types.js +1 -0
- package/lib/esm/services/overpass/overpass.js +3 -2
- package/lib/types/activities/base/convert/fit/local-fit.d.ts +2 -0
- package/lib/types/activities/ride/service.d.ts +1 -1
- package/lib/types/api/fs/index.d.ts +12 -1
- package/lib/types/api/rest/types.d.ts +1 -0
- package/lib/types/api/ui/index.d.ts +1 -0
- package/lib/types/ride/display/service.d.ts +2 -2
- package/lib/types/ride/route/FreeRideDisplayService.d.ts +2 -0
- package/lib/types/routes/base/parsers/epm.d.ts +2 -0
- package/lib/types/routes/base/parsers/factory.d.ts +2 -1
- package/lib/types/routes/base/parsers/geometry.d.ts +4 -1
- package/lib/types/routes/base/parsers/gpx.d.ts +2 -0
- package/lib/types/routes/base/parsers/index.d.ts +1 -1
- package/lib/types/routes/base/parsers/multixml.d.ts +3 -1
- package/lib/types/routes/base/parsers/tacx/TacxParser.d.ts +4 -1
- package/lib/types/routes/base/parsers/types.d.ts +14 -0
- package/lib/types/routes/base/parsers/xml.d.ts +4 -1
- package/lib/types/routes/base/types/index.d.ts +0 -11
- package/lib/types/routes/library/service.d.ts +28 -0
- package/lib/types/routes/library/types.d.ts +57 -0
- package/lib/types/routes/list/cards/RouteCard.d.ts +1 -1
- package/lib/types/routes/types.d.ts +2 -0
- package/package.json +4 -4
|
@@ -40,6 +40,7 @@ const gd_eventlog_1 = require("gd-eventlog");
|
|
|
40
40
|
const settings_1 = require("../../../../settings");
|
|
41
41
|
const decorators_1 = require("../../../../base/decorators");
|
|
42
42
|
const DEG_TO_SEMICIRCLES = (2 ** 31) / 180;
|
|
43
|
+
const DEFAULT_PRODUCT = 3843;
|
|
43
44
|
let LocalFitConverter = (() => {
|
|
44
45
|
let _instanceExtraInitializers = [];
|
|
45
46
|
let _getUserSettings_decorators;
|
|
@@ -68,7 +69,7 @@ let LocalFitConverter = (() => {
|
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
getFitActivity(activity) {
|
|
71
|
-
const { id, title, time, timeTotal, timePause, distance } = activity;
|
|
72
|
+
const { id, title, time, timeTotal, timePause, distance, sport = 'cycling' } = activity;
|
|
72
73
|
const status = 'created';
|
|
73
74
|
const startTime = new Date(activity.startTime).toISOString();
|
|
74
75
|
const logs = activity.logs.map(this.mapLogToFit.bind(this));
|
|
@@ -78,7 +79,7 @@ let LocalFitConverter = (() => {
|
|
|
78
79
|
id: this.getUserSettings().get('uuid', undefined),
|
|
79
80
|
weight: activity.user.weight
|
|
80
81
|
};
|
|
81
|
-
return { id, title, status, logs, laps, startTime, time, timeTotal, timePause, distance, user, screenshots };
|
|
82
|
+
return { id, title, status, logs, laps, startTime, time, timeTotal, timePause, distance, user, screenshots, sport };
|
|
82
83
|
}
|
|
83
84
|
mapLapsToFit(laps, activityStartTime) {
|
|
84
85
|
const activityStartMs = new Date(activityStartTime).getTime();
|
|
@@ -102,13 +103,36 @@ let LocalFitConverter = (() => {
|
|
|
102
103
|
const power = Math.round(powerOrg);
|
|
103
104
|
return { time, speed, slope, cadence, heartrate, distance, power, lat, lon: lng, elevation };
|
|
104
105
|
}
|
|
106
|
+
buildSerialNo() {
|
|
107
|
+
return Date.now() & 0xFFFFFFFF;
|
|
108
|
+
}
|
|
109
|
+
findProduct(name) {
|
|
110
|
+
const entry = Object.entries(fitsdk_1.Profile.types.garminProduct)
|
|
111
|
+
.find(([, value]) => value === name);
|
|
112
|
+
return entry ? Number(entry[0]) : DEFAULT_PRODUCT;
|
|
113
|
+
}
|
|
105
114
|
encode(activity) {
|
|
106
115
|
const encoder = new fitsdk_1.Encoder();
|
|
107
116
|
const startTime = new Date(activity.startTime);
|
|
117
|
+
const manufacturer = this.getUserSettings().get('fitexport.manufacturer', 'garmin');
|
|
118
|
+
const productIdentifier = this.getUserSettings().get('fitexport.device', 'edge1040');
|
|
119
|
+
const storedSerial = this.getUserSettings().get('fitexport.serialNo', undefined);
|
|
120
|
+
const serialNumber = storedSerial ?? this.buildSerialNo();
|
|
121
|
+
if (!storedSerial) {
|
|
122
|
+
this.getUserSettings().set('fitexport.serialNo', serialNumber);
|
|
123
|
+
}
|
|
124
|
+
let product;
|
|
125
|
+
if (Number.isNaN(Number(productIdentifier))) {
|
|
126
|
+
product = this.findProduct(productIdentifier);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
product = productIdentifier;
|
|
130
|
+
}
|
|
108
131
|
encoder.onMesg(fitsdk_1.Profile.MesgNum.FILE_ID, {
|
|
109
132
|
type: 'activity',
|
|
110
|
-
manufacturer
|
|
111
|
-
product
|
|
133
|
+
manufacturer,
|
|
134
|
+
product,
|
|
135
|
+
serialNumber,
|
|
112
136
|
timeCreated: startTime,
|
|
113
137
|
});
|
|
114
138
|
encoder.onMesg(fitsdk_1.Profile.MesgNum.EVENT, {
|
|
@@ -184,7 +208,7 @@ let LocalFitConverter = (() => {
|
|
|
184
208
|
totalElapsedTime: activity.timeTotal,
|
|
185
209
|
totalTimerTime: activity.time,
|
|
186
210
|
totalDistance: activity.distance,
|
|
187
|
-
sport:
|
|
211
|
+
sport: this.mapSport(activity.sport),
|
|
188
212
|
subSport: 'virtualActivity',
|
|
189
213
|
event: 'session',
|
|
190
214
|
eventType: 'stopDisableAll',
|
|
@@ -200,6 +224,14 @@ let LocalFitConverter = (() => {
|
|
|
200
224
|
const uint8Array = encoder.close();
|
|
201
225
|
return uint8Array.buffer;
|
|
202
226
|
}
|
|
227
|
+
mapSport(incyclistSport) {
|
|
228
|
+
if (!incyclistSport || incyclistSport === 'cycling')
|
|
229
|
+
return 'cycling';
|
|
230
|
+
if (incyclistSport === 'running')
|
|
231
|
+
return 'running';
|
|
232
|
+
if (incyclistSport === 'rowing')
|
|
233
|
+
return 'rowing';
|
|
234
|
+
}
|
|
203
235
|
getUserSettings() {
|
|
204
236
|
return (0, settings_1.useUserSettings)();
|
|
205
237
|
}
|
|
@@ -29,7 +29,7 @@ class RemoteFitConverter {
|
|
|
29
29
|
return this.api;
|
|
30
30
|
}
|
|
31
31
|
getFitActivity(activity) {
|
|
32
|
-
const { id, title, time, timeTotal, timePause, distance } = activity;
|
|
32
|
+
const { id, title, time, timeTotal, timePause, distance, sport = 'cycling' } = activity;
|
|
33
33
|
const status = 'created';
|
|
34
34
|
const startTime = new Date(activity.startTime).toISOString();
|
|
35
35
|
const logs = activity.logs.map(this.mapLogToFit.bind(this));
|
|
@@ -39,7 +39,7 @@ class RemoteFitConverter {
|
|
|
39
39
|
id: this.getUserSettings().get('uuid', undefined),
|
|
40
40
|
weight: activity.user.weight
|
|
41
41
|
};
|
|
42
|
-
return { id, title, status, logs, laps, startTime, time, timeTotal, timePause, distance, user, screenshots };
|
|
42
|
+
return { id, title, status, logs, laps, startTime, time, timeTotal, timePause, distance, user, screenshots, sport };
|
|
43
43
|
}
|
|
44
44
|
mapLogToFit(log) {
|
|
45
45
|
const { time, speed, slope, cadence: cadenceOrg, heartrate: heartrateOrg, distance, power: powerOrg, lat, lng, elevation } = log;
|
|
@@ -88,7 +88,8 @@ class TcxConverter {
|
|
|
88
88
|
const trackPoints = this.creatTrackPoints(activity, startTime);
|
|
89
89
|
const laps = this.createLaps(startTime, activity, trackPoints);
|
|
90
90
|
const creator = new IncyclistAttribution();
|
|
91
|
-
const
|
|
91
|
+
const sport = this.mapSport(activity.sport);
|
|
92
|
+
const tcxActivity = new tcx_builder_1.Activity(sport, { Id: startTime, Notes: 'Incyclist Ride', Laps: laps, Creator: creator });
|
|
92
93
|
const activityList = new tcx_builder_1.ActivityList({ activity: [tcxActivity] });
|
|
93
94
|
const tcxObj = new tcx_builder_1.TrainingCenterDatabase({ activities: activityList });
|
|
94
95
|
const xml = tcxObj.toXml();
|
|
@@ -100,6 +101,14 @@ class TcxConverter {
|
|
|
100
101
|
throw err;
|
|
101
102
|
}
|
|
102
103
|
}
|
|
104
|
+
mapSport(incyclistSport) {
|
|
105
|
+
if (!incyclistSport || incyclistSport === 'cycling')
|
|
106
|
+
return 'Biking';
|
|
107
|
+
if (incyclistSport === 'running')
|
|
108
|
+
return 'Running';
|
|
109
|
+
if (incyclistSport === 'rowing')
|
|
110
|
+
return 'Other';
|
|
111
|
+
}
|
|
103
112
|
createLaps(startTime, activity, trackPoints) {
|
|
104
113
|
if (!activity.workout && !activity.laps)
|
|
105
114
|
return this.createActivityLap(startTime, activity, trackPoints);
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DEFAULT_ACTIVITY_TITLE = void 0;
|
|
3
|
+
exports.DEFAULT_SPORT_ACTIVITY_TITLE = exports.DEFAULT_ACTIVITY_TITLE = void 0;
|
|
4
4
|
exports.DEFAULT_ACTIVITY_TITLE = 'Incyclist Ride';
|
|
5
|
+
exports.DEFAULT_SPORT_ACTIVITY_TITLE = {
|
|
6
|
+
cycling: 'Incyclist Ride',
|
|
7
|
+
rowing: 'Incyclist Rowing',
|
|
8
|
+
running: 'Incyclist Run'
|
|
9
|
+
};
|
|
@@ -45,7 +45,7 @@ const routes_1 = require("../../../routes");
|
|
|
45
45
|
const factory_1 = require("./migration/factory");
|
|
46
46
|
const Injection_1 = require("../../../base/decorators/Injection");
|
|
47
47
|
const i18n_1 = require("../../../i18n");
|
|
48
|
-
exports.DB_VERSION = '
|
|
48
|
+
exports.DB_VERSION = '5';
|
|
49
49
|
exports.DB_NAME = 'db';
|
|
50
50
|
let ActivitiesRepository = (() => {
|
|
51
51
|
let _classDecorators = [types_1.Singleton];
|
|
@@ -118,7 +118,8 @@ let Activity = (() => {
|
|
|
118
118
|
}
|
|
119
119
|
getTitle() {
|
|
120
120
|
let title = this.info.summary.title;
|
|
121
|
-
|
|
121
|
+
const sport = this.details?.sport ?? 'cycling';
|
|
122
|
+
if (title === model_1.DEFAULT_ACTIVITY_TITLE || title === model_1.DEFAULT_SPORT_ACTIVITY_TITLE[sport]) {
|
|
122
123
|
if (this.details?.route?.title) {
|
|
123
124
|
title = this.details?.route.title;
|
|
124
125
|
}
|
|
@@ -27,7 +27,8 @@ const buildSummary = (activity, proposedName) => {
|
|
|
27
27
|
const previewImage = preview?.fileName;
|
|
28
28
|
const totalElevation = activity.totalElevation;
|
|
29
29
|
let title = activity.title;
|
|
30
|
-
|
|
30
|
+
const sport = activity.sport ?? 'cycling';
|
|
31
|
+
if (title === model_1.DEFAULT_ACTIVITY_TITLE || title === model_1.DEFAULT_SPORT_ACTIVITY_TITLE[sport]) {
|
|
31
32
|
if (activity.routeType === 'Video' && activity.route)
|
|
32
33
|
title = activity.route.title ?? activity.route.name;
|
|
33
34
|
else if (activity.route)
|
|
@@ -62,7 +62,6 @@ const decorators_1 = require("../../base/decorators");
|
|
|
62
62
|
const i18n_1 = require("../../i18n");
|
|
63
63
|
const utils_3 = require("../list/utils");
|
|
64
64
|
const sleep_1 = require("../../utils/sleep");
|
|
65
|
-
const node_events_1 = require("node:events");
|
|
66
65
|
const SAVE_INTERVAL = 5000;
|
|
67
66
|
let ActivityRideService = (() => {
|
|
68
67
|
let _classDecorators = [types_1.Singleton];
|
|
@@ -106,6 +105,11 @@ let ActivityRideService = (() => {
|
|
|
106
105
|
deviceDataHandler = this.onDeviceData.bind(this);
|
|
107
106
|
gearChangeHandler = this.onGearChange.bind(this);
|
|
108
107
|
dataHealthHandler = this.onDeviceHealthUpdate.bind(this);
|
|
108
|
+
speedHandler = {
|
|
109
|
+
'cycling': this.getCyclingSpeed.bind(this),
|
|
110
|
+
'rowing': this.getRowingPace.bind(this),
|
|
111
|
+
'running': this.getRunningPace.bind(this)
|
|
112
|
+
};
|
|
109
113
|
current;
|
|
110
114
|
constructor() {
|
|
111
115
|
super('ActivityRide');
|
|
@@ -168,7 +172,7 @@ let ActivityRideService = (() => {
|
|
|
168
172
|
this.isSaveDone = false;
|
|
169
173
|
this.isDonateShown = false;
|
|
170
174
|
this.isSummaryShown = false;
|
|
171
|
-
this.logEvent({ message: 'activity started' });
|
|
175
|
+
this.logEvent({ message: 'activity started', sport: this.activity.sport ?? 'cycling' });
|
|
172
176
|
this.startWorker();
|
|
173
177
|
this.enableDeviceHealthCheck();
|
|
174
178
|
this.emit('started');
|
|
@@ -266,6 +270,34 @@ let ActivityRideService = (() => {
|
|
|
266
270
|
return [];
|
|
267
271
|
}
|
|
268
272
|
}
|
|
273
|
+
getRowingPace(speed) {
|
|
274
|
+
if (speed > 0) {
|
|
275
|
+
const value = 30 / speed;
|
|
276
|
+
const min = Math.floor(value);
|
|
277
|
+
const sec = Math.floor((value - min) * 60);
|
|
278
|
+
const p = sec < 10 ? '0' : '';
|
|
279
|
+
return { value: `${min}:${p}${sec}`, unit: '' };
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
return { value: '', unit: '' };
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
getRunningPace(speed) {
|
|
286
|
+
if (speed > 0) {
|
|
287
|
+
const value = 60 / speed;
|
|
288
|
+
const min = Math.floor(value);
|
|
289
|
+
const sec = Math.floor((value - min) * 60);
|
|
290
|
+
const p = sec < 10 ? '0' : '';
|
|
291
|
+
return { value: `${min}:${p}${sec}`, unit: '' };
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
return { value: '', unit: '' };
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
getCyclingSpeed(speed) {
|
|
298
|
+
const [C, U] = this.getUnitConversionShortcuts();
|
|
299
|
+
return { value: C(speed, 'speed')?.toFixed(1) ?? '', unit: U('speed') };
|
|
300
|
+
}
|
|
269
301
|
buildDashboardInfo(currentValues, avgMaxStats, display) {
|
|
270
302
|
const { distance, time, speed, power, slope, heartrate, cadence, distanceRemaining, timeRemaining, gear } = currentValues;
|
|
271
303
|
const { speedDetails, powerDetails, elevationGain, heartrateDetails, cadenceDetails } = avgMaxStats;
|
|
@@ -283,9 +315,10 @@ let ActivityRideService = (() => {
|
|
|
283
315
|
{ value: distanceRemaining === undefined ? undefined : `-${C(distanceRemaining, 'distance', { from: 'km' }).toFixed(2)}` }
|
|
284
316
|
]
|
|
285
317
|
});
|
|
318
|
+
const sport = this.activity.sport ?? 'cycling';
|
|
286
319
|
info.push({ title: 'Speed',
|
|
287
320
|
data: [
|
|
288
|
-
|
|
321
|
+
this.speedHandler[sport](speed),
|
|
289
322
|
speedDetails
|
|
290
323
|
],
|
|
291
324
|
dataState: this.current.dataState?.speed
|
|
@@ -297,7 +330,9 @@ let ActivityRideService = (() => {
|
|
|
297
330
|
info.push({ title: 'Slope', data: [{ value: (0, utils_1.formatNumber)(slope, 1), unit: '%', info: slopeInfo }, elevationGain] });
|
|
298
331
|
}
|
|
299
332
|
info.push({ title: 'Heartrate', data: [{ value: (0, utils_1.formatNumber)(heartrate, 0), unit: 'bpm' }, heartrateDetails], dataState: this.current.dataState?.heartrate });
|
|
300
|
-
|
|
333
|
+
const cadenceUnit = this.activity.sport === 'rowing' ? 'spm' : 'rpm';
|
|
334
|
+
const cadenceInfo = { title: 'Cadence', data: [{ value: (0, utils_1.formatNumber)(cadence, 0), unit: cadenceUnit }, cadenceDetails], dataState: this.current.dataState?.cadence };
|
|
335
|
+
info.push(cadenceInfo);
|
|
301
336
|
if (gear) {
|
|
302
337
|
info.push({ title: 'Gear', data: [{ value: gear }] });
|
|
303
338
|
}
|
|
@@ -334,7 +369,11 @@ let ActivityRideService = (() => {
|
|
|
334
369
|
const [C, U] = this.getUnitConversionShortcuts();
|
|
335
370
|
const stats = this.activity?.stats;
|
|
336
371
|
const maxSpeed = stats?.speed?.max;
|
|
337
|
-
const
|
|
372
|
+
const sport = this.activity.sport ?? 'cycling';
|
|
373
|
+
const data = this.speedHandler[sport](maxSpeed);
|
|
374
|
+
const speedDetails = this.activity.sport === 'cycling' ?
|
|
375
|
+
{ value: (0, utils_1.formatNumber)(C(maxSpeed, 'speed'), 1), label: 'max' } :
|
|
376
|
+
{ value: data.value, label: 'max' };
|
|
338
377
|
const powerDetails = { value: (0, utils_1.formatNumber)(stats?.power?.max, 0), label: 'max' };
|
|
339
378
|
const heartrateDetails = { value: (0, utils_1.formatNumber)(stats?.hrm?.max, 0), label: 'max' };
|
|
340
379
|
const cadenceDetails = { value: (0, utils_1.formatNumber)(stats?.cadence?.max, 0), label: 'max' };
|
|
@@ -351,7 +390,11 @@ let ActivityRideService = (() => {
|
|
|
351
390
|
const [C, U] = this.getUnitConversionShortcuts();
|
|
352
391
|
const stats = this.activity?.stats;
|
|
353
392
|
const avgSpeed = stats?.speed?.avg;
|
|
354
|
-
const
|
|
393
|
+
const sport = this.activity.sport ?? 'cycling';
|
|
394
|
+
const data = this.speedHandler[sport](avgSpeed);
|
|
395
|
+
const speedDetails = this.activity.sport === 'cycling' ?
|
|
396
|
+
{ value: (0, utils_1.formatNumber)(C(avgSpeed, 'speed'), 1), label: 'avg' } :
|
|
397
|
+
{ value: data.value, label: 'max' };
|
|
355
398
|
const powerDetails = { value: (0, utils_1.formatNumber)(stats?.power?.avg, 0), label: 'avg' };
|
|
356
399
|
const heartrateDetails = { value: (0, utils_1.formatNumber)(stats?.hrm?.avg, 0), label: 'avg' };
|
|
357
400
|
const cadenceDetails = { value: (0, utils_1.formatNumber)(stats?.cadence?.avg, 0), label: 'avg' };
|
|
@@ -466,47 +509,17 @@ let ActivityRideService = (() => {
|
|
|
466
509
|
};
|
|
467
510
|
const run = async () => {
|
|
468
511
|
await (0, sleep_1.sleep)(5);
|
|
469
|
-
let cntCompleted = 0;
|
|
470
512
|
let success = false;
|
|
471
|
-
const localEmitter = new node_events_1.EventEmitter();
|
|
472
|
-
const incCompleted = () => {
|
|
473
|
-
cntCompleted++;
|
|
474
|
-
if (cntCompleted >= 2) {
|
|
475
|
-
localEmitter.emit('completed');
|
|
476
|
-
}
|
|
477
|
-
};
|
|
478
|
-
localEmitter.once('completed', () => {
|
|
479
|
-
(0, sleep_1.sleep)(0).then(() => {
|
|
480
|
-
delete this.saveObserver;
|
|
481
|
-
});
|
|
482
|
-
});
|
|
483
513
|
try {
|
|
484
514
|
emit('start', success);
|
|
485
515
|
await this._save();
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
if (convertSuccess) {
|
|
491
|
-
format = 'TCX';
|
|
492
|
-
this.convert('FIT')
|
|
493
|
-
.then(incCompleted)
|
|
494
|
-
.catch(incCompleted);
|
|
495
|
-
}
|
|
496
|
-
else {
|
|
497
|
-
convertSuccess = await this.convert('FIT');
|
|
498
|
-
cntCompleted++;
|
|
499
|
-
if (convertSuccess)
|
|
500
|
-
format = 'FIT';
|
|
501
|
-
}
|
|
502
|
-
if (convertSuccess) {
|
|
503
|
-
uploadSuccess = await this.upload(format);
|
|
504
|
-
}
|
|
505
|
-
success = convertSuccess && uploadSuccess;
|
|
516
|
+
success = await this._convertAndUpload();
|
|
517
|
+
(0, sleep_1.sleep)(0).then(() => {
|
|
518
|
+
delete this.saveObserver;
|
|
519
|
+
});
|
|
506
520
|
this.isSaveDone = true;
|
|
507
521
|
}
|
|
508
522
|
catch {
|
|
509
|
-
cntCompleted++;
|
|
510
523
|
success = false;
|
|
511
524
|
}
|
|
512
525
|
emit('done', success);
|
|
@@ -836,6 +849,29 @@ let ActivityRideService = (() => {
|
|
|
836
849
|
this.logError(err, '_save');
|
|
837
850
|
}
|
|
838
851
|
}
|
|
852
|
+
async _convertAndUpload() {
|
|
853
|
+
const successes = [];
|
|
854
|
+
const promises = [
|
|
855
|
+
this.convert('TCX').then(() => successes.push('tcx')),
|
|
856
|
+
this.convert('FIT').then(() => successes.push('fit')),
|
|
857
|
+
];
|
|
858
|
+
await Promise.allSettled(promises);
|
|
859
|
+
this.logEvent({ message: 'conversion finished, ' });
|
|
860
|
+
const convertSuccess = successes.length > 0;
|
|
861
|
+
let uploadSuccess;
|
|
862
|
+
let format = 'tcx';
|
|
863
|
+
if (this.activity.sport === 'rowing') {
|
|
864
|
+
if (successes.some(e => e === 'fit'))
|
|
865
|
+
format = 'fit';
|
|
866
|
+
}
|
|
867
|
+
else if (!successes.some(e => e === 'tcx')) {
|
|
868
|
+
format = 'fit';
|
|
869
|
+
}
|
|
870
|
+
if (convertSuccess) {
|
|
871
|
+
uploadSuccess = await this.upload(format);
|
|
872
|
+
}
|
|
873
|
+
return convertSuccess && uploadSuccess;
|
|
874
|
+
}
|
|
839
875
|
getSaveInterval() {
|
|
840
876
|
return SAVE_INTERVAL;
|
|
841
877
|
}
|
|
@@ -1003,7 +1039,8 @@ let ActivityRideService = (() => {
|
|
|
1003
1039
|
this.current.position = undefined;
|
|
1004
1040
|
}
|
|
1005
1041
|
}
|
|
1006
|
-
const
|
|
1042
|
+
const sport = this.getDeviceRide().getSport() ?? 'cycling';
|
|
1043
|
+
const title = base_1.DEFAULT_SPORT_ACTIVITY_TITLE[sport];
|
|
1007
1044
|
const id = requestedId ?? (0, uuid_1.v4)();
|
|
1008
1045
|
const date = (0, utils_1.formatDateTime)(new Date(), "%Y%m%d%H%M%S", false);
|
|
1009
1046
|
const name = `${title}-${date}`;
|
|
@@ -1030,7 +1067,8 @@ let ActivityRideService = (() => {
|
|
|
1030
1067
|
totalElevation: 0,
|
|
1031
1068
|
logs: [],
|
|
1032
1069
|
startPos, endPos, segment, realityFactor,
|
|
1033
|
-
fileName
|
|
1070
|
+
fileName,
|
|
1071
|
+
sport
|
|
1034
1072
|
};
|
|
1035
1073
|
return activity;
|
|
1036
1074
|
}
|
package/lib/cjs/api/rest/api.js
CHANGED
|
@@ -12,7 +12,7 @@ class ApiClient {
|
|
|
12
12
|
logger;
|
|
13
13
|
requests;
|
|
14
14
|
init(props) {
|
|
15
|
-
const { uuid, apiKey, requestLog = true, version, appVersion, channel = 'desktop' } = props;
|
|
15
|
+
const { uuid, apiKey, requestLog = true, version, appVersion, channel = 'desktop', platform } = props;
|
|
16
16
|
const headers = {
|
|
17
17
|
"X-uuid": uuid,
|
|
18
18
|
"X-API-Key": apiKey,
|
|
@@ -20,6 +20,9 @@ class ApiClient {
|
|
|
20
20
|
"x-app-version": appVersion,
|
|
21
21
|
"x-react-version": version
|
|
22
22
|
};
|
|
23
|
+
if (platform) {
|
|
24
|
+
headers['x-app-platform'] = platform;
|
|
25
|
+
}
|
|
23
26
|
this.axios = axios_1.default.create({ headers });
|
|
24
27
|
if (requestLog) {
|
|
25
28
|
this.logger = new gd_eventlog_1.EventLogger('API');
|
|
@@ -135,7 +135,6 @@ let DeviceRideService = (() => {
|
|
|
135
135
|
return this.data;
|
|
136
136
|
}
|
|
137
137
|
pauseLogging() {
|
|
138
|
-
console.log('# PAUSE LOGGING');
|
|
139
138
|
this.logPaused = true;
|
|
140
139
|
const interfaces = this.getEnabledInterfaces();
|
|
141
140
|
interfaces.forEach(i => i.pauseLogging());
|
|
@@ -145,7 +144,6 @@ let DeviceRideService = (() => {
|
|
|
145
144
|
.forEach(a => a.pauseLogging());
|
|
146
145
|
}
|
|
147
146
|
resumeLogging() {
|
|
148
|
-
console.log('# RESUME LOGGING');
|
|
149
147
|
this.logPaused = false;
|
|
150
148
|
const interfaces = this.getEnabledInterfaces();
|
|
151
149
|
interfaces.forEach(i => i.resumeLogging());
|
|
@@ -927,6 +925,19 @@ let DeviceRideService = (() => {
|
|
|
927
925
|
this.storeOriginalCyclingMode();
|
|
928
926
|
return this.startAdapters(adapters, 'start', props);
|
|
929
927
|
}
|
|
928
|
+
getSport() {
|
|
929
|
+
try {
|
|
930
|
+
const info = this.getControlAdapter();
|
|
931
|
+
const adapter = info?.adapter;
|
|
932
|
+
const sports = adapter?.getSupportedSports?.() ?? ['cycling'];
|
|
933
|
+
this.logEvent({ message: '[DeviceRide] getSports', adapter: info.adapter.getName(), sports: sports.join(',') });
|
|
934
|
+
return sports[0];
|
|
935
|
+
}
|
|
936
|
+
catch (err) {
|
|
937
|
+
this.logError(err, 'getSports');
|
|
938
|
+
return 'cycling';
|
|
939
|
+
}
|
|
940
|
+
}
|
|
930
941
|
async startRetry(props) {
|
|
931
942
|
await this.lazyInit();
|
|
932
943
|
const selected = this.getRideAdapters();
|
|
@@ -33,6 +33,9 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
|
|
|
33
33
|
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
34
34
|
done = true;
|
|
35
35
|
};
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
36
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
40
|
exports.FreeRideDisplayService = void 0;
|
|
38
41
|
const activities_1 = require("../../activities");
|
|
@@ -44,13 +47,8 @@ const route_1 = require("../../routes/base/model/route");
|
|
|
44
47
|
const geo_1 = require("../../utils/geo");
|
|
45
48
|
const GpxDisplayService_1 = require("./GpxDisplayService");
|
|
46
49
|
const utils_2 = require("../../utils");
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
const pp = p;
|
|
50
|
-
const id = pp.id ?? '{lat:' + Number(p.lat).toFixed(4) + ',lng:' + Number(p.lng).toFixed(4) + '}';
|
|
51
|
-
return `${p.cnt},${p.routeDistance.toFixed(0)},${id})`;
|
|
52
|
-
});
|
|
53
|
-
};
|
|
50
|
+
const node_events_1 = __importDefault(require("node:events"));
|
|
51
|
+
const sleep_1 = require("../../utils/sleep");
|
|
54
52
|
const DEFAULT_OPTIONS_DELAY = 5000;
|
|
55
53
|
let FreeRideDisplayService = (() => {
|
|
56
54
|
let _classSuper = GpxDisplayService_1.GpxDisplayService;
|
|
@@ -85,6 +83,7 @@ let FreeRideDisplayService = (() => {
|
|
|
85
83
|
onViewportChange = this.saveViewport.bind(this);
|
|
86
84
|
onOptionsVisibleChangedHandler = this.onOptionsVisibleChanged.bind(this);
|
|
87
85
|
onTurnHandler = this.onTurn.bind(this);
|
|
86
|
+
internalEmitter = new node_events_1.default();
|
|
88
87
|
start() {
|
|
89
88
|
try {
|
|
90
89
|
super.start();
|
|
@@ -409,13 +408,29 @@ let FreeRideDisplayService = (() => {
|
|
|
409
408
|
return false;
|
|
410
409
|
}
|
|
411
410
|
getNextOptions(forStart) {
|
|
411
|
+
let options;
|
|
412
|
+
const freeRide = this.getFreeRideService();
|
|
413
|
+
const getOptions = async () => {
|
|
414
|
+
let finished = false;
|
|
415
|
+
const setFinished = () => { finished = true; };
|
|
416
|
+
this.internalEmitter.on('stop-query', setFinished);
|
|
417
|
+
do {
|
|
418
|
+
try {
|
|
419
|
+
options = await freeRide.getNextOptions(forStart);
|
|
420
|
+
}
|
|
421
|
+
catch (err) {
|
|
422
|
+
this.logError(err, 'getNextOptions');
|
|
423
|
+
options = undefined;
|
|
424
|
+
}
|
|
425
|
+
if (!options?.length && !finished && !forStart) {
|
|
426
|
+
this.logEvent({ message: 'no options available - retry in 3000ms', });
|
|
427
|
+
await (0, sleep_1.sleep)(3000);
|
|
428
|
+
}
|
|
429
|
+
} while (!options?.length && !finished && !forStart);
|
|
430
|
+
this.internalEmitter.off('stop-query', setFinished);
|
|
431
|
+
};
|
|
412
432
|
return new Promise(done => {
|
|
413
|
-
|
|
414
|
-
freeRide.getNextOptions(forStart)
|
|
415
|
-
.catch(err => {
|
|
416
|
-
this.logError(err, 'getNextOptions');
|
|
417
|
-
done();
|
|
418
|
-
})
|
|
433
|
+
getOptions()
|
|
419
434
|
.then(() => {
|
|
420
435
|
done();
|
|
421
436
|
if (this.isStarting) {
|
|
@@ -489,9 +504,15 @@ let FreeRideDisplayService = (() => {
|
|
|
489
504
|
const prev = { ...this.position, totalDistance: this.position.routeDistance };
|
|
490
505
|
let route = this.route;
|
|
491
506
|
if (newRouteDistance > route.description.distance) {
|
|
507
|
+
this.internalEmitter.emit('stop-query');
|
|
492
508
|
route = this.route.clone();
|
|
493
509
|
const selectedOption = this.getFreeRideService().getSelectedOption();
|
|
494
|
-
|
|
510
|
+
if (selectedOption)
|
|
511
|
+
this.appendOption(route, selectedOption);
|
|
512
|
+
else {
|
|
513
|
+
this.logEvent({ message: 'no continuation available' });
|
|
514
|
+
return this.position;
|
|
515
|
+
}
|
|
495
516
|
}
|
|
496
517
|
const nextPos = (0, routes_1.getNextPosition)(route, { routeDistance: activityPos.routeDistance, prev });
|
|
497
518
|
if (!nextPos) {
|
|
@@ -206,8 +206,8 @@ let RouteDisplayService = (() => {
|
|
|
206
206
|
const nearbyRides = this.getNearbyRidesProps(props);
|
|
207
207
|
const [C, U] = this.getUnitConversionShortcuts();
|
|
208
208
|
const isLoop = this.currentRoute?.description?.isLoop;
|
|
209
|
-
const xScale = { value: C(1, 'distance', { from:
|
|
210
|
-
const yScale = { value: C(1, 'elevation', { from:
|
|
209
|
+
const xScale = { value: C(1, 'distance', { from: 'm' }), unit: U('distance') };
|
|
210
|
+
const yScale = { value: C(1, 'elevation', { from: 'm' }), unit: U('elevation') };
|
|
211
211
|
const mapStartPos = (isLoop && !loopOverwrite) ? undefined : startPos;
|
|
212
212
|
return {
|
|
213
213
|
...parent,
|
|
@@ -10,6 +10,12 @@ class EPMParser extends xml_1.XMLParser {
|
|
|
10
10
|
supportsExtension(extension) {
|
|
11
11
|
return extension.toLowerCase() === 'epm';
|
|
12
12
|
}
|
|
13
|
+
getPrimaryExtension() {
|
|
14
|
+
return 'epm';
|
|
15
|
+
}
|
|
16
|
+
getCompanionExtensions() {
|
|
17
|
+
return ['epp'];
|
|
18
|
+
}
|
|
13
19
|
async loadPoints(context) {
|
|
14
20
|
const { data, route } = context;
|
|
15
21
|
route.points = [];
|
|
@@ -24,6 +24,11 @@ class ParserFactory {
|
|
|
24
24
|
throw new Error(`invalid file format ${extension}`);
|
|
25
25
|
return matching;
|
|
26
26
|
}
|
|
27
|
+
isPrimaryExtension(extension) {
|
|
28
|
+
const ext = extension.toLowerCase();
|
|
29
|
+
const isPrimary = this.parsers.some(p => p.getPrimaryExtension() === ext);
|
|
30
|
+
return isPrimary;
|
|
31
|
+
}
|
|
27
32
|
findMatching(extension, data) {
|
|
28
33
|
const matching = this.parsers
|
|
29
34
|
.filter(p => p.supportsExtension(extension))
|
|
@@ -52,6 +52,12 @@ let GeometryParser = (() => {
|
|
|
52
52
|
const geometry = await this.getData(file, data);
|
|
53
53
|
return await this.parse(file, geometry);
|
|
54
54
|
}
|
|
55
|
+
getPrimaryExtension() {
|
|
56
|
+
return 'xml';
|
|
57
|
+
}
|
|
58
|
+
getCompanionExtensions() {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
55
61
|
supportsExtension(extension) {
|
|
56
62
|
return extension.toLowerCase() === 'json';
|
|
57
63
|
}
|
|
@@ -14,6 +14,12 @@ class GPXParser extends xml_1.XMLParser {
|
|
|
14
14
|
super();
|
|
15
15
|
this.props = props;
|
|
16
16
|
}
|
|
17
|
+
getPrimaryExtension() {
|
|
18
|
+
return 'gpx';
|
|
19
|
+
}
|
|
20
|
+
getCompanionExtensions() {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
17
23
|
supportsExtension(extension) {
|
|
18
24
|
return extension.toLowerCase() === 'gpx';
|
|
19
25
|
}
|
|
@@ -20,6 +20,12 @@ class MultipleXMLParser {
|
|
|
20
20
|
supportsExtension(extension) {
|
|
21
21
|
return extension?.toLowerCase() === 'xml';
|
|
22
22
|
}
|
|
23
|
+
getPrimaryExtension() {
|
|
24
|
+
return 'xml';
|
|
25
|
+
}
|
|
26
|
+
getCompanionExtensions() {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
23
29
|
supportsContent() {
|
|
24
30
|
return true;
|
|
25
31
|
}
|
|
@@ -39,6 +39,12 @@ class TacxParser {
|
|
|
39
39
|
supportsContent(data) {
|
|
40
40
|
return TacxReader_1.TacxFileReader.isValid(data);
|
|
41
41
|
}
|
|
42
|
+
getPrimaryExtension() {
|
|
43
|
+
return 'rlv';
|
|
44
|
+
}
|
|
45
|
+
getCompanionExtensions() {
|
|
46
|
+
return ['pgmf'];
|
|
47
|
+
}
|
|
42
48
|
buildContext(file) {
|
|
43
49
|
const { dir, delimiter: d } = file;
|
|
44
50
|
if (file.ext === 'rlv') {
|