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
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,13 +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
|
+
}
|
|
126
|
+
getAnt() {
|
|
127
|
+
return this.ant || DeviceProtocol_1.default.getAnt();
|
|
128
|
+
}
|
|
71
129
|
getName() { return 'Ant'; }
|
|
72
|
-
getInterfaces() { return DeviceProtocol_1.INTERFACE.ANT; }
|
|
130
|
+
getInterfaces() { return [DeviceProtocol_1.INTERFACE.ANT]; }
|
|
73
131
|
isBike() { return true; }
|
|
74
132
|
isHrm() { return true; }
|
|
75
133
|
isPower() { return true; }
|
|
@@ -106,50 +164,177 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
106
164
|
const info = this.getStickInfo(sticks);
|
|
107
165
|
this.logger.logEvent({ message: 'stick info', info });
|
|
108
166
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
+
}
|
|
121
180
|
}
|
|
122
|
-
return undefined;
|
|
123
181
|
}
|
|
124
|
-
|
|
182
|
+
getStick(onStart, onError) {
|
|
125
183
|
return __awaiter(this, void 0, void 0, function* () {
|
|
126
184
|
if (!this.ant)
|
|
127
185
|
return;
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
+
}
|
|
137
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]);
|
|
138
298
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
+
}));
|
|
147
324
|
});
|
|
148
325
|
}
|
|
149
326
|
closeStick(stick) {
|
|
327
|
+
this.logger.logEvent({ message: 'closing stick' });
|
|
150
328
|
return new Promise((resolve, reject) => {
|
|
151
329
|
stick.on('shutdown', () => {
|
|
152
|
-
|
|
330
|
+
this.logger.logEvent({ message: 'stick shutdown' });
|
|
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 = {};
|
|
153
338
|
resolve(true);
|
|
154
339
|
});
|
|
155
340
|
try {
|
|
@@ -159,6 +344,9 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
159
344
|
stick.close();
|
|
160
345
|
}
|
|
161
346
|
catch (err) { }
|
|
347
|
+
this.logger.logEvent({ message: 'stick closed' });
|
|
348
|
+
stick.scanConnected = false;
|
|
349
|
+
this.sensors = {};
|
|
162
350
|
}, 1000);
|
|
163
351
|
}
|
|
164
352
|
catch (err) {
|
|
@@ -172,6 +360,7 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
172
360
|
return this.closeStick(stick)
|
|
173
361
|
.then(() => {
|
|
174
362
|
state.isScanning = false;
|
|
363
|
+
this.scanning = this.stillScanning();
|
|
175
364
|
if (state.iv) {
|
|
176
365
|
clearInterval(state.iv);
|
|
177
366
|
state.iv = undefined;
|
|
@@ -183,100 +372,119 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
183
372
|
return true;
|
|
184
373
|
});
|
|
185
374
|
}
|
|
375
|
+
stillScanning() {
|
|
376
|
+
const ports = Object.keys(this.activeScans);
|
|
377
|
+
return ports.find(p => this.activeScans[p].isScanning) !== undefined;
|
|
378
|
+
}
|
|
186
379
|
scanOnStick(stickInfo, props = {}) {
|
|
187
380
|
const { stick, port } = stickInfo;
|
|
188
381
|
const timeout = props.timeout || DEFAULT_SCAN_TIMEOUT;
|
|
189
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
|
+
}
|
|
190
387
|
return new Promise((resolve, reject) => {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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)
|
|
210
410
|
this.devices.push(device);
|
|
211
|
-
}
|
|
212
|
-
catch (err) {
|
|
213
|
-
console.log(err);
|
|
214
|
-
}
|
|
215
|
-
if (device && onDeviceFound) {
|
|
216
|
-
onDeviceFound(device, this);
|
|
217
|
-
device.setDetected(true);
|
|
218
|
-
}
|
|
219
411
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const device = this.devices.find(d => d.getID() === deviceId);
|
|
223
|
-
if (device) {
|
|
224
|
-
const isHrm = device.isHrm();
|
|
225
|
-
device.onDeviceData(data);
|
|
226
|
-
if (device.isHrm() && !isHrm && onDeviceFound) {
|
|
227
|
-
onDeviceFound(device, this);
|
|
228
|
-
}
|
|
229
|
-
if (onUpdate)
|
|
230
|
-
onUpdate(device);
|
|
412
|
+
catch (err) {
|
|
413
|
+
this.logger.logEvent({ message: 'onNewDevice:ERROR', error: err.message });
|
|
231
414
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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 {
|
|
236
437
|
hrm.getScanner().scan();
|
|
237
438
|
hrm.getScanner().on('attached', () => {
|
|
238
439
|
power.getScanner().scan();
|
|
239
440
|
fe.getScanner().scan();
|
|
240
441
|
});
|
|
241
|
-
state.iv = setInterval(() => {
|
|
242
|
-
if (Date.now() > timeout) {
|
|
243
|
-
this.logger.logEvent({ message: 'scan timeout', port });
|
|
244
|
-
this.stopScanOnStick(stickInfo).then(() => {
|
|
245
|
-
if (onScanFinished)
|
|
246
|
-
onScanFinished(id);
|
|
247
|
-
resolve(true);
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
}, timeout);
|
|
251
|
-
});
|
|
252
|
-
let opened = false;
|
|
253
|
-
try {
|
|
254
|
-
opened = stick.open();
|
|
255
442
|
}
|
|
256
|
-
catch (err) {
|
|
257
|
-
|
|
258
|
-
reject(new Error('stick could not be opened'));
|
|
259
|
-
return;
|
|
443
|
+
catch (err) {
|
|
444
|
+
this.logger.logEvent({ message: 'scan error', error: err.message });
|
|
260
445
|
}
|
|
261
|
-
|
|
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);
|
|
262
456
|
});
|
|
263
457
|
}
|
|
264
458
|
scan(props) {
|
|
265
459
|
return __awaiter(this, void 0, void 0, function* () {
|
|
460
|
+
this.logger.logEvent({ message: 'scan request', props });
|
|
461
|
+
this.scanning = true;
|
|
266
462
|
this.logStickInfo();
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
+
}
|
|
270
479
|
this.scanOnStick(stick, props);
|
|
271
480
|
}
|
|
272
|
-
|
|
273
|
-
this.logger.logEvent({ message: '
|
|
481
|
+
catch (err) {
|
|
482
|
+
this.logger.logEvent({ message: 'scan request error', error: err.message });
|
|
274
483
|
}
|
|
275
484
|
});
|
|
276
485
|
}
|
|
277
486
|
stopScan() {
|
|
278
487
|
return __awaiter(this, void 0, void 0, function* () {
|
|
279
|
-
console.log('stop scan requested');
|
|
280
488
|
const activePorts = Object.keys(this.activeScans);
|
|
281
489
|
for (let i = 0; i < activePorts.length; i++) {
|
|
282
490
|
const port = activePorts[i];
|
|
@@ -285,14 +493,25 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
285
493
|
yield this.stopScanOnStick({ port, stick: scanState.stick });
|
|
286
494
|
}
|
|
287
495
|
}
|
|
288
|
-
|
|
496
|
+
this.sensors = {};
|
|
289
497
|
this.logger.logEvent({ message: 'scan stopped' });
|
|
498
|
+
this.scanning = false;
|
|
290
499
|
return true;
|
|
291
500
|
});
|
|
292
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
|
+
}
|
|
293
512
|
attachSensors(d, SensorClass, message) {
|
|
294
513
|
return __awaiter(this, void 0, void 0, function* () {
|
|
295
|
-
return new Promise((resolve, reject) => {
|
|
514
|
+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
296
515
|
if (d === undefined) {
|
|
297
516
|
resolve(false);
|
|
298
517
|
return;
|
|
@@ -301,33 +520,46 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
301
520
|
if (devices.length === 0) {
|
|
302
521
|
return resolve(false);
|
|
303
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;
|
|
304
529
|
if (!this.sensors.stick) {
|
|
305
|
-
|
|
306
|
-
let
|
|
307
|
-
|
|
530
|
+
this.logger.logEvent({ message: 'openStick', device: devices[0].getName() });
|
|
531
|
+
let retryCnt = 0;
|
|
532
|
+
while (!stick && retryCnt < 3) {
|
|
308
533
|
try {
|
|
309
|
-
const
|
|
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
|
-
console.log('~~create Sensor class');
|
|
327
557
|
if (!this.sensors.pending)
|
|
328
558
|
this.sensors.pending = [];
|
|
329
559
|
devices.forEach(device => {
|
|
330
560
|
const sensor = new SensorClass(this.sensors.stick);
|
|
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,49 +570,58 @@ 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
|
-
|
|
350
|
-
|
|
573
|
+
try {
|
|
574
|
+
this.sensors.pending.forEach((i, idx) => {
|
|
575
|
+
const channel = channelsUsed + idx;
|
|
576
|
+
const { sensor } = i;
|
|
577
|
+
i.device.setChannel(channel);
|
|
578
|
+
if (process.env.DEBUG)
|
|
579
|
+
console.log('~~~~Ant: attach', channel, i.device.getID());
|
|
580
|
+
sensor.attach(channel, i.device.getID());
|
|
581
|
+
this.sensors.attached.push(i);
|
|
582
|
+
});
|
|
583
|
+
this.sensors.pending = [];
|
|
584
|
+
resolve(true);
|
|
585
|
+
}
|
|
586
|
+
catch (err) {
|
|
587
|
+
this.logger.logEvent({ message: 'error', fn: 'attachFromPending()', error: err.message || err });
|
|
588
|
+
reject(err.message ? err : new Error(err));
|
|
589
|
+
}
|
|
351
590
|
};
|
|
352
591
|
if (this.sensors.stickStarted) {
|
|
353
592
|
attachFromPending();
|
|
354
593
|
}
|
|
355
594
|
else {
|
|
356
|
-
this.sensors
|
|
357
|
-
|
|
358
|
-
|
|
595
|
+
const sensors = this.sensors;
|
|
596
|
+
const stick = this.sensors.stick;
|
|
597
|
+
stick.on('error', (err) => {
|
|
598
|
+
this.logger.logEvent({ message: 'stick error', error: err.message });
|
|
599
|
+
});
|
|
600
|
+
stick.once('startup', () => {
|
|
601
|
+
sensors.stickStarted = true;
|
|
359
602
|
setTimeout(attachFromPending, 1000);
|
|
360
603
|
});
|
|
361
604
|
}
|
|
362
|
-
});
|
|
605
|
+
}));
|
|
363
606
|
});
|
|
364
607
|
}
|
|
365
608
|
detachSensor(adapter) {
|
|
366
609
|
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
367
|
-
const idx = this.sensors
|
|
610
|
+
const idx = (this.sensors && this.sensors.attached) ?
|
|
611
|
+
this.sensors.attached.findIndex(i => (i.device.getID() === adapter.getID() && i.device.getName() === adapter.getName())) : -1;
|
|
368
612
|
if (idx === -1)
|
|
369
613
|
return resolve(true);
|
|
370
614
|
this.sensors.attached.splice(idx, 1);
|
|
371
615
|
if (this.sensors.attached.length > 0)
|
|
372
616
|
return resolve(true);
|
|
373
|
-
console.log('~~detachSensor: closing stick');
|
|
374
617
|
const stick = this.sensors.stick;
|
|
375
618
|
if (stick === undefined)
|
|
376
619
|
return resolve(false);
|
|
377
620
|
try {
|
|
378
621
|
yield this.closeStick(stick);
|
|
379
|
-
console.log('~~detachSensor: stick closed');
|
|
380
622
|
resolve(true);
|
|
381
623
|
}
|
|
382
624
|
catch (err) {
|
|
383
|
-
console.log('~~detachSensor: could not close stick', err);
|
|
384
625
|
reject(err);
|
|
385
626
|
}
|
|
386
627
|
}));
|
|
@@ -393,7 +634,7 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
393
634
|
stick.close();
|
|
394
635
|
}
|
|
395
636
|
catch (err) {
|
|
396
|
-
|
|
637
|
+
this.logger.logEvent({ message: 'closeSensor error', error: err.message, device: device ? device.getName() : 'unknown' });
|
|
397
638
|
}
|
|
398
639
|
}
|
|
399
640
|
});
|