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.
Files changed (71) hide show
  1. package/lib/CyclingMode.d.ts +72 -0
  2. package/lib/CyclingMode.js +66 -0
  3. package/lib/Device.d.ts +48 -10
  4. package/lib/Device.js +9 -8
  5. package/lib/DeviceProtocol.d.ts +40 -12
  6. package/lib/DeviceProtocol.js +16 -16
  7. package/lib/DeviceRegistry.d.ts +4 -4
  8. package/lib/DeviceRegistry.js +10 -10
  9. package/lib/DeviceSupport.d.ts +4 -3
  10. package/lib/DeviceSupport.js +32 -8
  11. package/lib/ant/AntAdapter.d.ts +7 -3
  12. package/lib/ant/AntAdapter.js +23 -3
  13. package/lib/ant/AntScanner.d.ts +15 -6
  14. package/lib/ant/AntScanner.js +372 -128
  15. package/lib/ant/antfe/AntFEAdapter.d.ts +1 -1
  16. package/lib/ant/antfe/AntFEAdapter.js +191 -92
  17. package/lib/ant/anthrm/AntHrmAdapter.d.ts +3 -1
  18. package/lib/ant/anthrm/AntHrmAdapter.js +70 -19
  19. package/lib/ant/utils.js +2 -1
  20. package/lib/calculations.d.ts +12 -13
  21. package/lib/calculations.js +88 -125
  22. package/lib/daum/DaumAdapter.d.ts +29 -6
  23. package/lib/daum/DaumAdapter.js +219 -96
  24. package/lib/daum/ERGCyclingMode.d.ts +28 -0
  25. package/lib/daum/ERGCyclingMode.js +207 -0
  26. package/lib/daum/PowerMeterCyclingMode.d.ts +18 -0
  27. package/lib/daum/PowerMeterCyclingMode.js +79 -0
  28. package/lib/daum/SmartTrainerCyclingMode.d.ts +41 -0
  29. package/lib/daum/SmartTrainerCyclingMode.js +344 -0
  30. package/lib/daum/classic/DaumClassicAdapter.d.ts +3 -1
  31. package/lib/daum/classic/DaumClassicAdapter.js +46 -32
  32. package/lib/daum/classic/DaumClassicCyclingMode.d.ts +13 -0
  33. package/lib/daum/classic/DaumClassicCyclingMode.js +98 -0
  34. package/lib/daum/classic/DaumClassicProtocol.d.ts +5 -3
  35. package/lib/daum/classic/DaumClassicProtocol.js +47 -8
  36. package/lib/daum/classic/ERGCyclingMode.d.ts +23 -0
  37. package/lib/daum/classic/ERGCyclingMode.js +171 -0
  38. package/lib/daum/classic/bike.d.ts +41 -37
  39. package/lib/daum/classic/bike.js +86 -53
  40. package/lib/daum/classic/utils.d.ts +3 -3
  41. package/lib/daum/classic/utils.js +18 -10
  42. package/lib/daum/indoorbike.d.ts +2 -1
  43. package/lib/daum/indoorbike.js +23 -21
  44. package/lib/daum/premium/DaumPremiumAdapter.d.ts +2 -2
  45. package/lib/daum/premium/DaumPremiumAdapter.js +30 -20
  46. package/lib/daum/premium/DaumPremiumProtocol.d.ts +11 -2
  47. package/lib/daum/premium/DaumPremiumProtocol.js +57 -10
  48. package/lib/daum/premium/bike.d.ts +63 -52
  49. package/lib/daum/premium/bike.js +254 -207
  50. package/lib/daum/premium/tcpserial.d.ts +18 -14
  51. package/lib/daum/premium/tcpserial.js +44 -20
  52. package/lib/daum/premium/utils.d.ts +2 -2
  53. package/lib/simulator/Simulator.d.ts +13 -7
  54. package/lib/simulator/Simulator.js +62 -21
  55. package/lib/utils.d.ts +3 -1
  56. package/lib/utils.js +39 -18
  57. package/package.json +12 -11
  58. package/lib/ant/AntScanner.unit.tests.d.ts +0 -1
  59. package/lib/ant/AntScanner.unit.tests.js +0 -25
  60. package/lib/ant/antfe/AntFEProcessor.d.ts +0 -40
  61. package/lib/ant/antfe/AntFEProcessor.js +0 -238
  62. package/lib/ant/antfe/AntHrmProtocol.d.ts +0 -9
  63. package/lib/ant/antfe/AntHrmProtocol.js +0 -30
  64. package/lib/ant/antfe/FEDevice.d.ts +0 -1
  65. package/lib/ant/antfe/FEDevice.js +0 -7
  66. package/lib/ant/antfe/bike.d.ts +0 -47
  67. package/lib/ant/antfe/bike.js +0 -602
  68. package/lib/ant/anthrm/anthrm.d.ts +0 -33
  69. package/lib/ant/anthrm/anthrm.js +0 -523
  70. package/lib/simulator/Simulator.unit.tests.d.ts +0 -1
  71. package/lib/simulator/Simulator.unit.tests.js +0 -79
