incyclist-devices 1.3.0 → 1.4.1
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/CyclingMode.d.ts +72 -0
- package/lib/CyclingMode.js +66 -0
- package/lib/Device.d.ts +48 -10
- package/lib/Device.js +9 -8
- package/lib/DeviceProtocol.d.ts +40 -12
- package/lib/DeviceProtocol.js +16 -16
- package/lib/DeviceRegistry.d.ts +4 -4
- package/lib/DeviceRegistry.js +10 -10
- package/lib/DeviceSupport.d.ts +4 -3
- package/lib/DeviceSupport.js +32 -8
- package/lib/ant/AntAdapter.d.ts +7 -3
- package/lib/ant/AntAdapter.js +23 -3
- package/lib/ant/AntScanner.d.ts +15 -6
- package/lib/ant/AntScanner.js +372 -128
- package/lib/ant/antfe/AntFEAdapter.d.ts +1 -1
- package/lib/ant/antfe/AntFEAdapter.js +191 -92
- package/lib/ant/anthrm/AntHrmAdapter.d.ts +3 -1
- package/lib/ant/anthrm/AntHrmAdapter.js +70 -19
- package/lib/ant/utils.js +2 -1
- package/lib/calculations.d.ts +12 -13
- package/lib/calculations.js +88 -125
- package/lib/daum/DaumAdapter.d.ts +29 -6
- package/lib/daum/DaumAdapter.js +219 -96
- package/lib/daum/ERGCyclingMode.d.ts +28 -0
- package/lib/daum/ERGCyclingMode.js +207 -0
- package/lib/daum/PowerMeterCyclingMode.d.ts +18 -0
- package/lib/daum/PowerMeterCyclingMode.js +79 -0
- package/lib/daum/SmartTrainerCyclingMode.d.ts +41 -0
- package/lib/daum/SmartTrainerCyclingMode.js +344 -0
- package/lib/daum/classic/DaumClassicAdapter.d.ts +3 -1
- package/lib/daum/classic/DaumClassicAdapter.js +46 -32
- package/lib/daum/classic/DaumClassicCyclingMode.d.ts +13 -0
- package/lib/daum/classic/DaumClassicCyclingMode.js +98 -0
- package/lib/daum/classic/DaumClassicProtocol.d.ts +5 -3
- package/lib/daum/classic/DaumClassicProtocol.js +47 -8
- package/lib/daum/classic/ERGCyclingMode.d.ts +23 -0
- package/lib/daum/classic/ERGCyclingMode.js +171 -0
- package/lib/daum/classic/bike.d.ts +41 -37
- package/lib/daum/classic/bike.js +86 -53
- package/lib/daum/classic/utils.d.ts +3 -3
- package/lib/daum/classic/utils.js +18 -10
- package/lib/daum/indoorbike.d.ts +2 -1
- package/lib/daum/indoorbike.js +23 -21
- package/lib/daum/premium/DaumPremiumAdapter.d.ts +2 -2
- package/lib/daum/premium/DaumPremiumAdapter.js +30 -20
- package/lib/daum/premium/DaumPremiumProtocol.d.ts +11 -2
- package/lib/daum/premium/DaumPremiumProtocol.js +57 -10
- package/lib/daum/premium/bike.d.ts +63 -52
- package/lib/daum/premium/bike.js +254 -207
- package/lib/daum/premium/tcpserial.d.ts +18 -14
- package/lib/daum/premium/tcpserial.js +44 -20
- package/lib/daum/premium/utils.d.ts +2 -2
- package/lib/simulator/Simulator.d.ts +13 -7
- package/lib/simulator/Simulator.js +62 -21
- package/lib/utils.d.ts +3 -1
- package/lib/utils.js +39 -18
- package/package.json +12 -11
- package/lib/ant/AntScanner.unit.tests.d.ts +0 -1
- package/lib/ant/AntScanner.unit.tests.js +0 -25
- package/lib/ant/antfe/AntFEProcessor.d.ts +0 -40
- package/lib/ant/antfe/AntFEProcessor.js +0 -238
- package/lib/ant/antfe/AntHrmProtocol.d.ts +0 -9
- package/lib/ant/antfe/AntHrmProtocol.js +0 -30
- package/lib/ant/antfe/FEDevice.d.ts +0 -1
- package/lib/ant/antfe/FEDevice.js +0 -7
- package/lib/ant/antfe/bike.d.ts +0 -47
- package/lib/ant/antfe/bike.js +0 -602
- package/lib/ant/anthrm/anthrm.d.ts +0 -33
- package/lib/ant/anthrm/anthrm.js +0 -523
- package/lib/simulator/Simulator.unit.tests.d.ts +0 -1
- package/lib/simulator/Simulator.unit.tests.js +0 -79
package/lib/daum/DaumAdapter.js
CHANGED
|
@@ -8,22 +8,83 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
11
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
const Device_1 = require("../Device");
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (d === undefined)
|
|
20
|
-
return d;
|
|
21
|
-
return parseInt(d);
|
|
22
|
-
}
|
|
15
|
+
const Device_1 = __importDefault(require("../Device"));
|
|
16
|
+
const ERGCyclingMode_1 = __importDefault(require("./ERGCyclingMode"));
|
|
17
|
+
const SmartTrainerCyclingMode_1 = __importDefault(require("./SmartTrainerCyclingMode"));
|
|
18
|
+
const PowerMeterCyclingMode_1 = __importDefault(require("./PowerMeterCyclingMode"));
|
|
19
|
+
const utils_1 = require("../utils");
|
|
20
|
+
const DEFAULT_BIKE_WEIGHT = 10;
|
|
21
|
+
const DEFAULT_USER_WEIGHT = 75;
|
|
23
22
|
class DaumAdapterBase extends Device_1.default {
|
|
24
23
|
constructor(props, bike) {
|
|
25
24
|
super(props);
|
|
25
|
+
this.requests = [];
|
|
26
|
+
this.adapterTime = 0;
|
|
27
|
+
this.requestBusy = false;
|
|
28
|
+
this.updateBusy = false;
|
|
26
29
|
this.bike = bike;
|
|
30
|
+
this.stopped = false;
|
|
31
|
+
this.paused = false;
|
|
32
|
+
this.data = {};
|
|
33
|
+
const options = props || {};
|
|
34
|
+
this.cyclingMode = options.cyclingMode;
|
|
35
|
+
this.setUserSettings(options.userSettings);
|
|
36
|
+
this.setBikeSettings(options.bikeSettings);
|
|
37
|
+
}
|
|
38
|
+
setCyclingMode(mode, settings) {
|
|
39
|
+
let selectedMode;
|
|
40
|
+
if (typeof mode === 'string') {
|
|
41
|
+
const supported = this.getSupportedCyclingModes();
|
|
42
|
+
const CyclingModeClass = supported.find(M => { const m = new M(this); return m.getName() === mode; });
|
|
43
|
+
if (CyclingModeClass) {
|
|
44
|
+
this.cyclingMode = new CyclingModeClass(this, settings);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
selectedMode = this.getDefaultCyclingMode();
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
selectedMode = mode;
|
|
51
|
+
}
|
|
52
|
+
this.cyclingMode = selectedMode;
|
|
53
|
+
this.cyclingMode.setSettings(settings);
|
|
54
|
+
}
|
|
55
|
+
getSupportedCyclingModes() {
|
|
56
|
+
return [ERGCyclingMode_1.default, SmartTrainerCyclingMode_1.default, PowerMeterCyclingMode_1.default];
|
|
57
|
+
}
|
|
58
|
+
getCyclingMode() {
|
|
59
|
+
if (!this.cyclingMode)
|
|
60
|
+
this.setCyclingMode(this.getDefaultCyclingMode());
|
|
61
|
+
return this.cyclingMode;
|
|
62
|
+
}
|
|
63
|
+
getDefaultCyclingMode() {
|
|
64
|
+
return new ERGCyclingMode_1.default(this);
|
|
65
|
+
}
|
|
66
|
+
setUserSettings(userSettings) {
|
|
67
|
+
this.userSettings = userSettings || {};
|
|
68
|
+
if (this.bike) {
|
|
69
|
+
if (!this.bike.settings)
|
|
70
|
+
this.bike.settings = { user: {} };
|
|
71
|
+
if (!this.bike.settings.user)
|
|
72
|
+
this.bike.settings.user = {};
|
|
73
|
+
this.bike.settings.user.weight = this.userSettings.weight || DEFAULT_USER_WEIGHT;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
setBikeSettings(bikeSettings) {
|
|
77
|
+
this.bikeSettings = bikeSettings || {};
|
|
78
|
+
if (this.bike) {
|
|
79
|
+
if (!this.bike.settings)
|
|
80
|
+
this.bike.settings = {};
|
|
81
|
+
this.bike.settings.weight = this.userSettings.weight || DEFAULT_BIKE_WEIGHT;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
getWeight() {
|
|
85
|
+
const userWeight = this.userSettings.weight || DEFAULT_USER_WEIGHT;
|
|
86
|
+
const bikeWeight = this.bikeSettings.weight || DEFAULT_BIKE_WEIGHT;
|
|
87
|
+
return bikeWeight + userWeight;
|
|
27
88
|
}
|
|
28
89
|
getCurrentBikeData() {
|
|
29
90
|
throw new Error('Method not implemented.');
|
|
@@ -46,12 +107,30 @@ class DaumAdapterBase extends Device_1.default {
|
|
|
46
107
|
setIgnoreBike(ignore) {
|
|
47
108
|
this.ignoreBike = ignore;
|
|
48
109
|
}
|
|
110
|
+
isStopped() {
|
|
111
|
+
return this.stopped;
|
|
112
|
+
}
|
|
49
113
|
initData() {
|
|
50
114
|
this.distanceInternal = undefined;
|
|
51
|
-
this.paused =
|
|
52
|
-
this.
|
|
115
|
+
this.paused = false;
|
|
116
|
+
this.stopped = false;
|
|
117
|
+
this.data = {
|
|
118
|
+
time: 0,
|
|
119
|
+
slope: 0,
|
|
120
|
+
distance: 0,
|
|
121
|
+
speed: 0,
|
|
122
|
+
isPedalling: false,
|
|
123
|
+
power: 0,
|
|
124
|
+
distanceInternal: 0
|
|
125
|
+
};
|
|
53
126
|
this.currentRequest = {};
|
|
54
127
|
this.requests = [];
|
|
128
|
+
const name = this.getCyclingMode().getName();
|
|
129
|
+
const settings = this.getCyclingMode().getSettings();
|
|
130
|
+
this.setCyclingMode(name, settings);
|
|
131
|
+
}
|
|
132
|
+
start(props) {
|
|
133
|
+
throw new Error('Method not implemented.');
|
|
55
134
|
}
|
|
56
135
|
startUpdatePull() {
|
|
57
136
|
if (this.iv)
|
|
@@ -59,7 +138,11 @@ class DaumAdapterBase extends Device_1.default {
|
|
|
59
138
|
if (this.ignoreBike && this.ignoreHrm && this.ignorePower)
|
|
60
139
|
return;
|
|
61
140
|
this.iv = setInterval(() => {
|
|
62
|
-
this.
|
|
141
|
+
this.bikeSync();
|
|
142
|
+
}, 1000);
|
|
143
|
+
this.iv = setInterval(() => {
|
|
144
|
+
this.sendData();
|
|
145
|
+
this.refreshRequests();
|
|
63
146
|
}, 1000);
|
|
64
147
|
}
|
|
65
148
|
connect() {
|
|
@@ -74,43 +157,9 @@ class DaumAdapterBase extends Device_1.default {
|
|
|
74
157
|
return;
|
|
75
158
|
this.logger.logEvent(event);
|
|
76
159
|
}
|
|
77
|
-
sendBikeUpdate(request) {
|
|
78
|
-
return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
|
|
79
|
-
if (request.slope) {
|
|
80
|
-
this.data.slope = request.slope;
|
|
81
|
-
}
|
|
82
|
-
try {
|
|
83
|
-
if (this.bike.processor !== undefined) {
|
|
84
|
-
this.bike.processor.setValues(request);
|
|
85
|
-
}
|
|
86
|
-
this.logEvent({ message: "sendBikeUpdate():sending", request });
|
|
87
|
-
if (request.slope !== undefined) {
|
|
88
|
-
yield this.bike.setSlope(request.slope);
|
|
89
|
-
}
|
|
90
|
-
if (request.targetPower !== undefined) {
|
|
91
|
-
yield this.bike.setPower(request.targetPower);
|
|
92
|
-
}
|
|
93
|
-
if (request.minPower !== undefined && request.maxPower !== undefined && request.minPower === request.maxPower) {
|
|
94
|
-
yield this.bike.setPower(request.minPower);
|
|
95
|
-
}
|
|
96
|
-
if (request.maxPower !== undefined) {
|
|
97
|
-
}
|
|
98
|
-
if (request.minPower !== undefined) {
|
|
99
|
-
}
|
|
100
|
-
if (request.maxHrm !== undefined) {
|
|
101
|
-
}
|
|
102
|
-
if (request.minHrm !== undefined) {
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
catch (err) {
|
|
106
|
-
this.logEvent({ message: 'sendBikeUpdate error', error: err.message });
|
|
107
|
-
resolve(undefined);
|
|
108
|
-
}
|
|
109
|
-
resolve(request);
|
|
110
|
-
}));
|
|
111
|
-
}
|
|
112
160
|
stop() {
|
|
113
161
|
this.logEvent({ message: 'stop request' });
|
|
162
|
+
this.stopped = true;
|
|
114
163
|
return new Promise((resolve, reject) => {
|
|
115
164
|
try {
|
|
116
165
|
if (this.iv) {
|
|
@@ -128,63 +177,86 @@ class DaumAdapterBase extends Device_1.default {
|
|
|
128
177
|
});
|
|
129
178
|
}
|
|
130
179
|
pause() {
|
|
180
|
+
this.logEvent({ message: 'pause' });
|
|
131
181
|
return new Promise(resolve => {
|
|
132
182
|
this.paused = true;
|
|
133
183
|
resolve(true);
|
|
134
184
|
});
|
|
135
185
|
}
|
|
136
186
|
resume() {
|
|
187
|
+
this.logEvent({ message: 'resume' });
|
|
137
188
|
return new Promise(resolve => {
|
|
138
189
|
this.paused = false;
|
|
139
190
|
resolve(true);
|
|
140
191
|
});
|
|
141
192
|
}
|
|
142
|
-
sendUpdate(
|
|
193
|
+
sendUpdate(request) {
|
|
143
194
|
return __awaiter(this, void 0, void 0, function* () {
|
|
144
195
|
if (this.paused)
|
|
145
196
|
return;
|
|
146
|
-
this.logEvent({ message: 'sendUpdate',
|
|
147
|
-
this.
|
|
197
|
+
this.logEvent({ message: 'sendUpdate', request, waiting: this.requests.length });
|
|
198
|
+
return yield this.processClientRequest(request);
|
|
148
199
|
});
|
|
149
200
|
}
|
|
201
|
+
sendData() {
|
|
202
|
+
if (this.onDataFn)
|
|
203
|
+
this.onDataFn(this.data);
|
|
204
|
+
}
|
|
150
205
|
update() {
|
|
151
206
|
return __awaiter(this, void 0, void 0, function* () {
|
|
152
|
-
|
|
153
|
-
return;
|
|
154
|
-
if (!this.ignoreBike) {
|
|
155
|
-
if (this.requests.length === 0) {
|
|
156
|
-
this.sendUpdate({ refresh: true });
|
|
157
|
-
}
|
|
158
|
-
if (this.requests.length > 0) {
|
|
159
|
-
const processing = [...this.requests];
|
|
160
|
-
processing.forEach((request) => __awaiter(this, void 0, void 0, function* () {
|
|
161
|
-
try {
|
|
162
|
-
this.logEvent({ message: 'bike update request', request });
|
|
163
|
-
yield this.sendBikeUpdate(request);
|
|
164
|
-
this.requests.shift();
|
|
165
|
-
}
|
|
166
|
-
catch (err) {
|
|
167
|
-
this.logEvent({ message: 'bike update error', error: err.message, stack: err.stack, request });
|
|
168
|
-
}
|
|
169
|
-
}));
|
|
170
|
-
}
|
|
171
|
-
}
|
|
207
|
+
this.updateBusy = true;
|
|
172
208
|
this.getCurrentBikeData()
|
|
173
209
|
.then(bikeData => {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
this.
|
|
177
|
-
if (this.onDataFn) {
|
|
178
|
-
console.log('~~~updateBike:', this.ignoreHrm, this.data);
|
|
179
|
-
this.onDataFn(this.data);
|
|
180
|
-
}
|
|
210
|
+
this.updateData(this.data, bikeData);
|
|
211
|
+
this.transformData();
|
|
212
|
+
this.updateBusy = false;
|
|
181
213
|
})
|
|
182
214
|
.catch(err => {
|
|
183
215
|
this.logEvent({ message: 'bike update error', error: err.message, stack: err.stack });
|
|
216
|
+
this.updateBusy = false;
|
|
184
217
|
});
|
|
185
218
|
});
|
|
186
219
|
}
|
|
187
|
-
|
|
220
|
+
sendRequests() {
|
|
221
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
222
|
+
if (this.requests.length > 0) {
|
|
223
|
+
const processing = [...this.requests];
|
|
224
|
+
const cnt = processing.length;
|
|
225
|
+
processing.forEach((request, idx) => __awaiter(this, void 0, void 0, function* () {
|
|
226
|
+
if (cnt > 1 && idx < cnt - 1) {
|
|
227
|
+
this.logEvent({ message: 'ignoring bike update request', request });
|
|
228
|
+
this.requests.shift();
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
}));
|
|
232
|
+
const request = processing[0];
|
|
233
|
+
try {
|
|
234
|
+
yield this.sendRequest(request);
|
|
235
|
+
this.requests.shift();
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
this.logEvent({ message: 'bike update error', error: err.message, stack: err.stack, request });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
bikeSync() {
|
|
244
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
245
|
+
if (this.paused) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (this.updateBusy || this.requestBusy) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
this.logEvent({ message: 'bikeSync' });
|
|
252
|
+
if (!this.ignoreBike) {
|
|
253
|
+
yield this.sendRequests();
|
|
254
|
+
}
|
|
255
|
+
yield this.update();
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
updateData(prev, bikeData) {
|
|
259
|
+
let data = {};
|
|
188
260
|
data.isPedalling = bikeData.cadence > 0;
|
|
189
261
|
data.power = bikeData.power;
|
|
190
262
|
data.pedalRpm = bikeData.cadence;
|
|
@@ -192,28 +264,31 @@ class DaumAdapterBase extends Device_1.default {
|
|
|
192
264
|
data.heartrate = bikeData.heartrate;
|
|
193
265
|
data.distance = bikeData.distance / 100;
|
|
194
266
|
data.distanceInternal = bikeData.distance;
|
|
195
|
-
data.time = bikeData.time;
|
|
196
267
|
data.gear = bikeData.gear;
|
|
197
|
-
if (this.
|
|
198
|
-
|
|
268
|
+
if (this.tsPrevData && data.isPedalling) {
|
|
269
|
+
this.adapterTime = Date.now() - this.tsPrevData;
|
|
199
270
|
}
|
|
200
|
-
|
|
271
|
+
this.tsPrevData = Date.now();
|
|
272
|
+
data.time = Math.round(this.adapterTime || 0);
|
|
273
|
+
if (bikeData.slope)
|
|
274
|
+
data.slope = bikeData.slope;
|
|
275
|
+
this.data = this.getCyclingMode().updateData(data);
|
|
201
276
|
}
|
|
202
|
-
transformData(
|
|
203
|
-
if (
|
|
277
|
+
transformData() {
|
|
278
|
+
if (this.data === undefined)
|
|
204
279
|
return;
|
|
205
280
|
let distance = 0;
|
|
206
|
-
if (this.distanceInternal !== undefined &&
|
|
207
|
-
distance = intVal(
|
|
281
|
+
if (this.distanceInternal !== undefined && this.data.distanceInternal !== undefined) {
|
|
282
|
+
distance = (0, utils_1.intVal)(this.data.distanceInternal - this.distanceInternal);
|
|
208
283
|
}
|
|
209
|
-
if (
|
|
210
|
-
this.distanceInternal =
|
|
284
|
+
if (this.data.distanceInternal !== undefined)
|
|
285
|
+
this.distanceInternal = this.data.distanceInternal;
|
|
211
286
|
let data = {
|
|
212
|
-
speed: floatVal(
|
|
213
|
-
slope: floatVal(
|
|
214
|
-
power: intVal(
|
|
215
|
-
cadence: intVal(
|
|
216
|
-
heartrate: intVal(
|
|
287
|
+
speed: (0, utils_1.floatVal)(this.data.speed),
|
|
288
|
+
slope: (0, utils_1.floatVal)(this.data.slope),
|
|
289
|
+
power: (0, utils_1.intVal)(this.data.power),
|
|
290
|
+
cadence: (0, utils_1.intVal)(this.data.pedalRpm),
|
|
291
|
+
heartrate: (0, utils_1.intVal)(this.data.heartrate),
|
|
217
292
|
distance,
|
|
218
293
|
timestamp: Date.now()
|
|
219
294
|
};
|
|
@@ -226,7 +301,55 @@ class DaumAdapterBase extends Device_1.default {
|
|
|
226
301
|
if (this.ignoreBike) {
|
|
227
302
|
data = { heartrate: data.heartrate };
|
|
228
303
|
}
|
|
229
|
-
|
|
304
|
+
this.data = data;
|
|
305
|
+
}
|
|
306
|
+
sendRequest(request) {
|
|
307
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
308
|
+
this.requestBusy = true;
|
|
309
|
+
try {
|
|
310
|
+
this.logEvent({ message: 'sendRequest', request });
|
|
311
|
+
const bike = this.getBike();
|
|
312
|
+
const isReset = (!request || request.reset || Object.keys(request).length === 0);
|
|
313
|
+
if (isReset) {
|
|
314
|
+
this.requestBusy = false;
|
|
315
|
+
return {};
|
|
316
|
+
}
|
|
317
|
+
if (request.slope !== undefined) {
|
|
318
|
+
yield bike.setSlope(request.slope);
|
|
319
|
+
}
|
|
320
|
+
if (request.targetPower !== undefined) {
|
|
321
|
+
yield bike.setPower(request.targetPower);
|
|
322
|
+
}
|
|
323
|
+
this.requestBusy = false;
|
|
324
|
+
return request;
|
|
325
|
+
}
|
|
326
|
+
catch (err) {
|
|
327
|
+
this.requestBusy = false;
|
|
328
|
+
this.logEvent({ message: 'error', fn: 'sendRequest()', error: err.message || err });
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
refreshRequests() {
|
|
334
|
+
if (!this.data.isPedalling || this.data.pedalRpm === 0)
|
|
335
|
+
return;
|
|
336
|
+
let bikeRequest = this.getCyclingMode().sendBikeUpdate({ refresh: true });
|
|
337
|
+
const prev = this.requests[this.requests.length - 1];
|
|
338
|
+
if (bikeRequest.targetPower !== undefined && bikeRequest.targetPower !== prev.targetPower) {
|
|
339
|
+
this.logEvent({ message: 'add request', request: bikeRequest });
|
|
340
|
+
this.requests.push(bikeRequest);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
processClientRequest(request) {
|
|
344
|
+
if (request.slope !== undefined) {
|
|
345
|
+
this.data.slope = request.slope;
|
|
346
|
+
}
|
|
347
|
+
return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
|
|
348
|
+
let bikeRequest = this.getCyclingMode().sendBikeUpdate(request);
|
|
349
|
+
this.logEvent({ message: 'add request', request: bikeRequest });
|
|
350
|
+
this.requests.push(bikeRequest);
|
|
351
|
+
resolve(bikeRequest);
|
|
352
|
+
}));
|
|
230
353
|
}
|
|
231
354
|
}
|
|
232
355
|
exports.default = DaumAdapterBase;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { EventLogger } from "gd-eventlog";
|
|
2
|
+
import CyclingMode, { CyclingModeBase, CyclingModeProperty, IncyclistBikeData, UpdateRequest } from "../CyclingMode";
|
|
3
|
+
import DaumAdapter from "./DaumAdapter";
|
|
4
|
+
export declare type ERGEvent = {
|
|
5
|
+
rpmUpdated?: boolean;
|
|
6
|
+
gearUpdated?: boolean;
|
|
7
|
+
starting?: boolean;
|
|
8
|
+
tsStart?: number;
|
|
9
|
+
};
|
|
10
|
+
export default class ERGCyclingMode extends CyclingModeBase implements CyclingMode {
|
|
11
|
+
logger: EventLogger;
|
|
12
|
+
data: IncyclistBikeData;
|
|
13
|
+
prevRequest: UpdateRequest;
|
|
14
|
+
prevUpdateTS: number;
|
|
15
|
+
hasBikeUpdate: boolean;
|
|
16
|
+
chain: number[];
|
|
17
|
+
cassette: number[];
|
|
18
|
+
event: ERGEvent;
|
|
19
|
+
constructor(adapter: DaumAdapter, props?: any);
|
|
20
|
+
getName(): string;
|
|
21
|
+
getDescription(): string;
|
|
22
|
+
getProperties(): CyclingModeProperty[];
|
|
23
|
+
getProperty(name: string): CyclingModeProperty;
|
|
24
|
+
getBikeInitRequest(): UpdateRequest;
|
|
25
|
+
sendBikeUpdate(request: UpdateRequest): UpdateRequest;
|
|
26
|
+
updateData(bikeData: IncyclistBikeData): any;
|
|
27
|
+
calculateTargetPower(request: any, updateMode?: boolean): any;
|
|
28
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const gd_eventlog_1 = require("gd-eventlog");
|
|
7
|
+
const CyclingMode_1 = require("../CyclingMode");
|
|
8
|
+
const calculations_1 = __importDefault(require("../calculations"));
|
|
9
|
+
const config = {
|
|
10
|
+
name: "ERG",
|
|
11
|
+
description: "Calculates speed based on power and slope. Power is either set by workout or calculated based on gear and cadence",
|
|
12
|
+
properties: [
|
|
13
|
+
{ key: 'bikeType', name: 'Bike Type', description: '', type: CyclingMode_1.CyclingModeProperyType.SingleSelect, options: ['Race', 'Mountain', 'Triathlon'], default: 'Race' },
|
|
14
|
+
{ key: 'startPower', name: 'Starting Power', description: 'Initial power in Watts at start of raining', type: CyclingMode_1.CyclingModeProperyType.Integer, default: 50, min: 25, max: 800 },
|
|
15
|
+
]
|
|
16
|
+
};
|
|
17
|
+
class ERGCyclingMode extends CyclingMode_1.CyclingModeBase {
|
|
18
|
+
constructor(adapter, props) {
|
|
19
|
+
super(adapter, props);
|
|
20
|
+
this.prevUpdateTS = 0;
|
|
21
|
+
this.hasBikeUpdate = false;
|
|
22
|
+
this.event = {};
|
|
23
|
+
this.logger = adapter.logger || new gd_eventlog_1.EventLogger('ERGMode');
|
|
24
|
+
this.data = {};
|
|
25
|
+
this.logger.logEvent({ message: 'constructor', props });
|
|
26
|
+
}
|
|
27
|
+
getName() {
|
|
28
|
+
return config.name;
|
|
29
|
+
}
|
|
30
|
+
getDescription() {
|
|
31
|
+
return config.description;
|
|
32
|
+
}
|
|
33
|
+
getProperties() {
|
|
34
|
+
return config.properties;
|
|
35
|
+
}
|
|
36
|
+
getProperty(name) {
|
|
37
|
+
return config.properties.find(p => p.name === name);
|
|
38
|
+
}
|
|
39
|
+
getBikeInitRequest() {
|
|
40
|
+
const startPower = this.getSetting('startPower');
|
|
41
|
+
return { targetPower: startPower };
|
|
42
|
+
}
|
|
43
|
+
sendBikeUpdate(request) {
|
|
44
|
+
const getData = () => {
|
|
45
|
+
if (!this.data)
|
|
46
|
+
return {};
|
|
47
|
+
const { gear, pedalRpm, slope, power, speed } = this.data;
|
|
48
|
+
return { gear, pedalRpm, slope, power, speed };
|
|
49
|
+
};
|
|
50
|
+
this.logger.logEvent({ message: "processing update request", request, prev: this.prevRequest, data: getData(), event: this.event });
|
|
51
|
+
let newRequest = {};
|
|
52
|
+
try {
|
|
53
|
+
if (!request || request.reset || Object.keys(request).length === 0) {
|
|
54
|
+
this.prevRequest = {};
|
|
55
|
+
return request || {};
|
|
56
|
+
}
|
|
57
|
+
const prevData = this.data || {};
|
|
58
|
+
if (request.targetPower !== undefined) {
|
|
59
|
+
delete request.slope;
|
|
60
|
+
delete request.refresh;
|
|
61
|
+
}
|
|
62
|
+
if (this.event.starting && request.targetPower === undefined) {
|
|
63
|
+
const startPower = this.getSetting('startPower');
|
|
64
|
+
if (this.event.tsStart && Date.now() - this.event.tsStart > 5000) {
|
|
65
|
+
delete this.event.starting;
|
|
66
|
+
delete this.event.tsStart;
|
|
67
|
+
}
|
|
68
|
+
const target = this.calculateTargetPower(request);
|
|
69
|
+
if (target <= startPower && (!request.minPower || target >= request.minPower)) {
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
delete this.event.starting;
|
|
74
|
+
delete this.event.tsStart;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (request.refresh) {
|
|
78
|
+
delete request.refresh;
|
|
79
|
+
if (this.prevRequest !== undefined && !this.event.gearUpdated && !this.event.rpmUpdated) {
|
|
80
|
+
newRequest.targetPower = this.prevRequest.targetPower;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
newRequest.targetPower = this.calculateTargetPower(request);
|
|
84
|
+
}
|
|
85
|
+
if (this.prevRequest !== undefined && Object.keys(this.prevRequest).length > 0) {
|
|
86
|
+
request = Object.assign({}, this.prevRequest);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
newRequest.targetPower = this.calculateTargetPower(request);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (request.slope !== undefined) {
|
|
93
|
+
if (!this.data)
|
|
94
|
+
this.data = {};
|
|
95
|
+
this.data.slope = request.slope;
|
|
96
|
+
}
|
|
97
|
+
if (request.maxPower !== undefined && request.minPower !== undefined && request.maxPower === request.minPower) {
|
|
98
|
+
request.targetPower = request.maxPower;
|
|
99
|
+
}
|
|
100
|
+
if (request.targetPower === undefined) {
|
|
101
|
+
newRequest.targetPower = this.calculateTargetPower(request);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
newRequest.targetPower = request.targetPower;
|
|
105
|
+
}
|
|
106
|
+
delete request.slope;
|
|
107
|
+
if (request.maxPower !== undefined) {
|
|
108
|
+
if (newRequest.targetPower !== undefined && newRequest.targetPower > request.maxPower) {
|
|
109
|
+
newRequest.targetPower = request.maxPower;
|
|
110
|
+
}
|
|
111
|
+
newRequest.maxPower = request.maxPower;
|
|
112
|
+
}
|
|
113
|
+
if (request.minPower !== undefined) {
|
|
114
|
+
if (newRequest.targetPower !== undefined && newRequest.targetPower < request.minPower) {
|
|
115
|
+
newRequest.targetPower = request.minPower;
|
|
116
|
+
}
|
|
117
|
+
newRequest.minPower = request.minPower;
|
|
118
|
+
}
|
|
119
|
+
if (newRequest.targetPower !== undefined && prevData.power !== undefined && newRequest.targetPower === prevData.power) {
|
|
120
|
+
delete newRequest.targetPower;
|
|
121
|
+
}
|
|
122
|
+
this.prevRequest = JSON.parse(JSON.stringify(request));
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
this.logger.logEvent({ message: "error", fn: 'sendBikeUpdate()', error: err.message || err, stack: err.stack });
|
|
126
|
+
}
|
|
127
|
+
return newRequest;
|
|
128
|
+
}
|
|
129
|
+
updateData(bikeData) {
|
|
130
|
+
const prevData = JSON.parse(JSON.stringify(this.data || {}));
|
|
131
|
+
const prevSpeed = prevData.speed;
|
|
132
|
+
const prevRequest = this.prevRequest || {};
|
|
133
|
+
const data = this.data || {};
|
|
134
|
+
const bikeType = this.getSetting('bikeType');
|
|
135
|
+
delete this.event.gearUpdated;
|
|
136
|
+
delete this.event.rpmUpdated;
|
|
137
|
+
if (prevData === {} || prevData.speed === undefined || prevData.speed === 0) {
|
|
138
|
+
this.event.starting = true;
|
|
139
|
+
this.event.tsStart = Date.now();
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
let rpm = bikeData.pedalRpm || 0;
|
|
143
|
+
let gear = bikeData.gear || 0;
|
|
144
|
+
let power = bikeData.power || 0;
|
|
145
|
+
let slope = (prevData.slope !== undefined ? prevData.slope : prevRequest.slope || 0);
|
|
146
|
+
let speed;
|
|
147
|
+
let m = this.adapter.getWeight();
|
|
148
|
+
let distanceInternal = prevData.distanceInternal || 0;
|
|
149
|
+
let distance = Math.round(distanceInternal / 100);
|
|
150
|
+
let ts = Date.now();
|
|
151
|
+
let duration = this.prevUpdateTS === 0 ? 0 : ((ts - this.prevUpdateTS) / 1000);
|
|
152
|
+
if (rpm === 0 || bikeData.isPedalling === false) {
|
|
153
|
+
speed = 0;
|
|
154
|
+
power = 0;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
speed = calculations_1.default.calculateSpeed(m, power, slope, { bikeType });
|
|
158
|
+
let v = speed / 3.6;
|
|
159
|
+
distanceInternal += Math.round(v * duration);
|
|
160
|
+
distance = Math.round(distanceInternal / 100);
|
|
161
|
+
}
|
|
162
|
+
data.speed = parseFloat(speed.toFixed(1));
|
|
163
|
+
data.power = Math.round(power);
|
|
164
|
+
data.distanceInternal = Math.round(distanceInternal);
|
|
165
|
+
data.distance = distance;
|
|
166
|
+
data.slope = slope;
|
|
167
|
+
data.pedalRpm = rpm;
|
|
168
|
+
data.gear = gear;
|
|
169
|
+
if (data.time)
|
|
170
|
+
data.time += duration;
|
|
171
|
+
else
|
|
172
|
+
data.time = 0;
|
|
173
|
+
data.heartrate = bikeData.heartrate;
|
|
174
|
+
data.isPedalling = bikeData.isPedalling;
|
|
175
|
+
if (gear !== prevData.gear) {
|
|
176
|
+
this.event.gearUpdated = true;
|
|
177
|
+
}
|
|
178
|
+
if (rpm && rpm !== prevData.pedalRpm) {
|
|
179
|
+
this.event.rpmUpdated = true;
|
|
180
|
+
}
|
|
181
|
+
this.prevUpdateTS = ts;
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
this.logger.logEvent({ message: 'error', fn: 'updateData()', error: err.message || err });
|
|
185
|
+
}
|
|
186
|
+
this.logger.logEvent({ message: "updateData result", data, bikeData, prevRequest, prevSpeed });
|
|
187
|
+
this.data = data;
|
|
188
|
+
return data;
|
|
189
|
+
}
|
|
190
|
+
calculateTargetPower(request, updateMode = true) {
|
|
191
|
+
const bikeType = this.getSetting('bikeType').toLowerCase();
|
|
192
|
+
const defaultPower = this.getSetting('startPower');
|
|
193
|
+
let m = this.adapter.getWeight();
|
|
194
|
+
const prevData = this.data || {};
|
|
195
|
+
let target;
|
|
196
|
+
if (prevData.pedalRpm && prevData.gear && (!updateMode || prevData.pedalRpm !== 0)) {
|
|
197
|
+
const speed = calculations_1.default.calculateSpeedDaum(prevData.gear, prevData.pedalRpm, bikeType);
|
|
198
|
+
var power = calculations_1.default.calculatePower(m, speed / 3.6, 0, { bikeType });
|
|
199
|
+
target = Math.round(power);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
target = Math.round(request.targetPower || defaultPower);
|
|
203
|
+
}
|
|
204
|
+
return target;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
exports.default = ERGCyclingMode;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { EventLogger } from "gd-eventlog";
|
|
2
|
+
import CyclingMode, { CyclingModeProperty, IncyclistBikeData, Settings, UpdateRequest, CyclingModeBase } from "../CyclingMode";
|
|
3
|
+
import DaumAdapter from "./DaumAdapter";
|
|
4
|
+
export default class PowerMeterCyclingMode extends CyclingModeBase implements CyclingMode {
|
|
5
|
+
logger: EventLogger;
|
|
6
|
+
data: IncyclistBikeData;
|
|
7
|
+
prevRequest: UpdateRequest;
|
|
8
|
+
prevUpdateTS: number;
|
|
9
|
+
hasBikeUpdate: boolean;
|
|
10
|
+
constructor(adapter: DaumAdapter, props?: Settings);
|
|
11
|
+
getName(): string;
|
|
12
|
+
getDescription(): string;
|
|
13
|
+
getProperties(): CyclingModeProperty[];
|
|
14
|
+
getProperty(name: string): CyclingModeProperty;
|
|
15
|
+
getBikeInitRequest(): UpdateRequest;
|
|
16
|
+
sendBikeUpdate(request: UpdateRequest): UpdateRequest;
|
|
17
|
+
updateData(data: IncyclistBikeData): IncyclistBikeData;
|
|
18
|
+
}
|