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.
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 +11 -2
  13. package/lib/ant/AntAdapter.js +30 -1
  14. package/lib/ant/AntScanner.d.ts +17 -6
  15. package/lib/ant/AntScanner.js +406 -135
  16. package/lib/ant/antfe/AntFEAdapter.d.ts +12 -8
  17. package/lib/ant/antfe/AntFEAdapter.js +404 -182
  18. package/lib/ant/anthrm/AntHrmAdapter.d.ts +4 -2
  19. package/lib/ant/anthrm/AntHrmAdapter.js +72 -25
  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,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
- 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;
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
- 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
- stick.once('shutdown', () => {
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(() => { stick.close(); }, 1000);
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
- stick.once('startup', () => {
186
- if (!port || (this.activeScans[port] && this.activeScans[port].isScanning))
187
- return;
188
- if (!this.activeScans[port]) {
189
- this.activeScans[port] = { isScanning: false, stick };
190
- }
191
- const state = this.activeScans[port];
192
- if (state.isScanning)
193
- return;
194
- state.isScanning = true;
195
- this.logger.logEvent({ message: 'start scan', port });
196
- state.timeout = Date.now() + timeout;
197
- const onNewDevice = (profile, deviceId) => {
198
- this.logger.logEvent({ message: 'found device', profile, id: deviceId });
199
- const profileInfo = this.profiles.find(i => i.name === profile);
200
- if (profileInfo) {
201
- let device;
202
- try {
203
- 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)
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
- const onData = (profile, deviceId, data) => {
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
- const hrm = new AntProfile('Heartrate Monitor', this.ant.HeartRateScanner, stick, 'hbData', onNewDevice, onData);
228
- const fe = new AntProfile('Smart Trainer', this.ant.FitnessEquipmentScanner, stick, 'fitnessData', onNewDevice, onData);
229
- 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 {
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
- if (!opened) {
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
- 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);
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
- const sticks = yield this.getSticks();
262
- if (sticks && sticks.length > 0) {
263
- 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
+ }
264
479
  this.scanOnStick(stick, props);
265
480
  }
266
- else {
267
- this.logger.logEvent({ message: 'no stick found' });
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
- console.log('scan stopped');
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
- const stick = this.findStickByPort(devices[0].getPort());
300
- let opened = false;
301
- if (!stick.inUse) {
530
+ this.logger.logEvent({ message: 'openStick', device: devices[0].getName() });
531
+ let retryCnt = 0;
532
+ while (!stick && retryCnt < 3) {
302
533
  try {
303
- const open = stick.open();
304
- console.log('opened', open);
305
- 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
+ }
306
546
  }
307
547
  catch (err) {
308
- console.log(err);
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
- else {
314
- opened = true;
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
- this.sensors.pending.forEach((i, idx) => {
335
- const channel = channelsUsed + idx;
336
- const { sensor } = i;
337
- console.log('attach ', channel, i.device.getID());
338
- sensor.attach(channel, i.device.getID());
339
- i.device.setChannel(channel);
340
- this.sensors.attached.push(i);
341
- });
342
- this.sensors.pending = [];
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.stick.once('startup', () => {
349
- console.log('started');
350
- 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;
351
602
  setTimeout(attachFromPending, 1000);
352
603
  });
353
604
  }
354
- resolve(true);
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
- console.log(err);
637
+ this.logger.logEvent({ message: 'closeSensor error', error: err.message, device: device ? device.getName() : 'unknown' });
367
638
  }
368
639
  }
369
640
  });