@@ -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
- getStick() {
113
- if (!this.ant)
114
- return;
115
- const stick2 = new this.ant.GarminStick2();
116
- if (stick2.is_present() && stick2.open()) {
117
- this.logger.logEvent({ message: 'found GarminStick2' });
118
- return stick2;
119
- }
120
- const stick3 = new this.ant.GarminStick3();
121
- if (stick3.is_present() && stick3.open()) {
122
- this.logger.logEvent({ message: 'found GarminStick3' });
123
- return stick3;
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
- getSticks() {
182
+ getStick(onStart, onError) {
128
183
  return __awaiter(this, void 0, void 0, function* () {
129
184
  if (!this.ant)
130
185
  return;
131
- let done = false;
132
- const sticks = [];
133
- while (!done) {
134
- const stick = this.getStick();
135
- if (stick) {
136
- const port = this.getUSBDeviceInfo(stick.device).port;
137
- sticks.push(stick);
138
- if (!this.sticks.find(i => i.port === port)) {
139
- 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;
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
- else
143
- done = true;
144
- }
145
- const stickInfos = sticks.map(stick => { return { stick, port: this.getUSBDeviceInfo(stick.device).port }; });
146
- for (let i = 0; i < sticks.length; i++) {
147
- yield this.closeStick(sticks[i]);
148
- }
149
- return stickInfos;
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
- stick.once('startup', () => {
195
- if (!port || (this.activeScans[port] && this.activeScans[port].isScanning))
196
- return;
197
- if (!this.activeScans[port]) {
198
- this.activeScans[port] = { isScanning: false, stick };
199
- }
200
- const state = this.activeScans[port];
201
- if (state.isScanning)
202
- return;
203
- state.isScanning = true;
204
- this.logger.logEvent({ message: 'start scan', port });
205
- state.timeout = Date.now() + timeout;
206
- const onNewDevice = (profile, deviceId) => {
207
- this.logger.logEvent({ message: 'found device', profile, id: deviceId });
208
- const profileInfo = this.profiles.find(i => i.name === profile);
209
- if (profileInfo) {
210
- let device;
211
- try {
212
- device = new profileInfo.Adapter(deviceId, port, stick, this, props);
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
- const onData = (profile, deviceId, data) => {
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
- const hrm = new AntProfile('Heartrate Monitor', this.ant.HeartRateScanner, stick, 'hbData', onNewDevice, onData);
237
- const fe = new AntProfile('Smart Trainer', this.ant.FitnessEquipmentScanner, stick, 'fitnessData', onNewDevice, onData);
238
- const power = new AntProfile('Power Meter', this.ant.BicyclePowerScanner, stick, 'powerData', onNewDevice, onData);
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
- if (!opened) {
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
- this.logger.log('stick opened', port);
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
- const sticks = yield this.getSticks();
271
- if (sticks && sticks.length > 0) {
272
- const stick = sticks[0];
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
- else {
276
- this.logger.logEvent({ message: 'no stick found' });
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
- const stick = this.findStickByPort(devices[0].getPort());
307
- let opened = false;
308
- if (!stick.inUse) {
530
+ this.logger.logEvent({ message: 'openStick', device: devices[0].getName() });
531
+ let retryCnt = 0;
532
+ while (!stick && retryCnt < 3) {
309
533
  try {
310
- stick.open();
311
- opened = true;
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
- console.log(err);
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
- else {
320
- opened = true;
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
- this.sensors.pending.forEach((i, idx) => {
342
- const channel = channelsUsed + idx;
343
- const { sensor } = i;
344
- i.device.setChannel(channel);
345
- sensor.attach(channel, i.device.getID());
346
- this.sensors.attached.push(i);
347
- });
348
- this.sensors.pending = [];
349
- resolve(true);
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.stick.once('startup', () => {
356
- this.sensors.stickStarted = true;
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.attached.findIndex(i => (i.device.getID() === adapter.getID() && i.device.getName() === adapter.getName()));
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
- console.log(err);
635
+ this.logger.logEvent({ message: 'closeSensor error', error: err.message, device: device ? device.getName() : 'unknown' });
392
636
  }
393
637
  }
394
638
  });