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