incyclist-devices 1.1.0 → 1.3.25
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 +11 -2
- package/lib/ant/AntAdapter.js +30 -1
- package/lib/ant/AntScanner.d.ts +17 -6
- package/lib/ant/AntScanner.js +406 -135
- package/lib/ant/antfe/AntFEAdapter.d.ts +12 -8
- package/lib/ant/antfe/AntFEAdapter.js +404 -182
- package/lib/ant/anthrm/AntHrmAdapter.d.ts +4 -2
- package/lib/ant/anthrm/AntHrmAdapter.js +72 -25
- 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,54 +164,190 @@ 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
|
-
if (!this.sticks.find(i => i.port === port)) {
|
|
136
|
-
this.sticks.push({ port, stick });
|
|
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;
|
|
137
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
|
+
}
|
|
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
|
-
stick.
|
|
329
|
+
stick.on('shutdown', () => {
|
|
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 = {};
|
|
152
338
|
resolve(true);
|
|
153
339
|
});
|
|
154
340
|
try {
|
|
155
341
|
stick.detach_all();
|
|
156
|
-
setTimeout(() => {
|
|
342
|
+
setTimeout(() => {
|
|
343
|
+
try {
|
|
344
|
+
stick.close();
|
|
345
|
+
}
|
|
346
|
+
catch (err) { }
|
|
347
|
+
this.logger.logEvent({ message: 'stick closed' });
|
|
348
|
+
stick.scanConnected = false;
|
|
349
|
+
this.sensors = {};
|
|
350
|
+
}, 1000);
|
|
157
351
|
}
|
|
158
352
|
catch (err) {
|
|
159
353
|
reject(err);
|
|
@@ -166,6 +360,7 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
166
360
|
return this.closeStick(stick)
|
|
167
361
|
.then(() => {
|
|
168
362
|
state.isScanning = false;
|
|
363
|
+
this.scanning = this.stillScanning();
|
|
169
364
|
if (state.iv) {
|
|
170
365
|
clearInterval(state.iv);
|
|
171
366
|
state.iv = undefined;
|
|
@@ -177,100 +372,119 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
177
372
|
return true;
|
|
178
373
|
});
|
|
179
374
|
}
|
|
375
|
+
stillScanning() {
|
|
376
|
+
const ports = Object.keys(this.activeScans);
|
|
377
|
+
return ports.find(p => this.activeScans[p].isScanning) !== undefined;
|
|
378
|
+
}
|
|
180
379
|
scanOnStick(stickInfo, props = {}) {
|
|
181
380
|
const { stick, port } = stickInfo;
|
|
182
381
|
const timeout = props.timeout || DEFAULT_SCAN_TIMEOUT;
|
|
183
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
|
+
}
|
|
184
387
|
return new Promise((resolve, reject) => {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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)
|
|
204
410
|
this.devices.push(device);
|
|
205
|
-
}
|
|
206
|
-
catch (err) {
|
|
207
|
-
console.log(err);
|
|
208
|
-
}
|
|
209
|
-
if (device && onDeviceFound) {
|
|
210
|
-
onDeviceFound(device, this);
|
|
211
|
-
device.setDetected(true);
|
|
212
|
-
}
|
|
213
411
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const device = this.devices.find(d => d.getID() === deviceId);
|
|
217
|
-
if (device) {
|
|
218
|
-
const isHrm = device.isHrm();
|
|
219
|
-
device.onDeviceData(data);
|
|
220
|
-
if (device.isHrm() && !isHrm && onDeviceFound) {
|
|
221
|
-
onDeviceFound(device, this);
|
|
222
|
-
}
|
|
223
|
-
if (onUpdate)
|
|
224
|
-
onUpdate(device);
|
|
412
|
+
catch (err) {
|
|
413
|
+
this.logger.logEvent({ message: 'onNewDevice:ERROR', error: err.message });
|
|
225
414
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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 {
|
|
230
437
|
hrm.getScanner().scan();
|
|
231
438
|
hrm.getScanner().on('attached', () => {
|
|
232
439
|
power.getScanner().scan();
|
|
233
440
|
fe.getScanner().scan();
|
|
234
441
|
});
|
|
235
|
-
state.iv = setInterval(() => {
|
|
236
|
-
if (Date.now() > timeout) {
|
|
237
|
-
this.logger.logEvent({ message: 'scan timeout', port });
|
|
238
|
-
this.stopScanOnStick(stickInfo).then(() => {
|
|
239
|
-
if (onScanFinished)
|
|
240
|
-
onScanFinished(id);
|
|
241
|
-
resolve(true);
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
}, timeout);
|
|
245
|
-
});
|
|
246
|
-
let opened = false;
|
|
247
|
-
try {
|
|
248
|
-
opened = stick.open();
|
|
249
442
|
}
|
|
250
|
-
catch (err) {
|
|
251
|
-
|
|
252
|
-
reject(new Error('stick could not be opened'));
|
|
253
|
-
return;
|
|
443
|
+
catch (err) {
|
|
444
|
+
this.logger.logEvent({ message: 'scan error', error: err.message });
|
|
254
445
|
}
|
|
255
|
-
|
|
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);
|
|
256
456
|
});
|
|
257
457
|
}
|
|
258
458
|
scan(props) {
|
|
259
459
|
return __awaiter(this, void 0, void 0, function* () {
|
|
460
|
+
this.logger.logEvent({ message: 'scan request', props });
|
|
461
|
+
this.scanning = true;
|
|
260
462
|
this.logStickInfo();
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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
|
+
}
|
|
264
479
|
this.scanOnStick(stick, props);
|
|
265
480
|
}
|
|
266
|
-
|
|
267
|
-
this.logger.logEvent({ message: '
|
|
481
|
+
catch (err) {
|
|
482
|
+
this.logger.logEvent({ message: 'scan request error', error: err.message });
|
|
268
483
|
}
|
|
269
484
|
});
|
|
270
485
|
}
|
|
271
486
|
stopScan() {
|
|
272
487
|
return __awaiter(this, void 0, void 0, function* () {
|
|
273
|
-
console.log('stop scan requested');
|
|
274
488
|
const activePorts = Object.keys(this.activeScans);
|
|
275
489
|
for (let i = 0; i < activePorts.length; i++) {
|
|
276
490
|
const port = activePorts[i];
|
|
@@ -279,14 +493,25 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
279
493
|
yield this.stopScanOnStick({ port, stick: scanState.stick });
|
|
280
494
|
}
|
|
281
495
|
}
|
|
282
|
-
|
|
496
|
+
this.sensors = {};
|
|
283
497
|
this.logger.logEvent({ message: 'scan stopped' });
|
|
498
|
+
this.scanning = false;
|
|
284
499
|
return true;
|
|
285
500
|
});
|
|
286
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
|
+
}
|
|
287
512
|
attachSensors(d, SensorClass, message) {
|
|
288
513
|
return __awaiter(this, void 0, void 0, function* () {
|
|
289
|
-
return new Promise((resolve, reject) => {
|
|
514
|
+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
290
515
|
if (d === undefined) {
|
|
291
516
|
resolve(false);
|
|
292
517
|
return;
|
|
@@ -295,35 +520,49 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
295
520
|
if (devices.length === 0) {
|
|
296
521
|
return resolve(false);
|
|
297
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;
|
|
298
529
|
if (!this.sensors.stick) {
|
|
299
|
-
|
|
300
|
-
let
|
|
301
|
-
|
|
530
|
+
this.logger.logEvent({ message: 'openStick', device: devices[0].getName() });
|
|
531
|
+
let retryCnt = 0;
|
|
532
|
+
while (!stick && retryCnt < 3) {
|
|
302
533
|
try {
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
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
|
+
}
|
|
306
546
|
}
|
|
307
547
|
catch (err) {
|
|
308
|
-
|
|
548
|
+
retryCnt++;
|
|
549
|
+
this.logger.logEvent({ message: 'stick open error', error: err.message, device: devices[0].getName() });
|
|
309
550
|
}
|
|
310
|
-
if (!opened)
|
|
311
|
-
return false;
|
|
312
551
|
}
|
|
313
|
-
|
|
314
|
-
|
|
552
|
+
if (!stick) {
|
|
553
|
+
return reject(new Error('could not pen stick'));
|
|
315
554
|
}
|
|
316
|
-
this.sensors.stick = stick;
|
|
317
|
-
this.sensors.stickOpen = opened;
|
|
318
555
|
}
|
|
319
556
|
if (this.sensors.stickOpen) {
|
|
320
|
-
console.log('create Sensor class');
|
|
321
557
|
if (!this.sensors.pending)
|
|
322
558
|
this.sensors.pending = [];
|
|
323
559
|
devices.forEach(device => {
|
|
324
560
|
const sensor = new SensorClass(this.sensors.stick);
|
|
561
|
+
device.setSensor(sensor);
|
|
562
|
+
device.setStick(stick);
|
|
325
563
|
sensor.on(message, (data) => { device.onDeviceData(data); });
|
|
326
564
|
sensor.on('eventData', (data) => { device.onDeviceEvent(data); });
|
|
565
|
+
sensor.once('attached', () => { device.onAttached(); });
|
|
327
566
|
this.sensors.pending.push({ device, sensor, message });
|
|
328
567
|
});
|
|
329
568
|
}
|
|
@@ -331,30 +570,62 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
331
570
|
if (!this.sensors.attached)
|
|
332
571
|
this.sensors.attached = [];
|
|
333
572
|
const channelsUsed = this.sensors.attached.length;
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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
|
+
}
|
|
343
590
|
};
|
|
344
591
|
if (this.sensors.stickStarted) {
|
|
345
592
|
attachFromPending();
|
|
346
593
|
}
|
|
347
594
|
else {
|
|
348
|
-
this.sensors
|
|
349
|
-
|
|
350
|
-
|
|
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;
|
|
351
602
|
setTimeout(attachFromPending, 1000);
|
|
352
603
|
});
|
|
353
604
|
}
|
|
354
|
-
|
|
355
|
-
});
|
|
605
|
+
}));
|
|
356
606
|
});
|
|
357
607
|
}
|
|
608
|
+
detachSensor(adapter) {
|
|
609
|
+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
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;
|
|
612
|
+
if (idx === -1)
|
|
613
|
+
return resolve(true);
|
|
614
|
+
this.sensors.attached.splice(idx, 1);
|
|
615
|
+
if (this.sensors.attached.length > 0)
|
|
616
|
+
return resolve(true);
|
|
617
|
+
const stick = this.sensors.stick;
|
|
618
|
+
if (stick === undefined)
|
|
619
|
+
return resolve(false);
|
|
620
|
+
try {
|
|
621
|
+
yield this.closeStick(stick);
|
|
622
|
+
resolve(true);
|
|
623
|
+
}
|
|
624
|
+
catch (err) {
|
|
625
|
+
reject(err);
|
|
626
|
+
}
|
|
627
|
+
}));
|
|
628
|
+
}
|
|
358
629
|
closeSensor(device) {
|
|
359
630
|
return __awaiter(this, void 0, void 0, function* () {
|
|
360
631
|
const stick = this.findStickByPort(device.getPort());
|
|
@@ -363,7 +634,7 @@ class AntProtocol extends DeviceProtocol_1.default {
|
|
|
363
634
|
stick.close();
|
|
364
635
|
}
|
|
365
636
|
catch (err) {
|
|
366
|
-
|
|
637
|
+
this.logger.logEvent({ message: 'closeSensor error', error: err.message, device: device ? device.getName() : 'unknown' });
|
|
367
638
|
}
|
|
368
639
|
}
|
|
369
640
|
});
|