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/ant/AntScanner.js
CHANGED
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
2
21
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
22
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
23
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -8,14 +27,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
27
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
28
|
});
|
|
10
29
|
};
|
|
30
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
31
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
32
|
+
};
|
|
11
33
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
34
|
exports.AntScanner = exports.AntProtocol = void 0;
|
|
13
35
|
const gd_eventlog_1 = require("gd-eventlog");
|
|
14
|
-
const DeviceProtocol_1 = require("../DeviceProtocol");
|
|
15
|
-
const AntHrmAdapter_1 = require("./anthrm/AntHrmAdapter");
|
|
16
|
-
const AntFEAdapter_1 = require("./antfe/AntFEAdapter");
|
|
36
|
+
const DeviceProtocol_1 = __importStar(require("../DeviceProtocol"));
|
|
37
|
+
const AntHrmAdapter_1 = __importDefault(require("./anthrm/AntHrmAdapter"));
|
|
38
|
+
const AntFEAdapter_1 = __importDefault(require("./antfe/AntFEAdapter"));
|
|
17
39
|
const LOGGER_NAME = 'ANT+Scanner';
|
|
18
40
|
const DEFAULT_SCAN_TIMEOUT = 30000;
|
|
41
|
+
const TIMEOUT_STARTUP = 3000;
|
|
19
42
|
const hex = (n, len) => {
|
|
20
43
|
const c = "0";
|
|
21
44
|
let s = n.toString(16);
|
|
@@ -23,6 +46,19 @@ const hex = (n, len) => {
|
|
|
23
46
|
return `0x${c.repeat(len - s.length)}${s}`;
|
|
24
47
|
return `0x${s}`;
|
|
25
48
|
};
|
|
49
|
+
const isStickPresent = (stick, retries) => __awaiter(void 0, void 0, void 0, function* () {
|
|
50
|
+
let n = 0;
|
|
51
|
+
while (n < retries) {
|
|
52
|
+
try {
|
|
53
|
+
return stick.is_present();
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
n++;
|
|
57
|
+
yield new Promise(resolve => setTimeout(resolve, 1000));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
61
|
+
});
|
|
26
62
|
class AntProfile {
|
|
27
63
|
constructor(profile, AntScannerClass, stick, message, onNewDevice, onData) {
|
|
28
64
|
if (process.env.ANT_PROFILE_DEBUG)
|
|
@@ -63,16 +99,35 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
63
99
|
this.activeScans = {};
|
|
64
100
|
this.sensors = {};
|
|
65
101
|
this.sticks = [];
|
|
102
|
+
this.scanning = false;
|
|
66
103
|
this.profiles = [
|
|
67
104
|
{ name: 'Heartrate Monitor', Adapter: AntHrmAdapter_1.default },
|
|
68
105
|
{ name: 'Smart Trainer', Adapter: AntFEAdapter_1.default }
|
|
69
106
|
];
|
|
70
107
|
}
|
|
108
|
+
add(settings) {
|
|
109
|
+
this.logger.logEvent({ message: 'adding device', settings });
|
|
110
|
+
const { profile, deviceID, port } = settings;
|
|
111
|
+
const profileInfo = this.profiles.find(i => i.name === profile);
|
|
112
|
+
if (profileInfo) {
|
|
113
|
+
let device;
|
|
114
|
+
try {
|
|
115
|
+
device = new profileInfo.Adapter(deviceID, port, undefined, this);
|
|
116
|
+
this.devices.push(device);
|
|
117
|
+
return device;
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
this.logger.logEvent({ message: 'adding device error', error: err.message });
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
this.logger.logEvent({ message: 'adding device: profile not found' });
|
|
125
|
+
}
|
|
71
126
|
getAnt() {
|
|
72
127
|
return this.ant || DeviceProtocol_1.default.getAnt();
|
|
73
128
|
}
|
|
74
129
|
getName() { return 'Ant'; }
|
|
75
|
-
getInterfaces() { return DeviceProtocol_1.INTERFACE.ANT; }
|
|
130
|
+
getInterfaces() { return [DeviceProtocol_1.INTERFACE.ANT]; }
|
|
76
131
|
isBike() { return true; }
|
|
77
132
|
isHrm() { return true; }
|
|
78
133
|
isPower() { return true; }
|
|
@@ -109,50 +164,177 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
109
164
|
const info = this.getStickInfo(sticks);
|
|
110
165
|
this.logger.logEvent({ message: 'stick info', info });
|
|
111
166
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
167
|
+
getDetailedStickInfo(stick) {
|
|
168
|
+
const devices = stick.getDevices();
|
|
169
|
+
if (devices.length > 0) {
|
|
170
|
+
const device = devices[0];
|
|
171
|
+
try {
|
|
172
|
+
const config = JSON.parse(JSON.stringify(device.configDescriptor));
|
|
173
|
+
const interfaces = config ? config.interfaces : [];
|
|
174
|
+
delete config.interfaces;
|
|
175
|
+
this.logger.logEvent({ message: 'USB DeviceConfig', config, interfaces });
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
this.logger.logEvent({ message: 'USB Error', error: err.message });
|
|
179
|
+
}
|
|
124
180
|
}
|
|
125
|
-
return undefined;
|
|
126
181
|
}
|
|
127
|
-
|
|
182
|
+
getStick(onStart, onError) {
|
|
128
183
|
return __awaiter(this, void 0, void 0, function* () {
|
|
129
184
|
if (!this.ant)
|
|
130
185
|
return;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
186
|
+
const startupAttempt = (stick, name) => {
|
|
187
|
+
return new Promise((resolve, reject) => {
|
|
188
|
+
this.logger.logEvent({ message: `${name} startup attempt` });
|
|
189
|
+
if (stick.scanConnected) {
|
|
190
|
+
onStart(this);
|
|
191
|
+
resolve(stick);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (isStickPresent(stick, 2)) {
|
|
195
|
+
stick.on('error', (err) => {
|
|
196
|
+
this.logger.logEvent({ message: `${name} startup error`, error: err.message });
|
|
197
|
+
onError(err.message);
|
|
198
|
+
});
|
|
199
|
+
stick.on('startup', () => {
|
|
200
|
+
if (stick.scanConnected)
|
|
201
|
+
return;
|
|
202
|
+
this.logger.logEvent({ message: `${name} startup completed` });
|
|
203
|
+
stick.scanConnected = true;
|
|
204
|
+
onStart(stick);
|
|
205
|
+
resolve(stick);
|
|
206
|
+
});
|
|
207
|
+
const devices = stick.getDevices();
|
|
208
|
+
if (devices && devices.length > 0) {
|
|
209
|
+
devices.forEach(d => {
|
|
210
|
+
d.closeFn = d.close;
|
|
211
|
+
d.close = () => { };
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
this.logger.logEvent({ message: `${name} startup failed: no devices ` });
|
|
216
|
+
resolve(null);
|
|
217
|
+
}
|
|
218
|
+
this.getDetailedStickInfo(stick);
|
|
219
|
+
let open = false;
|
|
220
|
+
try {
|
|
221
|
+
open = stick.open();
|
|
222
|
+
if (open) {
|
|
223
|
+
this.logger.logEvent({ message: `found ${name}` });
|
|
224
|
+
const timeoutStartup = Date.now() + TIMEOUT_STARTUP;
|
|
225
|
+
const iv = setInterval(() => {
|
|
226
|
+
if (!stick.scanConnected && Date.now() > timeoutStartup) {
|
|
227
|
+
clearInterval(iv);
|
|
228
|
+
this.logger.logEvent({ message: `${name} startup timeout` });
|
|
229
|
+
}
|
|
230
|
+
if (stick.scanConnected)
|
|
231
|
+
clearInterval(iv);
|
|
232
|
+
}, 100);
|
|
233
|
+
return stick;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch (openErr) {
|
|
237
|
+
this.logger.logEvent({ message: 'Open Error', error: openErr.message });
|
|
238
|
+
}
|
|
239
|
+
if (devices && devices.length > 0) {
|
|
240
|
+
devices.forEach(d => {
|
|
241
|
+
d.close = d.closeFn;
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
if (!open) {
|
|
245
|
+
let detachedKernelDriver = false;
|
|
246
|
+
while (devices.length) {
|
|
247
|
+
let device;
|
|
248
|
+
try {
|
|
249
|
+
device = devices.shift();
|
|
250
|
+
device.open();
|
|
251
|
+
const iface = device.interfaces[0];
|
|
252
|
+
try {
|
|
253
|
+
if (iface.isKernelDriverActive()) {
|
|
254
|
+
detachedKernelDriver = true;
|
|
255
|
+
iface.detachKernelDriver();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
catch (kernelErr) {
|
|
259
|
+
this.logger.logEvent({ message: 'Kernel Error', error: kernelErr.message });
|
|
260
|
+
}
|
|
261
|
+
iface.claim();
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
catch (deviceErr) {
|
|
265
|
+
this.logger.logEvent({ message: 'Device Error', error: deviceErr.message });
|
|
266
|
+
if (device) {
|
|
267
|
+
try {
|
|
268
|
+
device.close();
|
|
269
|
+
}
|
|
270
|
+
catch (_a) { }
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
140
275
|
}
|
|
276
|
+
this.logger.logEvent({ message: `${name} startup failed: no stick present` });
|
|
277
|
+
resolve(null);
|
|
278
|
+
});
|
|
279
|
+
};
|
|
280
|
+
let stick = undefined;
|
|
281
|
+
stick = yield startupAttempt(new this.ant.GarminStick2(), 'GarminStick2');
|
|
282
|
+
if (!stick)
|
|
283
|
+
stick = yield startupAttempt(new this.ant.GarminStick3(), 'GarminStick3');
|
|
284
|
+
if (!stick)
|
|
285
|
+
onError('No stick found');
|
|
286
|
+
else
|
|
287
|
+
return stick;
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
getFirstStick() {
|
|
291
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
292
|
+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
293
|
+
if (!this.ant)
|
|
294
|
+
return reject(new Error('Ant not supported'));
|
|
295
|
+
if (this.sticks && this.sticks.length > 0 && this.sticks[0].connected) {
|
|
296
|
+
this.logger.logEvent({ message: 'stick already connected' });
|
|
297
|
+
return resolve(this.sticks[0]);
|
|
141
298
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
299
|
+
try {
|
|
300
|
+
let start = Date.now();
|
|
301
|
+
let timeout = start + 5000;
|
|
302
|
+
let found = false;
|
|
303
|
+
const iv = setInterval(() => {
|
|
304
|
+
if (!found && Date.now() > timeout) {
|
|
305
|
+
clearInterval(iv);
|
|
306
|
+
reject(new Error('timeout'));
|
|
307
|
+
}
|
|
308
|
+
}, 100);
|
|
309
|
+
this.getStick((stick) => {
|
|
310
|
+
clearInterval(iv);
|
|
311
|
+
const port = this.getUSBDeviceInfo(stick.device).port;
|
|
312
|
+
if (!this.sticks.find(i => i.port === port)) {
|
|
313
|
+
this.sticks.push({ port, stick, connected: true });
|
|
314
|
+
}
|
|
315
|
+
resolve({ port, stick });
|
|
316
|
+
}, (reason) => {
|
|
317
|
+
resolve(undefined);
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
catch (err) {
|
|
321
|
+
this.logger.logEvent({ message: 'getFirstStick error', error: err.message, stack: err.stack });
|
|
322
|
+
}
|
|
323
|
+
}));
|
|
150
324
|
});
|
|
151
325
|
}
|
|
152
326
|
closeStick(stick) {
|
|
327
|
+
this.logger.logEvent({ message: 'closing stick' });
|
|
153
328
|
return new Promise((resolve, reject) => {
|
|
154
329
|
stick.on('shutdown', () => {
|
|
330
|
+
this.logger.logEvent({ message: 'stick shutdown' });
|
|
155
331
|
stick.removeAllListeners('shutdown');
|
|
332
|
+
const port = this.getUSBDeviceInfo(stick.device).port;
|
|
333
|
+
const idx = this.sticks.findIndex(i => i.port === port);
|
|
334
|
+
if (idx !== -1) {
|
|
335
|
+
this.sticks[idx].connected = false;
|
|
336
|
+
}
|
|
337
|
+
this.sensors = {};
|
|
156
338
|
resolve(true);
|
|
157
339
|
});
|
|
158
340
|
try {
|
|
@@ -162,6 +344,9 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
162
344
|
stick.close();
|
|
163
345
|
}
|
|
164
346
|
catch (err) { }
|
|
347
|
+
this.logger.logEvent({ message: 'stick closed' });
|
|
348
|
+
stick.scanConnected = false;
|
|
349
|
+
this.sensors = {};
|
|
165
350
|
}, 1000);
|
|
166
351
|
}
|
|
167
352
|
catch (err) {
|
|
@@ -175,6 +360,7 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
175
360
|
return this.closeStick(stick)
|
|
176
361
|
.then(() => {
|
|
177
362
|
state.isScanning = false;
|
|
363
|
+
this.scanning = this.stillScanning();
|
|
178
364
|
if (state.iv) {
|
|
179
365
|
clearInterval(state.iv);
|
|
180
366
|
state.iv = undefined;
|
|
@@ -186,94 +372,114 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
186
372
|
return true;
|
|
187
373
|
});
|
|
188
374
|
}
|
|
375
|
+
stillScanning() {
|
|
376
|
+
const ports = Object.keys(this.activeScans);
|
|
377
|
+
return ports.find(p => this.activeScans[p].isScanning) !== undefined;
|
|
378
|
+
}
|
|
189
379
|
scanOnStick(stickInfo, props = {}) {
|
|
190
380
|
const { stick, port } = stickInfo;
|
|
191
381
|
const timeout = props.timeout || DEFAULT_SCAN_TIMEOUT;
|
|
192
382
|
const { onDeviceFound, onScanFinished, onUpdate, id } = props;
|
|
383
|
+
this.logger.logEvent({ message: 'stick scan request', port, activeScans: this.activeScans });
|
|
384
|
+
if (process.env.ANT_DEBUG) {
|
|
385
|
+
stick.props = { debug: true };
|
|
386
|
+
}
|
|
193
387
|
return new Promise((resolve, reject) => {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
388
|
+
if (!port)
|
|
389
|
+
return reject(new Error('busy'));
|
|
390
|
+
if (this.activeScans[port] && this.activeScans[port].isScanning)
|
|
391
|
+
return reject(new Error('busy'));
|
|
392
|
+
if (!this.activeScans[port]) {
|
|
393
|
+
this.activeScans[port] = { isScanning: false, stick };
|
|
394
|
+
}
|
|
395
|
+
const state = this.activeScans[port];
|
|
396
|
+
if (state.isScanning)
|
|
397
|
+
return reject(new Error('busy'));
|
|
398
|
+
state.isScanning = true;
|
|
399
|
+
this.logger.logEvent({ message: 'start scan', port });
|
|
400
|
+
state.timeout = Date.now() + timeout;
|
|
401
|
+
const onNewDevice = (profile, deviceId) => {
|
|
402
|
+
this.logger.logEvent({ message: 'found device', profile, id: deviceId });
|
|
403
|
+
const profileInfo = this.profiles.find(i => i.name === profile);
|
|
404
|
+
if (profileInfo) {
|
|
405
|
+
let device;
|
|
406
|
+
try {
|
|
407
|
+
device = new profileInfo.Adapter(deviceId, port, stick, this, props);
|
|
408
|
+
const existing = this.devices.find(d => (d.getID() === deviceId && d.getProfile() === profile));
|
|
409
|
+
if (!existing)
|
|
213
410
|
this.devices.push(device);
|
|
214
|
-
}
|
|
215
|
-
catch (err) {
|
|
216
|
-
console.log(err);
|
|
217
|
-
}
|
|
218
|
-
if (device && onDeviceFound) {
|
|
219
|
-
onDeviceFound(device, this);
|
|
220
|
-
device.setDetected(true);
|
|
221
|
-
}
|
|
222
411
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
const device = this.devices.find(d => d.getID() === deviceId);
|
|
226
|
-
if (device) {
|
|
227
|
-
const isHrm = device.isHrm();
|
|
228
|
-
device.onDeviceData(data);
|
|
229
|
-
if (device.isHrm() && !isHrm && onDeviceFound) {
|
|
230
|
-
onDeviceFound(device, this);
|
|
231
|
-
}
|
|
232
|
-
if (onUpdate)
|
|
233
|
-
onUpdate(device);
|
|
412
|
+
catch (err) {
|
|
413
|
+
this.logger.logEvent({ message: 'onNewDevice:ERROR', error: err.message });
|
|
234
414
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
415
|
+
if (device && onDeviceFound) {
|
|
416
|
+
onDeviceFound(device, this);
|
|
417
|
+
device.setDetected(true);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
const onData = (profile, deviceId, data) => {
|
|
422
|
+
const device = this.devices.find(d => d.getID() === deviceId);
|
|
423
|
+
if (device) {
|
|
424
|
+
const isHrm = device.isHrm();
|
|
425
|
+
device.onDeviceData(data);
|
|
426
|
+
if (device.isHrm() && !isHrm && onDeviceFound) {
|
|
427
|
+
onDeviceFound(device, this);
|
|
428
|
+
}
|
|
429
|
+
if (onUpdate)
|
|
430
|
+
onUpdate(device);
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
const hrm = new AntProfile('Heartrate Monitor', this.ant.HeartRateScanner, stick, 'hbData', onNewDevice, onData);
|
|
434
|
+
const fe = new AntProfile('Smart Trainer', this.ant.FitnessEquipmentScanner, stick, 'fitnessData', onNewDevice, onData);
|
|
435
|
+
const power = new AntProfile('Power Meter', this.ant.BicyclePowerScanner, stick, 'powerData', onNewDevice, onData);
|
|
436
|
+
try {
|
|
239
437
|
hrm.getScanner().scan();
|
|
240
438
|
hrm.getScanner().on('attached', () => {
|
|
241
439
|
power.getScanner().scan();
|
|
242
440
|
fe.getScanner().scan();
|
|
243
441
|
});
|
|
244
|
-
state.iv = setInterval(() => {
|
|
245
|
-
if (Date.now() > timeout) {
|
|
246
|
-
this.logger.logEvent({ message: 'scan timeout', port });
|
|
247
|
-
this.stopScanOnStick(stickInfo).then(() => {
|
|
248
|
-
if (onScanFinished)
|
|
249
|
-
onScanFinished(id);
|
|
250
|
-
resolve(true);
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
}, timeout);
|
|
254
|
-
});
|
|
255
|
-
let opened = false;
|
|
256
|
-
try {
|
|
257
|
-
opened = stick.open();
|
|
258
442
|
}
|
|
259
|
-
catch (err) {
|
|
260
|
-
|
|
261
|
-
reject(new Error('stick could not be opened'));
|
|
262
|
-
return;
|
|
443
|
+
catch (err) {
|
|
444
|
+
this.logger.logEvent({ message: 'scan error', error: err.message });
|
|
263
445
|
}
|
|
264
|
-
|
|
446
|
+
state.iv = setInterval(() => {
|
|
447
|
+
if (Date.now() > timeout) {
|
|
448
|
+
this.logger.logEvent({ message: 'scan timeout', port });
|
|
449
|
+
this.stopScanOnStick(stickInfo).then(() => {
|
|
450
|
+
if (onScanFinished)
|
|
451
|
+
onScanFinished(id);
|
|
452
|
+
resolve(true);
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}, timeout);
|
|
265
456
|
});
|
|
266
457
|
}
|
|
267
458
|
scan(props) {
|
|
268
459
|
return __awaiter(this, void 0, void 0, function* () {
|
|
460
|
+
this.logger.logEvent({ message: 'scan request', props });
|
|
461
|
+
this.scanning = true;
|
|
269
462
|
this.logStickInfo();
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
463
|
+
if (this.sensors && this.sensors.stick && this.sensors.stickOpen) {
|
|
464
|
+
try {
|
|
465
|
+
yield this.closeStick(this.sensors.stick);
|
|
466
|
+
this.sensors = {};
|
|
467
|
+
this.devices = [];
|
|
468
|
+
}
|
|
469
|
+
catch (err) {
|
|
470
|
+
this.logger.logEvent({ message: 'error on closing stick', error: err.message });
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
try {
|
|
474
|
+
const stick = yield this.getFirstStick();
|
|
475
|
+
if (!stick) {
|
|
476
|
+
this.logger.logEvent({ message: 'no stick found' });
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
273
479
|
this.scanOnStick(stick, props);
|
|
274
480
|
}
|
|
275
|
-
|
|
276
|
-
this.logger.logEvent({ message: '
|
|
481
|
+
catch (err) {
|
|
482
|
+
this.logger.logEvent({ message: 'scan request error', error: err.message });
|
|
277
483
|
}
|
|
278
484
|
});
|
|
279
485
|
}
|
|
@@ -287,13 +493,25 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
287
493
|
yield this.stopScanOnStick({ port, stick: scanState.stick });
|
|
288
494
|
}
|
|
289
495
|
}
|
|
496
|
+
this.sensors = {};
|
|
290
497
|
this.logger.logEvent({ message: 'scan stopped' });
|
|
498
|
+
this.scanning = false;
|
|
291
499
|
return true;
|
|
292
500
|
});
|
|
293
501
|
}
|
|
502
|
+
waitForStickOpenedForSensor() {
|
|
503
|
+
return new Promise((resolve, reject) => {
|
|
504
|
+
const iv = setInterval(() => {
|
|
505
|
+
if (!this.sensors.stickOpening && !this.scanning) {
|
|
506
|
+
clearInterval(iv);
|
|
507
|
+
resolve(true);
|
|
508
|
+
}
|
|
509
|
+
}, 100);
|
|
510
|
+
});
|
|
511
|
+
}
|
|
294
512
|
attachSensors(d, SensorClass, message) {
|
|
295
513
|
return __awaiter(this, void 0, void 0, function* () {
|
|
296
|
-
return new Promise((resolve, reject) => {
|
|
514
|
+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
297
515
|
if (d === undefined) {
|
|
298
516
|
resolve(false);
|
|
299
517
|
return;
|
|
@@ -302,25 +520,38 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
302
520
|
if (devices.length === 0) {
|
|
303
521
|
return resolve(false);
|
|
304
522
|
}
|
|
523
|
+
this.logger.logEvent({ message: 'attachSensors', names: Array.isArray(d) ? d.map(dd => dd.getName()) : d.getName(), state: this.sensors });
|
|
524
|
+
if (this.sensors.stickOpening || this.scanning) {
|
|
525
|
+
yield this.waitForStickOpenedForSensor();
|
|
526
|
+
}
|
|
527
|
+
this.sensors.stickOpening = true;
|
|
528
|
+
let stick;
|
|
305
529
|
if (!this.sensors.stick) {
|
|
306
|
-
|
|
307
|
-
let
|
|
308
|
-
|
|
530
|
+
this.logger.logEvent({ message: 'openStick', device: devices[0].getName() });
|
|
531
|
+
let retryCnt = 0;
|
|
532
|
+
while (!stick && retryCnt < 3) {
|
|
309
533
|
try {
|
|
310
|
-
|
|
311
|
-
|
|
534
|
+
const stickInfo = yield this.getFirstStick();
|
|
535
|
+
if (stickInfo && stickInfo.stick) {
|
|
536
|
+
this.logger.logEvent({ message: 'stick opened', device: devices[0].getName() });
|
|
537
|
+
stick = stickInfo.stick;
|
|
538
|
+
this.sensors.stick = stick;
|
|
539
|
+
this.sensors.stickOpen = true;
|
|
540
|
+
this.sensors.stickStarted = true;
|
|
541
|
+
this.sensors.stickOpening = false;
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
retryCnt++;
|
|
545
|
+
}
|
|
312
546
|
}
|
|
313
547
|
catch (err) {
|
|
314
|
-
|
|
548
|
+
retryCnt++;
|
|
549
|
+
this.logger.logEvent({ message: 'stick open error', error: err.message, device: devices[0].getName() });
|
|
315
550
|
}
|
|
316
|
-
if (!opened)
|
|
317
|
-
return false;
|
|
318
551
|
}
|
|
319
|
-
|
|
320
|
-
|
|
552
|
+
if (!stick) {
|
|
553
|
+
return reject(new Error('could not pen stick'));
|
|
321
554
|
}
|
|
322
|
-
this.sensors.stick = stick;
|
|
323
|
-
this.sensors.stickOpen = opened;
|
|
324
555
|
}
|
|
325
556
|
if (this.sensors.stickOpen) {
|
|
326
557
|
if (!this.sensors.pending)
|
|
@@ -328,6 +559,7 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
328
559
|
devices.forEach(device => {
|
|
329
560
|
const sensor = new SensorClass(this.sensors.stick);
|
|
330
561
|
device.setSensor(sensor);
|
|
562
|
+
device.setStick(stick);
|
|
331
563
|
sensor.on(message, (data) => { device.onDeviceData(data); });
|
|
332
564
|
sensor.on('eventData', (data) => { device.onDeviceEvent(data); });
|
|
333
565
|
sensor.once('attached', () => { device.onAttached(); });
|
|
@@ -338,31 +570,43 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
338
570
|
if (!this.sensors.attached)
|
|
339
571
|
this.sensors.attached = [];
|
|
340
572
|
const channelsUsed = this.sensors.attached.length;
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
573
|
+
try {
|
|
574
|
+
this.sensors.pending.forEach((i, idx) => {
|
|
575
|
+
const channel = channelsUsed + idx;
|
|
576
|
+
const { sensor } = i;
|
|
577
|
+
i.device.setChannel(channel);
|
|
578
|
+
sensor.attach(channel, i.device.getID());
|
|
579
|
+
this.sensors.attached.push(i);
|
|
580
|
+
});
|
|
581
|
+
this.sensors.pending = [];
|
|
582
|
+
resolve(true);
|
|
583
|
+
}
|
|
584
|
+
catch (err) {
|
|
585
|
+
this.logger.logEvent({ message: 'error', fn: 'attachFromPending()', error: err.message || err });
|
|
586
|
+
reject(err.message ? err : new Error(err));
|
|
587
|
+
}
|
|
350
588
|
};
|
|
351
589
|
if (this.sensors.stickStarted) {
|
|
352
590
|
attachFromPending();
|
|
353
591
|
}
|
|
354
592
|
else {
|
|
355
|
-
this.sensors
|
|
356
|
-
|
|
593
|
+
const sensors = this.sensors;
|
|
594
|
+
const stick = this.sensors.stick;
|
|
595
|
+
stick.on('error', (err) => {
|
|
596
|
+
this.logger.logEvent({ message: 'stick error', error: err.message });
|
|
597
|
+
});
|
|
598
|
+
stick.once('startup', () => {
|
|
599
|
+
sensors.stickStarted = true;
|
|
357
600
|
setTimeout(attachFromPending, 1000);
|
|
358
601
|
});
|
|
359
602
|
}
|
|
360
|
-
});
|
|
603
|
+
}));
|
|
361
604
|
});
|
|
362
605
|
}
|
|
363
606
|
detachSensor(adapter) {
|
|
364
607
|
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
365
|
-
const idx = this.sensors
|
|
608
|
+
const idx = (this.sensors && this.sensors.attached) ?
|
|
609
|
+
this.sensors.attached.findIndex(i => (i.device.getID() === adapter.getID() && i.device.getName() === adapter.getName())) : -1;
|
|
366
610
|
if (idx === -1)
|
|
367
611
|
return resolve(true);
|
|
368
612
|
this.sensors.attached.splice(idx, 1);
|
|
@@ -388,7 +632,7 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
388
632
|
stick.close();
|
|
389
633
|
}
|
|
390
634
|
catch (err) {
|
|
391
|
-
|
|
635
|
+
this.logger.logEvent({ message: 'closeSensor error', error: err.message, device: device ? device.getName() : 'unknown' });
|
|
392
636
|
}
|
|
393
637
|
}
|
|
394
638
|
});
|