incyclist-devices 1.2.0 → 1.4.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/LICENSE +21 -0
- 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 +9 -2
- package/lib/ant/AntAdapter.js +26 -2
- package/lib/ant/AntScanner.d.ts +16 -6
- package/lib/ant/AntScanner.js +379 -138
- package/lib/ant/antfe/AntFEAdapter.d.ts +4 -2
- package/lib/ant/antfe/AntFEAdapter.js +209 -72
- package/lib/ant/anthrm/AntHrmAdapter.d.ts +4 -1
- package/lib/ant/anthrm/AntHrmAdapter.js +73 -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 +39 -6
- 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 +16 -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 +49 -8
- package/lib/daum/premium/bike.d.ts +63 -52
- package/lib/daum/premium/bike.js +258 -207
- package/lib/daum/premium/tcpserial.d.ts +18 -14
- package/lib/daum/premium/tcpserial.js +50 -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/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
|
@@ -12,6 +12,7 @@ export default class AntFEAdapter extends AntAdapter {
|
|
|
12
12
|
isBike(): boolean;
|
|
13
13
|
isHrm(): boolean;
|
|
14
14
|
isPower(): boolean;
|
|
15
|
+
getProfile(): string;
|
|
15
16
|
getName(): string;
|
|
16
17
|
getDisplayName(): string;
|
|
17
18
|
onAttached(): void;
|
|
@@ -19,10 +20,11 @@ export default class AntFEAdapter extends AntAdapter {
|
|
|
19
20
|
onDeviceEvent(data: any): void;
|
|
20
21
|
updateData(data: any, deviceData: any): any;
|
|
21
22
|
transformData(bikeData: any): any;
|
|
22
|
-
start(props?: any): Promise<
|
|
23
|
+
start(props?: any): Promise<any>;
|
|
23
24
|
stop(): Promise<boolean>;
|
|
24
25
|
sendUpdate(request: any): Promise<void>;
|
|
25
|
-
send(msg: any, logStr: any, callback?: any): void;
|
|
26
|
+
send(msg: any, logStr: any, callback?: any, expectedResponse?: any): void;
|
|
27
|
+
sendAsync(msg: any, logStr: any, expectedResponse: any): Promise<unknown>;
|
|
26
28
|
startWorker(): void;
|
|
27
29
|
stopWorker(): void;
|
|
28
30
|
sendFromQueue(): void;
|
|
@@ -8,19 +8,27 @@ 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
15
|
const gd_eventlog_1 = require("gd-eventlog");
|
|
13
|
-
const AntAdapter_1 = require("../AntAdapter");
|
|
16
|
+
const AntAdapter_1 = __importDefault(require("../AntAdapter"));
|
|
14
17
|
const utils_1 = require("../utils");
|
|
15
18
|
const utils_2 = require("../../utils");
|
|
16
19
|
const floatVal = (d) => d ? parseFloat(d) : d;
|
|
17
20
|
const intVal = (d) => d ? parseInt(d) : d;
|
|
18
|
-
const
|
|
19
|
-
const
|
|
21
|
+
const hex = (v) => Math.abs(v).toString(16).toUpperCase();
|
|
22
|
+
const TIMEOUT_ACK = 5000;
|
|
23
|
+
const TIMEOUT_START = 10000;
|
|
24
|
+
const TIMEOUT_ATTACH = 3000;
|
|
25
|
+
const DEFAULT_USER_WEIGHT = 75;
|
|
26
|
+
const DEFAULT_BIKE_WEIGHT = 12.75;
|
|
20
27
|
class AntFEAdapter extends AntAdapter_1.default {
|
|
21
28
|
constructor(DeviceID, port, stick, protocol) {
|
|
22
29
|
super(protocol);
|
|
23
30
|
this.logger = new gd_eventlog_1.EventLogger('Ant+FE');
|
|
31
|
+
this.logger.logEvent({ message: 'Ant+FE Adapter created', DeviceID, port });
|
|
24
32
|
this.deviceID = DeviceID;
|
|
25
33
|
this.port = port;
|
|
26
34
|
this.stick = stick;
|
|
@@ -35,35 +43,29 @@ class AntFEAdapter extends AntAdapter_1.default {
|
|
|
35
43
|
isBike() { return true; }
|
|
36
44
|
isHrm() { return this.deviceData.HeartRate !== undefined; }
|
|
37
45
|
isPower() { return true; }
|
|
46
|
+
getProfile() {
|
|
47
|
+
return 'Smart Trainer';
|
|
48
|
+
}
|
|
38
49
|
getName() {
|
|
39
50
|
return `Ant+FE ${this.deviceID}`;
|
|
40
51
|
}
|
|
41
52
|
getDisplayName() {
|
|
42
53
|
const { DeviceID, ManId, ComputedHeartRate } = this.deviceData;
|
|
43
54
|
const hrmStr = ComputedHeartRate ? ` (${ComputedHeartRate})` : '';
|
|
44
|
-
return `${utils_1.getBrand(ManId)} FE ${DeviceID}${hrmStr}`;
|
|
55
|
+
return `${(0, utils_1.getBrand)(ManId)} FE ${DeviceID}${hrmStr}`;
|
|
45
56
|
}
|
|
46
57
|
onAttached() {
|
|
47
|
-
|
|
48
|
-
const Messages = this.getProtocol().getAnt().Messages;
|
|
49
|
-
const stick = this.stick;
|
|
50
|
-
const channel = this.channel;
|
|
51
|
-
const { DeviceID } = this.deviceData;
|
|
52
|
-
stick.write(Messages.assignChannel(channel, "transmit"));
|
|
53
|
-
stick.write(Messages.setFrequency(channel, 57));
|
|
54
|
-
stick.write(Messages.setPeriod(channel, 8192));
|
|
55
|
-
stick.write(Messages.setDevice(channel, DeviceID, 0x11, 0x0));
|
|
56
|
-
stick.write(Messages.openChannel(channel));
|
|
58
|
+
this.logger.logEvent({ message: 'Device connected' });
|
|
57
59
|
this.connected = true;
|
|
58
60
|
}
|
|
59
61
|
onDeviceData(deviceData) {
|
|
60
|
-
if (!this.started)
|
|
62
|
+
if (!this.started || this.isStopped())
|
|
61
63
|
return;
|
|
62
64
|
this.deviceData = deviceData;
|
|
63
65
|
try {
|
|
64
66
|
if (this.onDataFn && !(this.ignoreHrm && this.ignoreBike && this.ignorePower) && !this.paused) {
|
|
65
67
|
if (!this.lastUpdate || (Date.now() - this.lastUpdate) > this.updateFrequency) {
|
|
66
|
-
|
|
68
|
+
this.logger.logEvent({ message: 'onDeviceData', data: deviceData });
|
|
67
69
|
this.data = this.updateData(this.data, deviceData);
|
|
68
70
|
const data = this.transformData(this.data);
|
|
69
71
|
this.onDataFn(data);
|
|
@@ -76,24 +78,45 @@ class AntFEAdapter extends AntAdapter_1.default {
|
|
|
76
78
|
}
|
|
77
79
|
onDeviceEvent(data) {
|
|
78
80
|
try {
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
if (stick.currentCmd !== undefined && data.message === 1 && data.code === Constants.EVENT_TRANSFER_TX_COMPLETED) {
|
|
82
|
-
stick.currentCmd.response = { success: true };
|
|
81
|
+
const cmdInfo = this.currentCmd;
|
|
82
|
+
if (!cmdInfo)
|
|
83
83
|
return;
|
|
84
|
+
const msg = cmdInfo.msg.readUInt8(2);
|
|
85
|
+
const Constants = this.getProtocol().getAnt().Constants;
|
|
86
|
+
const { expectedResponse } = cmdInfo;
|
|
87
|
+
if (data.message === msg) {
|
|
88
|
+
if (expectedResponse === undefined && data.code === Constants.EVENT_TRANSFER_TX_COMPLETED) {
|
|
89
|
+
this.currentCmd.response = { success: true, message: hex(data.message), code: hex(data.code) };
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (expectedResponse === undefined && data.code !== Constants.EVENT_TRANSFER_TX_COMPLETED) {
|
|
93
|
+
this.currentCmd.response = { success: false, message: hex(data.message), code: hex(data.code) };
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
84
96
|
}
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
|
|
97
|
+
if (data.message === 1) {
|
|
98
|
+
if (expectedResponse !== undefined && data.code === expectedResponse) {
|
|
99
|
+
this.currentCmd.response = { success: true, message: hex(data.message), code: hex(data.code) };
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (expectedResponse === undefined && (data.code === Constants.EVENT_TRANSFER_TX_COMPLETED || data.code === 3)) {
|
|
103
|
+
this.currentCmd.response = { success: true, message: hex(data.message), code: hex(data.code) };
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (data.code === Constants.EVENT_TRANSFER_TX_FAILED || data.code === Constants.EVENT_CHANNEL_COLLISION) {
|
|
107
|
+
this.currentCmd.response = { success: false, message: hex(data.message), code: hex(data.code) };
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
88
110
|
}
|
|
89
|
-
if (
|
|
111
|
+
if (this.currentCmd !== undefined && data.message === Constants.MESSAGE_CHANNEL_ACKNOWLEDGED_DATA && data.code === 31) {
|
|
90
112
|
this.logger.log("could not send (TRANSFER_IN_PROGRESS)");
|
|
113
|
+
this.currentCmd.response = { success: false, message: hex(data.message), code: hex(data.code) };
|
|
91
114
|
return;
|
|
92
115
|
}
|
|
93
|
-
this.logger.logEvent({ message: "Incoming Event ", event: data });
|
|
116
|
+
this.logger.logEvent({ message: "Incoming Event ", event: { message: hex(data.message), code: hex(data.code) } });
|
|
94
117
|
}
|
|
95
118
|
catch (err) {
|
|
96
|
-
this.logger.logEvent({ message: 'Error', fn: 'parseEvent', data:
|
|
119
|
+
this.logger.logEvent({ message: 'Error', fn: 'parseEvent', event: { message: hex(data.message), code: hex(data.code) }, error: err.message || err });
|
|
97
120
|
}
|
|
98
121
|
}
|
|
99
122
|
updateData(data, deviceData) {
|
|
@@ -104,10 +127,18 @@ class AntFEAdapter extends AntAdapter_1.default {
|
|
|
104
127
|
data.power = (deviceData.InstantaneousPower !== undefined ? deviceData.InstantaneousPower : data.power);
|
|
105
128
|
data.pedalRpm = (deviceData.Cadence !== undefined ? deviceData.Cadence : data.pedalRpm);
|
|
106
129
|
data.heartrate = (deviceData.HeartRate !== undefined ? deviceData.HeartRate : data.heartrate);
|
|
107
|
-
if (deviceData.Distance !== undefined) {
|
|
130
|
+
if (deviceData.Distance !== undefined && deviceData.Distance !== 0) {
|
|
108
131
|
data.distanceInternal = deviceData.Distance - data.distanceOffs;
|
|
109
132
|
data.distance = data.distanceInternal / 1000;
|
|
110
133
|
}
|
|
134
|
+
else {
|
|
135
|
+
if (this.lastUpdate && deviceData.Cadence !== undefined && deviceData.Cadence > 0 && data.speed) {
|
|
136
|
+
const t = (Date.now() - this.lastUpdate) / 1000;
|
|
137
|
+
const prevDistance = data.distanceInternal || 0;
|
|
138
|
+
data.distanceInternal = Math.round(data.speed / 3.6 * t) + prevDistance;
|
|
139
|
+
data.distance = data.distanceInternal / 1000;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
111
142
|
return data;
|
|
112
143
|
}
|
|
113
144
|
transformData(bikeData) {
|
|
@@ -140,89 +171,156 @@ class AntFEAdapter extends AntAdapter_1.default {
|
|
|
140
171
|
return data;
|
|
141
172
|
}
|
|
142
173
|
start(props) {
|
|
174
|
+
const _super = Object.create(null, {
|
|
175
|
+
start: { get: () => super.start }
|
|
176
|
+
});
|
|
143
177
|
return __awaiter(this, void 0, void 0, function* () {
|
|
178
|
+
yield _super.start.call(this, props);
|
|
144
179
|
this.logger.logEvent({ message: 'start()' });
|
|
145
|
-
|
|
146
|
-
const opts = props || {};
|
|
180
|
+
const args = props || {};
|
|
147
181
|
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
148
|
-
if (this.ignoreHrm && this.ignoreBike && this.ignorePower)
|
|
182
|
+
if (this.ignoreHrm && this.ignoreBike && this.ignorePower) {
|
|
183
|
+
this.logger.logEvent({ message: 'start() not done: bike disabled' });
|
|
149
184
|
return resolve(false);
|
|
185
|
+
}
|
|
150
186
|
if (this.starting) {
|
|
187
|
+
this.logger.logEvent({ message: 'start() not done: bike starting' });
|
|
151
188
|
return resolve(false);
|
|
152
189
|
}
|
|
153
190
|
if (this.started) {
|
|
191
|
+
this.logger.logEvent({ message: 'start() done: bike was already started' });
|
|
192
|
+
this.startWorker();
|
|
154
193
|
return resolve(true);
|
|
155
194
|
}
|
|
156
195
|
this.starting = true;
|
|
157
196
|
const Ant = this.getProtocol().getAnt();
|
|
158
197
|
const protocol = this.getProtocol();
|
|
159
|
-
|
|
160
|
-
|
|
198
|
+
let start = Date.now();
|
|
199
|
+
let timeout = start + (args.timeout || TIMEOUT_ATTACH);
|
|
200
|
+
const ivAttach = setInterval(() => {
|
|
201
|
+
if (Date.now() > timeout) {
|
|
202
|
+
clearInterval(ivAttach);
|
|
203
|
+
this.starting = false;
|
|
204
|
+
reject(new Error('timeout'));
|
|
205
|
+
}
|
|
206
|
+
if (this.isStopped()) {
|
|
207
|
+
clearInterval(ivAttach);
|
|
208
|
+
this.starting = false;
|
|
209
|
+
reject(new Error('stopped'));
|
|
210
|
+
}
|
|
211
|
+
}, 100);
|
|
212
|
+
try {
|
|
213
|
+
if (!this.connected) {
|
|
214
|
+
this.logger.logEvent({ message: 'attach device ...' });
|
|
215
|
+
yield protocol.attachSensors(this, Ant.FitnessEquipmentSensor, 'fitnessData');
|
|
216
|
+
}
|
|
217
|
+
clearInterval(ivAttach);
|
|
161
218
|
this.startWorker();
|
|
162
219
|
const tsStart = Date.now();
|
|
163
220
|
const iv = setInterval(() => __awaiter(this, void 0, void 0, function* () {
|
|
164
221
|
if (this.connected) {
|
|
165
222
|
clearInterval(iv);
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
223
|
+
let status = {
|
|
224
|
+
trackResistanceSent: false,
|
|
225
|
+
userSent: false,
|
|
226
|
+
};
|
|
227
|
+
(0, utils_2.runWithRetries)(() => __awaiter(this, void 0, void 0, function* () {
|
|
228
|
+
if (this.isStopped())
|
|
229
|
+
resolve(false);
|
|
230
|
+
try {
|
|
231
|
+
if (!status.trackResistanceSent) {
|
|
232
|
+
yield this.sendTrackResistance(0.0);
|
|
233
|
+
status.trackResistanceSent = true;
|
|
234
|
+
}
|
|
235
|
+
if (!status.userSent) {
|
|
236
|
+
yield this.sendUserConfiguration(args.userWeight || DEFAULT_USER_WEIGHT, args.bikeWeight || DEFAULT_BIKE_WEIGHT, args.wheelDiameter, args.gearRatio);
|
|
237
|
+
status.userSent = true;
|
|
238
|
+
}
|
|
239
|
+
this.started = true;
|
|
240
|
+
this.starting = false;
|
|
241
|
+
resolve(true);
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
throw (new Error(`could not start device, reason:${err.message || err}`));
|
|
245
|
+
}
|
|
246
|
+
}), 5, 1500)
|
|
247
|
+
.catch(err => {
|
|
248
|
+
this.logger.logEvent({ message: 'start() error', error: err.message || err });
|
|
249
|
+
this.starting = false;
|
|
250
|
+
reject(err);
|
|
251
|
+
});
|
|
176
252
|
}
|
|
177
253
|
else if ((Date.now() - tsStart) > TIMEOUT_START) {
|
|
178
254
|
clearInterval(iv);
|
|
179
|
-
this.started = true;
|
|
180
255
|
try {
|
|
181
256
|
yield protocol.detachSensor(this);
|
|
182
257
|
}
|
|
183
258
|
catch (err) { }
|
|
259
|
+
this.started = false;
|
|
184
260
|
this.starting = false;
|
|
185
|
-
reject(new Error('timeout'));
|
|
261
|
+
reject(new Error('could not start device, reason:timeout'));
|
|
186
262
|
}
|
|
187
263
|
}), 50);
|
|
188
|
-
}
|
|
189
|
-
|
|
264
|
+
}
|
|
265
|
+
catch (err) {
|
|
266
|
+
this.logger.logEvent({ message: 'start() error', error: err.message });
|
|
190
267
|
this.starting = false;
|
|
191
|
-
reject(err);
|
|
192
|
-
}
|
|
268
|
+
reject(new Error(`could not start device, reason:${err.message}`));
|
|
269
|
+
}
|
|
193
270
|
}));
|
|
194
271
|
});
|
|
195
272
|
}
|
|
196
273
|
stop() {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
return
|
|
201
|
-
|
|
202
|
-
|
|
274
|
+
const _super = Object.create(null, {
|
|
275
|
+
stop: { get: () => super.stop }
|
|
276
|
+
});
|
|
277
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
278
|
+
yield _super.stop.call(this);
|
|
279
|
+
this.logger.logEvent({ message: 'stop()' });
|
|
280
|
+
this.stopWorker();
|
|
281
|
+
const Messages = this.getProtocol().getAnt().Messages;
|
|
282
|
+
const stick = this.stick;
|
|
283
|
+
const channel = this.channel;
|
|
284
|
+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
285
|
+
this.starting = false;
|
|
286
|
+
return resolve(true);
|
|
287
|
+
if (!this.started && !this.connected)
|
|
288
|
+
return resolve(false);
|
|
289
|
+
try {
|
|
290
|
+
stick.write(Messages.openChannel(channel));
|
|
291
|
+
const protocol = this.getProtocol();
|
|
292
|
+
yield protocol.detachSensor(this);
|
|
293
|
+
this.started = false;
|
|
294
|
+
this.connected = false;
|
|
295
|
+
this.logger.logEvent({ message: 'stop() finished' });
|
|
296
|
+
resolve(true);
|
|
297
|
+
}
|
|
298
|
+
catch (err) {
|
|
299
|
+
this.connected = false;
|
|
300
|
+
this.logger.logEvent({ message: 'stop() error', error: err.message });
|
|
301
|
+
reject(err);
|
|
302
|
+
}
|
|
303
|
+
}));
|
|
304
|
+
});
|
|
203
305
|
}
|
|
204
306
|
sendUpdate(request) {
|
|
205
307
|
return __awaiter(this, void 0, void 0, function* () {
|
|
206
308
|
this.logger.logEvent({ message: "sendBikeUpdate():", request });
|
|
207
|
-
console.log('~~sendUpdate', request);
|
|
208
309
|
try {
|
|
310
|
+
const isReset = (!request || request.reset || Object.keys(request).length === 0);
|
|
209
311
|
if (request.slope !== undefined) {
|
|
210
|
-
yield this.sendTrackResistance(request.slope);
|
|
312
|
+
yield (0, utils_2.runWithRetries)(() => __awaiter(this, void 0, void 0, function* () { return yield this.sendTrackResistance(request.slope); }), 2, 100);
|
|
211
313
|
}
|
|
212
314
|
if (request.targetPower !== undefined) {
|
|
213
|
-
yield this.sendTargetPower(request.targetPower);
|
|
315
|
+
yield (0, utils_2.runWithRetries)(() => __awaiter(this, void 0, void 0, function* () { return yield this.sendTargetPower(request.targetPower); }), 2, 100);
|
|
214
316
|
}
|
|
215
317
|
else if (request.maxPower !== undefined) {
|
|
216
318
|
if (this.data.power && this.data.power > request.maxPower)
|
|
217
|
-
yield this.sendTargetPower(request.maxPower);
|
|
319
|
+
yield (0, utils_2.runWithRetries)(() => __awaiter(this, void 0, void 0, function* () { return yield this.sendTargetPower(request.maxPower); }), 2, 100);
|
|
218
320
|
}
|
|
219
321
|
else if (request.minPower !== undefined) {
|
|
220
322
|
if (this.data.power && this.data.power < request.minPower)
|
|
221
|
-
yield this.sendTargetPower(request.minPower);
|
|
222
|
-
}
|
|
223
|
-
if (request.maxHrm !== undefined) {
|
|
224
|
-
}
|
|
225
|
-
if (request.minHrm !== undefined) {
|
|
323
|
+
yield (0, utils_2.runWithRetries)(() => __awaiter(this, void 0, void 0, function* () { return yield this.sendTargetPower(request.minPower); }), 2, 100);
|
|
226
324
|
}
|
|
227
325
|
}
|
|
228
326
|
catch (err) {
|
|
@@ -230,17 +328,36 @@ class AntFEAdapter extends AntAdapter_1.default {
|
|
|
230
328
|
}
|
|
231
329
|
});
|
|
232
330
|
}
|
|
233
|
-
send(msg, logStr, callback) {
|
|
331
|
+
send(msg, logStr, callback, expectedResponse) {
|
|
234
332
|
if (this.workerId === undefined) {
|
|
235
333
|
return;
|
|
236
334
|
}
|
|
237
|
-
this.queue.enqueue({ msg, logStr, callback });
|
|
335
|
+
this.queue.enqueue({ msg, logStr, callback, expectedResponse });
|
|
336
|
+
}
|
|
337
|
+
sendAsync(msg, logStr, expectedResponse) {
|
|
338
|
+
return new Promise((resolve, reject) => {
|
|
339
|
+
this.send(msg, logStr, (res, err) => {
|
|
340
|
+
if (err)
|
|
341
|
+
return reject(err);
|
|
342
|
+
resolve(res);
|
|
343
|
+
}, expectedResponse);
|
|
344
|
+
});
|
|
238
345
|
}
|
|
239
346
|
startWorker() {
|
|
347
|
+
this.logger.logEvent({ message: 'startWorker()' });
|
|
240
348
|
if (this.queue === undefined) {
|
|
241
349
|
this.queue = new utils_2.Queue();
|
|
242
350
|
}
|
|
243
|
-
|
|
351
|
+
if (this.workerId)
|
|
352
|
+
return;
|
|
353
|
+
this.workerId = setInterval(() => {
|
|
354
|
+
try {
|
|
355
|
+
this.sendFromQueue();
|
|
356
|
+
}
|
|
357
|
+
catch (err) {
|
|
358
|
+
this.logger.logEvent({ message: 'sendFromQueue error', error: err.message });
|
|
359
|
+
}
|
|
360
|
+
}, 10);
|
|
244
361
|
}
|
|
245
362
|
stopWorker() {
|
|
246
363
|
if (!this.workerId)
|
|
@@ -250,8 +367,9 @@ class AntFEAdapter extends AntAdapter_1.default {
|
|
|
250
367
|
this.workerId = undefined;
|
|
251
368
|
}
|
|
252
369
|
sendFromQueue() {
|
|
253
|
-
if (this.queue === undefined
|
|
370
|
+
if (this.queue === undefined) {
|
|
254
371
|
return;
|
|
372
|
+
}
|
|
255
373
|
if (this.currentCmd !== undefined) {
|
|
256
374
|
const cmdInfo = this.currentCmd;
|
|
257
375
|
if (this.currentCmd.response === undefined) {
|
|
@@ -261,7 +379,6 @@ class AntFEAdapter extends AntAdapter_1.default {
|
|
|
261
379
|
timeout = TIMEOUT_ACK;
|
|
262
380
|
let duration = Date.now() - tsStart;
|
|
263
381
|
if (duration > timeout) {
|
|
264
|
-
console.log('~~~timeout');
|
|
265
382
|
this.logger.logEvent({ message: 'timeout', cmd: logStr, timeout: `${timeout}ms` });
|
|
266
383
|
this.currentCmd = undefined;
|
|
267
384
|
if (callback !== undefined) {
|
|
@@ -275,7 +392,7 @@ class AntFEAdapter extends AntAdapter_1.default {
|
|
|
275
392
|
this.currentCmd = undefined;
|
|
276
393
|
if (callback !== undefined) {
|
|
277
394
|
if (response && response.success === false) {
|
|
278
|
-
callback(undefined, new Error(
|
|
395
|
+
callback(undefined, new Error(`ANT error ${response.code}`));
|
|
279
396
|
return;
|
|
280
397
|
}
|
|
281
398
|
callback(response);
|
|
@@ -288,13 +405,17 @@ class AntFEAdapter extends AntAdapter_1.default {
|
|
|
288
405
|
this.currentCmd = this.queue.dequeue();
|
|
289
406
|
this.currentCmd.tsStart = Date.now();
|
|
290
407
|
const { msg, logStr } = this.currentCmd;
|
|
291
|
-
this.logger.logEvent({ message: "sending", cmd: logStr, msg: utils_2.hexstr(msg), queueSize: this.queue.size() });
|
|
292
|
-
|
|
293
|
-
|
|
408
|
+
this.logger.logEvent({ message: "sending", cmd: logStr, msg: (0, utils_2.hexstr)(msg), queueSize: this.queue.size() });
|
|
409
|
+
if (this.stick)
|
|
410
|
+
this.stick.write(msg);
|
|
294
411
|
}
|
|
295
412
|
}
|
|
296
413
|
sendUserConfiguration(userWeight, bikeWeight, wheelDiameter, gearRatio) {
|
|
297
414
|
return new Promise((resolve, reject) => {
|
|
415
|
+
if (!this.connected)
|
|
416
|
+
reject(new Error('not connected'));
|
|
417
|
+
if (!this.queue)
|
|
418
|
+
this.startWorker();
|
|
298
419
|
var payload = [];
|
|
299
420
|
payload.push(this.channel);
|
|
300
421
|
const logStr = `sendUserConfiguration(${userWeight},${bikeWeight},${wheelDiameter},${gearRatio})`;
|
|
@@ -334,6 +455,10 @@ class AntFEAdapter extends AntAdapter_1.default {
|
|
|
334
455
|
}
|
|
335
456
|
sendBasicResistance(resistance) {
|
|
336
457
|
return new Promise((resolve, reject) => {
|
|
458
|
+
if (!this.connected)
|
|
459
|
+
reject(new Error('not connected'));
|
|
460
|
+
if (!this.queue)
|
|
461
|
+
this.startWorker();
|
|
337
462
|
var payload = [];
|
|
338
463
|
payload.push(this.channel);
|
|
339
464
|
const logStr = `sendBasicResistance(${resistance})`;
|
|
@@ -358,6 +483,10 @@ class AntFEAdapter extends AntAdapter_1.default {
|
|
|
358
483
|
}
|
|
359
484
|
sendTargetPower(power) {
|
|
360
485
|
return new Promise((resolve, reject) => {
|
|
486
|
+
if (!this.connected)
|
|
487
|
+
reject(new Error('not connected'));
|
|
488
|
+
if (!this.queue)
|
|
489
|
+
this.startWorker();
|
|
361
490
|
var payload = [];
|
|
362
491
|
payload.push(this.channel);
|
|
363
492
|
const logStr = `sendTargetPower(${power})`;
|
|
@@ -382,6 +511,10 @@ class AntFEAdapter extends AntAdapter_1.default {
|
|
|
382
511
|
}
|
|
383
512
|
sendWindResistance(windCoeff, windSpeed, draftFactor) {
|
|
384
513
|
return new Promise((resolve, reject) => {
|
|
514
|
+
if (!this.connected)
|
|
515
|
+
reject(new Error('not connected'));
|
|
516
|
+
if (!this.queue)
|
|
517
|
+
this.startWorker();
|
|
385
518
|
var payload = [];
|
|
386
519
|
payload.push(this.channel);
|
|
387
520
|
const logStr = `sendWindResistance(${windCoeff},${windSpeed},${draftFactor})`;
|
|
@@ -416,6 +549,10 @@ class AntFEAdapter extends AntAdapter_1.default {
|
|
|
416
549
|
}
|
|
417
550
|
sendTrackResistance(slope, rrCoeff) {
|
|
418
551
|
return new Promise((resolve, reject) => {
|
|
552
|
+
if (!this.connected)
|
|
553
|
+
reject(new Error('not connected'));
|
|
554
|
+
if (!this.queue)
|
|
555
|
+
this.startWorker();
|
|
419
556
|
var payload = [];
|
|
420
557
|
payload.push(this.channel);
|
|
421
558
|
const logStr = `sendTrackResistance(${slope},${rrCoeff})`;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import AntAdapter from '../AntAdapter';
|
|
2
2
|
export default class AntHrmAdapter extends AntAdapter {
|
|
3
|
+
started: boolean;
|
|
4
|
+
starting: boolean;
|
|
3
5
|
constructor(DeviceID: any, port: any, stick: any, protocol: any);
|
|
4
6
|
isBike(): boolean;
|
|
5
7
|
isHrm(): boolean;
|
|
6
8
|
isPower(): boolean;
|
|
9
|
+
getProfile(): string;
|
|
7
10
|
getName(): string;
|
|
8
11
|
getDisplayName(): string;
|
|
9
12
|
onDeviceData(deviceData: any): void;
|
|
10
13
|
updateData(data: any, deviceData: any): any;
|
|
11
|
-
start(): Promise<
|
|
14
|
+
start(props?: any): Promise<any>;
|
|
12
15
|
stop(): Promise<boolean>;
|
|
13
16
|
}
|
|
@@ -8,10 +8,14 @@ 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
15
|
const gd_eventlog_1 = require("gd-eventlog");
|
|
13
|
-
const AntAdapter_1 = require("../AntAdapter");
|
|
16
|
+
const AntAdapter_1 = __importDefault(require("../AntAdapter"));
|
|
14
17
|
const utils_1 = require("../utils");
|
|
18
|
+
const DEFAULT_START_TIMEOUT = 5000;
|
|
15
19
|
class AntHrmAdapter extends AntAdapter_1.default {
|
|
16
20
|
constructor(DeviceID, port, stick, protocol) {
|
|
17
21
|
super(protocol);
|
|
@@ -19,28 +23,36 @@ class AntHrmAdapter extends AntAdapter_1.default {
|
|
|
19
23
|
this.deviceID = DeviceID;
|
|
20
24
|
this.port = port;
|
|
21
25
|
this.stick = stick;
|
|
26
|
+
this.paused = false;
|
|
22
27
|
this.deviceData = {
|
|
23
28
|
DeviceID
|
|
24
29
|
};
|
|
25
30
|
this.data = {};
|
|
31
|
+
this.started = false;
|
|
32
|
+
this.starting = false;
|
|
26
33
|
}
|
|
27
34
|
isBike() { return false; }
|
|
28
35
|
isHrm() { return true; }
|
|
29
36
|
isPower() { return false; }
|
|
37
|
+
getProfile() {
|
|
38
|
+
return 'Heartrate Monitor';
|
|
39
|
+
}
|
|
30
40
|
getName() {
|
|
31
41
|
return `Ant+Hrm ${this.deviceID}`;
|
|
32
42
|
}
|
|
33
43
|
getDisplayName() {
|
|
34
44
|
const { DeviceID, manID, ComputedHeartRate } = this.deviceData;
|
|
35
45
|
const hrmStr = ComputedHeartRate ? ` (${ComputedHeartRate})` : '';
|
|
36
|
-
return `${utils_1.getBrand(manID)} Hrm ${DeviceID}${hrmStr}`;
|
|
46
|
+
return `${(0, utils_1.getBrand)(manID)} Hrm ${DeviceID}${hrmStr}`;
|
|
37
47
|
}
|
|
38
48
|
onDeviceData(deviceData) {
|
|
49
|
+
if (!this.started)
|
|
50
|
+
return;
|
|
39
51
|
this.deviceData = deviceData;
|
|
40
52
|
try {
|
|
41
53
|
if (this.onDataFn && !this.ignoreHrm && !this.paused) {
|
|
42
|
-
if (
|
|
43
|
-
|
|
54
|
+
if (this.lastUpdate === undefined || (Date.now() - this.lastUpdate) > this.updateFrequency) {
|
|
55
|
+
this.logger.logEvent({ message: 'onDeviceData', data: deviceData });
|
|
44
56
|
const data = this.updateData(this.data, deviceData);
|
|
45
57
|
this.onDataFn(data);
|
|
46
58
|
this.lastUpdate = Date.now();
|
|
@@ -54,34 +66,76 @@ class AntHrmAdapter extends AntAdapter_1.default {
|
|
|
54
66
|
data.heartrate = deviceData.ComputedHeartRate;
|
|
55
67
|
return data;
|
|
56
68
|
}
|
|
57
|
-
start() {
|
|
69
|
+
start(props) {
|
|
70
|
+
const _super = Object.create(null, {
|
|
71
|
+
start: { get: () => super.start }
|
|
72
|
+
});
|
|
58
73
|
return __awaiter(this, void 0, void 0, function* () {
|
|
74
|
+
yield _super.start.call(this, props);
|
|
59
75
|
this.logger.logEvent({ message: 'start()' });
|
|
76
|
+
const args = props || {};
|
|
60
77
|
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
61
78
|
if (this.ignoreHrm)
|
|
62
79
|
resolve(false);
|
|
80
|
+
if (this.starting) {
|
|
81
|
+
this.logger.logEvent({ message: 'start() not done: bike starting' });
|
|
82
|
+
return resolve(false);
|
|
83
|
+
}
|
|
84
|
+
if (this.started) {
|
|
85
|
+
this.logger.logEvent({ message: 'start() done: bike was already started' });
|
|
86
|
+
return resolve(true);
|
|
87
|
+
}
|
|
88
|
+
this.starting = true;
|
|
63
89
|
const Ant = this.getProtocol().getAnt();
|
|
64
90
|
const protocol = this.getProtocol();
|
|
91
|
+
let start = Date.now();
|
|
92
|
+
let timeout = start + (args.timeout || DEFAULT_START_TIMEOUT);
|
|
93
|
+
const iv = setInterval(() => {
|
|
94
|
+
if (Date.now() > timeout) {
|
|
95
|
+
clearInterval(iv);
|
|
96
|
+
this.starting = false;
|
|
97
|
+
reject(new Error('timeout'));
|
|
98
|
+
}
|
|
99
|
+
if (this.isStopped()) {
|
|
100
|
+
clearInterval(iv);
|
|
101
|
+
this.starting = false;
|
|
102
|
+
reject(new Error('stopped'));
|
|
103
|
+
}
|
|
104
|
+
}, 100);
|
|
65
105
|
protocol.attachSensors(this, Ant.HeartRateSensor, 'hbData')
|
|
66
|
-
.then(() =>
|
|
106
|
+
.then(() => {
|
|
107
|
+
this.starting = false;
|
|
108
|
+
this.started = true;
|
|
109
|
+
clearInterval(iv);
|
|
110
|
+
resolve(true);
|
|
111
|
+
})
|
|
67
112
|
.catch(err => reject(err));
|
|
68
113
|
}));
|
|
69
114
|
});
|
|
70
115
|
}
|
|
71
116
|
stop() {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
117
|
+
const _super = Object.create(null, {
|
|
118
|
+
stop: { get: () => super.stop }
|
|
119
|
+
});
|
|
120
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
121
|
+
yield _super.stop.call(this);
|
|
122
|
+
this.logger.logEvent({ message: 'stop()' });
|
|
123
|
+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
124
|
+
this.starting = false;
|
|
125
|
+
return resolve(true);
|
|
126
|
+
this.started = false;
|
|
127
|
+
if (this.ignoreHrm)
|
|
128
|
+
return resolve(false);
|
|
129
|
+
try {
|
|
130
|
+
const protocol = this.getProtocol();
|
|
131
|
+
yield protocol.detachSensor(this);
|
|
132
|
+
resolve(true);
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
reject(err);
|
|
136
|
+
}
|
|
137
|
+
}));
|
|
138
|
+
});
|
|
85
139
|
}
|
|
86
140
|
}
|
|
87
141
|
exports.default = AntHrmAdapter;
|