incyclist-devices 2.2.8 → 2.2.10-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/antv2/base/adapter.js +3 -1
- package/lib/antv2/base/interface.d.ts +2 -0
- package/lib/antv2/base/interface.js +37 -3
- package/lib/modes/antble-smarttrainer.d.ts +16 -2
- package/lib/modes/antble-smarttrainer.js +98 -1
- package/lib/modes/types.d.ts +5 -1
- package/lib/utils/calculations.d.ts +1 -0
- package/lib/utils/calculations.js +22 -6
- package/package.json +1 -1
|
@@ -438,8 +438,10 @@ class AntAdapter extends adpater_1.default {
|
|
|
438
438
|
stop() {
|
|
439
439
|
return __awaiter(this, void 0, void 0, function* () {
|
|
440
440
|
let stopped;
|
|
441
|
-
this.logger.logEvent({ message: 'stopping device', device: this.getName() });
|
|
442
441
|
this.internalEmitter.emit('stop');
|
|
442
|
+
if (this.stopped)
|
|
443
|
+
return;
|
|
444
|
+
this.logger.logEvent({ message: 'stopping device', device: this.getName() });
|
|
443
445
|
this.promiseWaitForData = null;
|
|
444
446
|
if (this.startStatus) {
|
|
445
447
|
this.startStatus.interrupted = true;
|
|
@@ -45,6 +45,8 @@ export default class AntInterface extends EventEmitter implements IncyclistInter
|
|
|
45
45
|
scannerWaitForConnection(): Promise<void>;
|
|
46
46
|
scan(props?: AntScanProps): Promise<AntDeviceSettings[]>;
|
|
47
47
|
isScanning(): boolean;
|
|
48
|
+
protected stopAllSensors(sensors: Array<ISensor>): Promise<void>;
|
|
49
|
+
protected stopDevices(detected: AntDeviceSettings[]): Promise<void>;
|
|
48
50
|
stopScan(): Promise<boolean>;
|
|
49
51
|
startSensor(sensor: ISensor, onDeviceData: (data: any) => void): Promise<boolean>;
|
|
50
52
|
private blockChannel;
|
|
@@ -16,6 +16,7 @@ const events_1 = __importDefault(require("events"));
|
|
|
16
16
|
const gd_eventlog_1 = require("gd-eventlog");
|
|
17
17
|
const sensor_factory_1 = __importDefault(require("../factories/sensor-factory"));
|
|
18
18
|
const utils_1 = require("../../utils/utils");
|
|
19
|
+
const incyclist_devices_1 = require("incyclist-devices");
|
|
19
20
|
class AntInterface extends events_1.default {
|
|
20
21
|
static getInstance(props = {}) {
|
|
21
22
|
if (AntInterface._instance === undefined)
|
|
@@ -237,6 +238,8 @@ class AntInterface extends events_1.default {
|
|
|
237
238
|
this.activeScan.emitter.on('stop', () => __awaiter(this, void 0, void 0, function* () {
|
|
238
239
|
this.activeScan.emitter.removeAllListeners();
|
|
239
240
|
this.emit('stop-scan');
|
|
241
|
+
yield this.stopDevices(detected);
|
|
242
|
+
yield this.stopAllSensors(sensors);
|
|
240
243
|
const stopped = yield this.activeScan.channel.stopScanner();
|
|
241
244
|
this.logEvent({ message: 'scan stopped' });
|
|
242
245
|
removeListeners(channel);
|
|
@@ -276,6 +279,38 @@ class AntInterface extends events_1.default {
|
|
|
276
279
|
isScanning() {
|
|
277
280
|
return this.scanPromise !== undefined && this.scanPromise !== null;
|
|
278
281
|
}
|
|
282
|
+
stopAllSensors(sensors) {
|
|
283
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
284
|
+
this.logger.logEvent({ message: 'stopping all sensors ' });
|
|
285
|
+
let promises = [];
|
|
286
|
+
sensors.forEach((sensor) => {
|
|
287
|
+
promises.push(this.stopSensor(sensor).catch(err => {
|
|
288
|
+
var _a;
|
|
289
|
+
this.logger.logEvent({ message: 'could not stop sensor', error: err.message, channel: (_a = sensor.getChannel()) === null || _a === void 0 ? void 0 : _a.getChannelNo(), stack: err.stack });
|
|
290
|
+
}));
|
|
291
|
+
});
|
|
292
|
+
if (promises.length > 0) {
|
|
293
|
+
yield Promise.allSettled(promises);
|
|
294
|
+
}
|
|
295
|
+
this.logger.logEvent({ message: 'sensors stopped' });
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
stopDevices(detected) {
|
|
299
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
300
|
+
this.logger.logEvent({ message: 'stopping devices' });
|
|
301
|
+
let promises = [];
|
|
302
|
+
detected.forEach((settings) => {
|
|
303
|
+
const adapter = incyclist_devices_1.AdapterFactory.create(settings);
|
|
304
|
+
promises.push(adapter.stop().catch(err => {
|
|
305
|
+
this.logger.logEvent({ message: 'could not stop device', error: err.message, deviceID: settings.deviceID, stack: err.stack });
|
|
306
|
+
}));
|
|
307
|
+
});
|
|
308
|
+
if (promises.length > 0) {
|
|
309
|
+
yield Promise.allSettled(promises);
|
|
310
|
+
}
|
|
311
|
+
this.logger.logEvent({ message: 'devices stopped' });
|
|
312
|
+
});
|
|
313
|
+
}
|
|
279
314
|
stopScan() {
|
|
280
315
|
return __awaiter(this, void 0, void 0, function* () {
|
|
281
316
|
this.logEvent({ message: 'stopping scan ..' });
|
|
@@ -348,7 +383,7 @@ class AntInterface extends events_1.default {
|
|
|
348
383
|
return true;
|
|
349
384
|
}
|
|
350
385
|
const channel = sensor.getChannel();
|
|
351
|
-
if (channel) {
|
|
386
|
+
if (channel !== undefined) {
|
|
352
387
|
try {
|
|
353
388
|
if (!channel.flush) {
|
|
354
389
|
this.logEvent({ message: 'old version of ant-channel detected' });
|
|
@@ -371,8 +406,7 @@ class AntInterface extends events_1.default {
|
|
|
371
406
|
}
|
|
372
407
|
}
|
|
373
408
|
else {
|
|
374
|
-
|
|
375
|
-
return false;
|
|
409
|
+
return true;
|
|
376
410
|
}
|
|
377
411
|
});
|
|
378
412
|
}
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import ICyclingMode, { CyclingModeProperyType, UpdateRequest } from "./types";
|
|
1
|
+
import ICyclingMode, { CyclingModeProperyType, IVirtualShifting, UpdateRequest } from "./types";
|
|
2
2
|
import PowerBasedCyclingModeBase from "./power-base";
|
|
3
3
|
import { IAdapter } from "../types";
|
|
4
|
-
export
|
|
4
|
+
export type GearConfigEntry = {
|
|
5
|
+
chainConfig: number[];
|
|
6
|
+
cassetteConfig: number[];
|
|
7
|
+
wheelCirc: number;
|
|
8
|
+
numGears: number;
|
|
9
|
+
};
|
|
10
|
+
export default class SmartTrainerCyclingMode extends PowerBasedCyclingModeBase implements ICyclingMode, IVirtualShifting {
|
|
5
11
|
protected static config: {
|
|
6
12
|
name: string;
|
|
7
13
|
isSIM: boolean;
|
|
@@ -26,10 +32,18 @@ export default class SmartTrainerCyclingMode extends PowerBasedCyclingModeBase i
|
|
|
26
32
|
options?: undefined;
|
|
27
33
|
})[];
|
|
28
34
|
};
|
|
35
|
+
protected gear: any;
|
|
29
36
|
constructor(adapter: IAdapter, props?: any);
|
|
30
37
|
getBikeInitRequest(): UpdateRequest;
|
|
31
38
|
checkForResetOrEmpty(request: UpdateRequest): UpdateRequest | undefined;
|
|
32
39
|
protected checkSlope(request: UpdateRequest, newRequest?: UpdateRequest): void;
|
|
33
40
|
protected checkEmptyRequest(newRequest: UpdateRequest): void;
|
|
34
41
|
sendBikeUpdate(incoming: UpdateRequest): UpdateRequest;
|
|
42
|
+
initGears(): Promise<number>;
|
|
43
|
+
gearUp(numGears: number): Promise<number>;
|
|
44
|
+
protected switchGear: (gear: any, prevGear: any) => number;
|
|
45
|
+
protected findBestGear(): any;
|
|
46
|
+
getCurrentGearConfig(): GearConfigEntry;
|
|
47
|
+
protected getDefaultCadence(): number;
|
|
48
|
+
protected getDefaultPower(): number;
|
|
35
49
|
}
|
|
@@ -1,13 +1,45 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
2
11
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
13
|
};
|
|
5
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
15
|
const types_1 = require("./types");
|
|
7
16
|
const power_base_1 = __importDefault(require("./power-base"));
|
|
17
|
+
const calculations_1 = __importDefault(require("../utils/calculations"));
|
|
18
|
+
const NUM_GEARS = 30;
|
|
19
|
+
const GearConfig = {
|
|
20
|
+
race: { chainConfig: [34, 50], cassetteConfig: [11, 36], wheelCirc: 2125, numGears: NUM_GEARS },
|
|
21
|
+
mountain: { chainConfig: [26, 36], cassetteConfig: [10, 44], wheelCirc: 2344, numGears: NUM_GEARS },
|
|
22
|
+
triathlon: { chainConfig: [36, 52], cassetteConfig: [11, 30], wheelCirc: 2125, numGears: NUM_GEARS },
|
|
23
|
+
};
|
|
8
24
|
class SmartTrainerCyclingMode extends power_base_1.default {
|
|
9
25
|
constructor(adapter, props) {
|
|
10
26
|
super(adapter, props);
|
|
27
|
+
this.switchGear = (gear, prevGear) => {
|
|
28
|
+
var _a;
|
|
29
|
+
const { chainConfig, cassetteConfig, wheelCirc, numGears } = this.getCurrentGearConfig();
|
|
30
|
+
const { pedalRpm, speed, power, slope } = (_a = this.getData()) !== null && _a !== void 0 ? _a : {};
|
|
31
|
+
const m = this.getWeight();
|
|
32
|
+
let cadence = pedalRpm !== null && pedalRpm !== void 0 ? pedalRpm : this.getDefaultCadence();
|
|
33
|
+
let currentPower = power !== null && power !== void 0 ? power : this.getDefaultPower();
|
|
34
|
+
const prevGearSpeed = calculations_1.default.calculateSpeedBike(prevGear, cadence, chainConfig, cassetteConfig, { numGears, wheelCirc });
|
|
35
|
+
const targetGearSpeed = calculations_1.default.calculateSpeedBike(gear, cadence, chainConfig, cassetteConfig, { numGears, wheelCirc });
|
|
36
|
+
const targetGearPower = calculations_1.default.calculatePower(m, targetGearSpeed / 3.6, 0);
|
|
37
|
+
const prevGearPower = calculations_1.default.calculatePower(m, prevGearSpeed / 3.6, 0);
|
|
38
|
+
const targetPower = currentPower + (targetGearPower - prevGearPower);
|
|
39
|
+
const slopeTarget = calculations_1.default.calculateSlope(m, targetPower, speed / 3.6);
|
|
40
|
+
console.log('Gear', gear, 'power', targetPower, 'speed', speed.toFixed(1), 'slope', slope.toFixed(1), 'virtual slope', slopeTarget.toFixed(1));
|
|
41
|
+
return slopeTarget;
|
|
42
|
+
};
|
|
11
43
|
this.initLogger('SmartTrainerMode');
|
|
12
44
|
}
|
|
13
45
|
getBikeInitRequest() {
|
|
@@ -28,11 +60,16 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
28
60
|
newRequest.slope = parseFloat(request.slope.toFixed(1));
|
|
29
61
|
this.data.slope = newRequest.slope;
|
|
30
62
|
try {
|
|
63
|
+
if (this.gear !== undefined) {
|
|
64
|
+
const slopeTarget = this.switchGear(this.gear, this.gear);
|
|
65
|
+
newRequest.slope = slopeTarget;
|
|
66
|
+
}
|
|
31
67
|
const slopeAdj = this.getSetting('slopeAdj');
|
|
32
68
|
if (slopeAdj !== undefined)
|
|
33
69
|
newRequest.slope = newRequest.slope * slopeAdj / 100;
|
|
34
70
|
}
|
|
35
|
-
catch (
|
|
71
|
+
catch (err) {
|
|
72
|
+
this.logger.logEvent({ message: "error", fn: '', error: err.message, request, prev: this.prevRequest, data: this.getData() });
|
|
36
73
|
}
|
|
37
74
|
}
|
|
38
75
|
}
|
|
@@ -61,6 +98,66 @@ class SmartTrainerCyclingMode extends power_base_1.default {
|
|
|
61
98
|
}
|
|
62
99
|
return newRequest;
|
|
63
100
|
}
|
|
101
|
+
initGears() {
|
|
102
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
103
|
+
this.gear = this.findBestGear();
|
|
104
|
+
return this.gear;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
gearUp(numGears) {
|
|
108
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
109
|
+
let target = this.gear + numGears;
|
|
110
|
+
if (target > NUM_GEARS)
|
|
111
|
+
target = NUM_GEARS;
|
|
112
|
+
if (target < 1)
|
|
113
|
+
target = 1;
|
|
114
|
+
this.gear = target;
|
|
115
|
+
return this.gear;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
findBestGear() {
|
|
119
|
+
var _a;
|
|
120
|
+
const { chainConfig, cassetteConfig, wheelCirc, numGears } = this.getCurrentGearConfig();
|
|
121
|
+
const { pedalRpm, speed, power, slope } = (_a = this.getData()) !== null && _a !== void 0 ? _a : {};
|
|
122
|
+
const m = this.getWeight();
|
|
123
|
+
let cadence = pedalRpm !== null && pedalRpm !== void 0 ? pedalRpm : this.getDefaultCadence();
|
|
124
|
+
let currentPower = power !== null && power !== void 0 ? power : this.getDefaultPower();
|
|
125
|
+
let minDiff, gearInitial;
|
|
126
|
+
if (speed === 0) {
|
|
127
|
+
const speedInitial = calculations_1.default.calculateSpeed(m, currentPower, slope);
|
|
128
|
+
for (let i = 1; i <= numGears; i++) {
|
|
129
|
+
const speed = calculations_1.default.calculateSpeedBike(i, cadence, chainConfig, cassetteConfig, { numGears, wheelCirc });
|
|
130
|
+
const diff = Math.abs(speed - speedInitial);
|
|
131
|
+
if (!minDiff || diff < minDiff) {
|
|
132
|
+
minDiff = diff;
|
|
133
|
+
gearInitial = i;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
for (let i = 1; i <= numGears; i++) {
|
|
139
|
+
const speed = calculations_1.default.calculateSpeedBike(i, cadence, chainConfig, cassetteConfig, { numGears, wheelCirc });
|
|
140
|
+
const targetPower = calculations_1.default.calculatePower(m, speed / 3.6, slope);
|
|
141
|
+
const diff = Math.abs(currentPower - targetPower);
|
|
142
|
+
if (!minDiff || diff < minDiff) {
|
|
143
|
+
minDiff = diff;
|
|
144
|
+
gearInitial = i;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return gearInitial;
|
|
149
|
+
}
|
|
150
|
+
getCurrentGearConfig() {
|
|
151
|
+
var _a;
|
|
152
|
+
const type = (_a = this.getSetting('bikeType')) !== null && _a !== void 0 ? _a : 'Race';
|
|
153
|
+
return GearConfig[type.toLowerCase()];
|
|
154
|
+
}
|
|
155
|
+
getDefaultCadence() {
|
|
156
|
+
return 90;
|
|
157
|
+
}
|
|
158
|
+
getDefaultPower() {
|
|
159
|
+
return 120;
|
|
160
|
+
}
|
|
64
161
|
}
|
|
65
162
|
SmartTrainerCyclingMode.config = {
|
|
66
163
|
name: "Smart Trainer",
|
package/lib/modes/types.d.ts
CHANGED
|
@@ -47,6 +47,10 @@ export default interface ICyclingMode {
|
|
|
47
47
|
setModeProperty(name: string, value: any): void;
|
|
48
48
|
getModeProperty(name: string): any;
|
|
49
49
|
}
|
|
50
|
+
export interface IVirtualShifting {
|
|
51
|
+
initGears(): Promise<number>;
|
|
52
|
+
gearUp(numGears: number): Promise<number>;
|
|
53
|
+
}
|
|
50
54
|
export type CyclingModeConfig = {
|
|
51
55
|
isERG?: boolean;
|
|
52
56
|
isSIM?: boolean;
|
|
@@ -69,7 +73,7 @@ export declare class CyclingMode implements ICyclingMode {
|
|
|
69
73
|
getSetting(_name: string): void;
|
|
70
74
|
getSettings(): Settings;
|
|
71
75
|
setModeProperty(_name: string, _value: any): void;
|
|
72
|
-
getModeProperty(_name: string):
|
|
76
|
+
getModeProperty(_name: string): any;
|
|
73
77
|
getConfig(): CyclingModeConfig;
|
|
74
78
|
isERG(): boolean;
|
|
75
79
|
isSIM(): boolean;
|
|
@@ -3,6 +3,7 @@ export declare class IllegalArgumentException extends Error {
|
|
|
3
3
|
}
|
|
4
4
|
export default class C {
|
|
5
5
|
static calculateSpeed(m: number, power: number, slope: number, props?: any): number;
|
|
6
|
+
static calculateSlope(m: number, P: number, v: number, props?: any): number;
|
|
6
7
|
static calculatePower(m: number, v: number, slope: number, props?: any): number;
|
|
7
8
|
static calculateSpeedDaum(gear: number, rpm: number, bikeType?: string | number): number;
|
|
8
9
|
static calculateSpeedBike(gear: number, rpm: number, chain: number[], cassette: number[], props?: {
|
|
@@ -19,13 +19,14 @@ class IllegalArgumentException extends Error {
|
|
|
19
19
|
exports.IllegalArgumentException = IllegalArgumentException;
|
|
20
20
|
class C {
|
|
21
21
|
static calculateSpeed(m, power, slope, props = {}) {
|
|
22
|
+
var _a, _b, _c, _d;
|
|
22
23
|
if (m === undefined || m === null || m < 0)
|
|
23
24
|
throw new IllegalArgumentException("m must be a positive number");
|
|
24
25
|
if (power === undefined || power === null || power < 0)
|
|
25
26
|
throw new IllegalArgumentException("power must be a positive number");
|
|
26
|
-
const _rho = props.rho
|
|
27
|
-
const _cRR = props.cRR
|
|
28
|
-
const _cwA = props.cwA
|
|
27
|
+
const _rho = (_a = props.rho) !== null && _a !== void 0 ? _a : rho;
|
|
28
|
+
const _cRR = (_b = props.cRR) !== null && _b !== void 0 ? _b : cRR;
|
|
29
|
+
const _cwA = (_c = props.cwA) !== null && _c !== void 0 ? _c : ((_d = cwABike[props.bikeType || 'race']) !== null && _d !== void 0 ? _d : cwABike.race);
|
|
29
30
|
let sl = Math.atan(slope / 100);
|
|
30
31
|
let c1 = 0.5 * _rho * _cwA;
|
|
31
32
|
let c2 = (sl + _cRR) * m * g;
|
|
@@ -58,14 +59,29 @@ class C {
|
|
|
58
59
|
}
|
|
59
60
|
return 0;
|
|
60
61
|
}
|
|
62
|
+
static calculateSlope(m, P, v, props = {}) {
|
|
63
|
+
var _a, _b, _c;
|
|
64
|
+
if (m === undefined || m === null || m < 0)
|
|
65
|
+
throw new IllegalArgumentException("m must be a positive number");
|
|
66
|
+
if (P === undefined || P === null || P < 0)
|
|
67
|
+
throw new IllegalArgumentException("power must be a positive number");
|
|
68
|
+
if (v === undefined || v === null || v === 0)
|
|
69
|
+
throw new IllegalArgumentException("v must be a positive number");
|
|
70
|
+
const _rho = (_a = props.rho) !== null && _a !== void 0 ? _a : rho;
|
|
71
|
+
const _cRR = (_b = props.cRR) !== null && _b !== void 0 ? _b : cRR;
|
|
72
|
+
const _cwA = (_c = props.cwA) !== null && _c !== void 0 ? _c : (cwABike[props.bikeType || 'race'] || cwABike.race);
|
|
73
|
+
const sl = (P - (0.5 * _rho * _cwA) * Math.pow(v, 3.0)) / (m * g * v) - _cRR;
|
|
74
|
+
return Math.tan(Math.asin(sl)) * 100;
|
|
75
|
+
}
|
|
61
76
|
static calculatePower(m, v, slope, props = {}) {
|
|
77
|
+
var _a, _b, _c;
|
|
62
78
|
if (m === undefined || m === null || m < 0)
|
|
63
79
|
throw new IllegalArgumentException("m must be a positive number");
|
|
64
80
|
if (v === undefined || v === null || v < 0)
|
|
65
81
|
throw new IllegalArgumentException("v must be a positive number");
|
|
66
|
-
let _rho = props.rho
|
|
67
|
-
let _cRR = props.cRR
|
|
68
|
-
let _cwA = props.cwA
|
|
82
|
+
let _rho = (_a = props.rho) !== null && _a !== void 0 ? _a : rho;
|
|
83
|
+
let _cRR = (_b = props.cRR) !== null && _b !== void 0 ? _b : cRR;
|
|
84
|
+
let _cwA = (_c = props.cwA) !== null && _c !== void 0 ? _c : cwABike[props.bikeType || 'race'];
|
|
69
85
|
let sl = Math.sin(Math.atan(slope / 100));
|
|
70
86
|
let P = (0.5 * _rho * _cwA) * Math.pow(v, 3.0) + (sl + _cRR) * m * g * v;
|
|
71
87
|
return P;
|