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
@@ -8,46 +8,50 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
11
14
  Object.defineProperty(exports, "__esModule", { value: true });
12
15
  exports.Daum8iSerial = exports.Daum8iTcp = void 0;
13
- const indoorbike_js_1 = require("../indoorbike.js");
14
- const constants_js_1 = require("../constants.js");
15
- const tcpserial_1 = require("./tcpserial");
16
+ const constants_1 = require("../constants");
17
+ const tcpserial_1 = __importDefault(require("./tcpserial"));
16
18
  const utils_1 = require("./utils");
17
- const utils_2 = require("../../utils");
18
19
  const gd_eventlog_1 = require("gd-eventlog");
19
20
  const nop = () => { };
20
21
  const MAX_RETRIES = 5;
21
22
  const DEFAULT_TIMEOUT = 10000;
22
23
  const DEFAULT_SEND_DELAY = 1000;
23
- const OPEN_RETRY_DELAY = 3000;
24
- const CLOSE_RETRY_TIMEOUT = 5000;
25
24
  const TIMEOUT_START = 15000;
25
+ const OPEN_TIMEOUT = 1000;
26
26
  const DAUM_PREMIUM_DEFAULT_PORT = 51955;
27
27
  const DAUM_PREMIUM_DEFAULT_HOST = '127.0.0.1';
28
28
  var __SerialPort = undefined;
29
29
  var net = undefined;
30
+ const DEBUG_LOGGER = {
31
+ log: (e, ...args) => console.log(e, ...args),
32
+ logEvent: (event) => console.log(JSON.stringify(event))
33
+ };
30
34
  class Daum8i {
31
35
  constructor(props) {
32
- const opts = props || {};
33
- this.LOG = new gd_eventlog_1.EventLogger('DaumPremium');
34
- this.LOG.logEvent({ message: 'new DaumPremium object', opts });
35
- if (opts.interface === 'tcpip') {
36
- const port = opts.port || DAUM_PREMIUM_DEFAULT_PORT;
37
- const host = opts.host || DAUM_PREMIUM_DEFAULT_HOST;
38
- this.portName = `${host}:${port}`;
36
+ this.props = props || {};
37
+ this.logger = process.env.DEBUG ? DEBUG_LOGGER : new gd_eventlog_1.EventLogger('DaumPremium');
38
+ this.logger.logEvent({ message: 'new DaumPremium object', props: this.props });
39
+ if (this.props.interface === 'tcpip') {
40
+ const port = this.props.port || DAUM_PREMIUM_DEFAULT_PORT;
41
+ const host = this.props.host || DAUM_PREMIUM_DEFAULT_HOST;
42
+ this.portName = `${host}:51955`;
39
43
  this.tcpip = true;
40
44
  this.serial = false;
41
45
  this.tcpipConnection = { host, port };
42
46
  }
43
47
  else {
44
- this.portName = opts.port || process.env.COM_PORT;
48
+ this.portName = this.props.port || process.env.COM_PORT;
45
49
  this.tcpip = false;
46
50
  this.serial = true;
47
51
  this.port = this.portName;
48
52
  }
49
- this.settings = opts.settings || {};
50
- this.settings.logger = this.LOG;
53
+ this.settings = this.props.settings || {};
54
+ this.settings.logger = this.logger;
51
55
  this.sendRetryDelay = DEFAULT_SEND_DELAY;
52
56
  this.sp = undefined;
53
57
  this.connected = false;
@@ -61,7 +65,6 @@ class Daum8i {
61
65
  bikeWeight: 10,
62
66
  maxPower: 800
63
67
  };
64
- this.processor = new indoorbike_js_1.default(this);
65
68
  }
66
69
  static getClassName() {
67
70
  return "Daum8i";
@@ -76,7 +79,7 @@ class Daum8i {
76
79
  net = netClass;
77
80
  }
78
81
  static getSupportedInterfaces() {
79
- return [constants_js_1.BIKE_INTERFACE.SERIAL, constants_js_1.BIKE_INTERFACE.TCPIP];
82
+ return [constants_1.BIKE_INTERFACE.SERIAL, constants_1.BIKE_INTERFACE.TCPIP];
80
83
  }
81
84
  getPort() {
82
85
  return this.portName;
@@ -85,7 +88,7 @@ class Daum8i {
85
88
  return this.connected;
86
89
  }
87
90
  setUser(user, callback) {
88
- this.LOG.logEvent({ message: "setUser()", user, port: this.portName });
91
+ this.logger.logEvent({ message: "setUser()", user, port: this.portName });
89
92
  this.settings.user = user || {};
90
93
  var cb = callback || nop;
91
94
  cb(200, user);
@@ -101,43 +104,49 @@ class Daum8i {
101
104
  unblock() {
102
105
  this.blocked = false;
103
106
  }
104
- connect(retry) {
105
- this.LOG.logEvent({ message: "connect()", sp: (this.sp !== undefined), port: this.portName, retry, settings: this.settings, scanMode: this.scanMode });
107
+ connect() {
108
+ this.logger.logEvent({ message: "connect()", sp: (this.sp !== undefined), connected: this.connected, blocked: this.blocked, port: this.portName, settings: this.settings });
106
109
  if (this.connected || this.blocked) {
107
110
  return;
108
111
  }
109
112
  this.state.busy = true;
110
113
  this.state.commandsInQueue = {};
111
114
  try {
115
+ if (this.sp !== undefined) {
116
+ try {
117
+ this.sp.removeAllListeners();
118
+ this.sp.close();
119
+ }
120
+ catch (err) {
121
+ }
122
+ this.sp = undefined;
123
+ }
112
124
  if (this.sp === undefined) {
113
125
  if (this.tcpip) {
114
126
  const { host, port } = this.tcpipConnection;
115
- this.sp = new tcpserial_1.default({ host, port, net });
127
+ const { logger } = this.props;
128
+ this.logger.logEvent({ message: "creating TCPSocketPort", host, port });
129
+ this.sp = new tcpserial_1.default({ host, port, net, timeout: OPEN_TIMEOUT, logger });
116
130
  }
117
131
  else {
118
132
  const settings = this.settings.port || {};
119
133
  settings.autoOpen = false;
134
+ this.logger.logEvent({ message: "creating SerialPort", port: this.port, settings });
120
135
  this.sp = new __SerialPort(this.port, settings);
121
136
  }
122
137
  this.sp.on('open', this.onPortOpen.bind(this));
123
138
  this.sp.on('close', this.onPortClose.bind(this));
124
139
  this.sp.on('error', (error) => { this.onPortError(error); });
125
140
  this.sp.on('data', (data) => { this.onData(data); });
126
- this.firstOpen = true;
127
141
  }
128
142
  const start = Date.now();
129
143
  this.state.connecting = true;
130
- if (!this.state.opening)
131
- this.state.opening = { start, timeout: start + this.getTimeoutValue(), retry: 0, maxRetries: MAX_RETRIES };
132
- else {
133
- this.state.opening.start = start;
134
- this.state.opening.timeout = start + this.getTimeoutValue();
135
- this.state.opening.retry = this.state.opening.retry + 1;
136
- }
144
+ this.state.opening = { start, timeout: start + this.getTimeoutValue() };
145
+ this.logger.logEvent({ message: "opening port ..." });
137
146
  this.sp.open();
138
147
  }
139
148
  catch (err) {
140
- this.LOG.logEvent({ message: "scan:error:", error: err.message, stack: err.stack });
149
+ this.logger.logEvent({ message: "scan:error:", error: err.message, stack: err.stack });
141
150
  this.state.busy = false;
142
151
  }
143
152
  }
@@ -156,16 +165,26 @@ class Daum8i {
156
165
  this.connect();
157
166
  const tTimeout = Date.now() + TIMEOUT_START;
158
167
  const iv = setInterval(() => {
159
- if (this.isConnected()) {
168
+ if (this.state.error !== undefined) {
169
+ console.log('~~ -> error');
170
+ clearInterval(iv);
171
+ this.forceClose();
172
+ reject(this.state.error);
173
+ this.state = { opened: false, closed: true, busy: false };
174
+ }
175
+ else if (this.isConnected()) {
176
+ console.log('~~ -> connected');
160
177
  this.state.connecting = false;
161
178
  resolve(true);
162
179
  clearInterval(iv);
163
180
  }
164
181
  else {
165
182
  if (Date.now() > tTimeout) {
183
+ console.log('~~ -> timeout');
166
184
  this.state.connecting = false;
167
- reject(new Error('timeout'));
185
+ this.forceClose();
168
186
  clearInterval(iv);
187
+ reject(new Error('timeout'));
169
188
  }
170
189
  }
171
190
  }, 100);
@@ -177,10 +196,10 @@ class Daum8i {
177
196
  this.state.opening = undefined;
178
197
  this.state.opened = true;
179
198
  this.state.busy = false;
180
- this.LOG.logEvent({ message: "port opened", port: this.portName });
199
+ this.logger.logEvent({ message: "port opened", port: this.portName });
181
200
  }
182
201
  onPortClose() {
183
- this.LOG.logEvent({ message: "port closed", port: this.portName });
202
+ this.logger.logEvent({ message: "port closed", port: this.portName });
184
203
  this.error = undefined;
185
204
  this.connected = false;
186
205
  if (this.state.opening) {
@@ -190,81 +209,56 @@ class Daum8i {
190
209
  else {
191
210
  this.state = { opened: false, closed: true, busy: false };
192
211
  }
212
+ this.sp.removeAllListeners();
193
213
  this.sp = undefined;
194
214
  if (this.queue !== undefined)
195
215
  this.queue.clear();
196
216
  }
217
+ getLogState() {
218
+ let s = undefined;
219
+ const { sending, busy, opening, connecting, writeBusy, waitingForStart, waitingForAck, waitingForEnd, retry } = this.state;
220
+ if (sending) {
221
+ s = {};
222
+ s.command = sending.command;
223
+ s.payload = sending.payload;
224
+ }
225
+ return { sending: s, busy, writeBusy, opening, connecting, waitingForStart, waitingForEnd, waitingForAck, retry };
226
+ }
197
227
  onPortError(error) {
198
- this.LOG.logEvent({ message: "port error:", port: this.portName, error: error.message, connected: this.connected, state: this.state });
228
+ this.logger.logEvent({ message: "port error:", port: this.portName, error: error.message, connected: this.connected, state: this.getLogState() });
199
229
  this.error = error;
200
230
  if (this.blocked) {
201
231
  if (!this.state.closed) {
202
- if (this.sp)
232
+ if (this.sp) {
233
+ this.sp.removeAllListeners();
203
234
  this.sp.close();
235
+ this.sp = undefined;
236
+ }
204
237
  this.state = { opened: false, closed: true, busy: false };
205
238
  }
206
239
  return;
207
240
  }
208
- const reconnect = () => {
209
- if (this.state.opening && !this.state.closing) {
210
- this.LOG.logEvent({ message: "retry connection:", portName: this.port, connected: this.connected, scanMode: this.scanMode });
211
- this.connect(true);
212
- }
213
- };
214
241
  if (this.state.closing) {
215
242
  if (error.message === 'Port is not open') {
216
243
  this.state = { opened: false, closed: true, busy: false };
217
244
  return;
218
245
  }
219
246
  else {
220
- const { retry, maxRetries } = this.state.closing;
221
- if ((retry + 1 < maxRetries)) {
222
- this.state.closing.retry = retry + 1;
223
- return setTimeout(() => { this.close(); }, CLOSE_RETRY_TIMEOUT);
224
- }
225
- else {
226
- this.LOG.logEvent({ message: "close request failed - giving up", port: this.portName });
227
- this.state.closing = undefined;
228
- }
247
+ this.forceClose();
229
248
  }
230
249
  }
231
250
  else if (this.state.opening) {
232
- if (!this.state.connect) {
233
- const { retry, maxRetries } = this.state.opening;
234
- try {
235
- if (this.sp)
236
- this.sp.close();
237
- }
238
- catch (err) {
239
- console.log(err);
240
- }
241
- if ((retry + 1) < maxRetries) {
242
- this.state.opening.retry = retry + 1;
243
- setTimeout(() => {
244
- if (!this.state.busy)
245
- reconnect();
246
- else {
247
- const iv = setInterval(() => {
248
- if (!this.state.busy) {
249
- clearInterval(iv);
250
- reconnect();
251
- }
252
- }, 50);
253
- }
254
- }, OPEN_RETRY_DELAY);
255
- }
256
- else {
257
- this.state.opening = undefined;
258
- }
251
+ if (this.state.connecting) {
252
+ this.state.error = error;
259
253
  }
260
254
  else {
261
255
  this.onPortOpen();
262
256
  }
263
257
  }
264
258
  else if (this.state.sending) {
265
- this.LOG.logEvent({ message: "closing port", port: this.portName });
266
- this.sp.close();
267
- this.state = { opened: true, closed: false, busy: true };
259
+ this.state.error = error;
260
+ this.forceClose(false);
261
+ return;
268
262
  }
269
263
  this.state.busy = false;
270
264
  }
@@ -286,36 +280,47 @@ class Daum8i {
286
280
  }, 50);
287
281
  });
288
282
  }
289
- close() {
290
- this.LOG.logEvent({ message: 'close request', port: this.portName });
291
- var port = this.sp;
292
- if (this.bikeCmdWorker !== undefined) {
293
- this.LOG.logEvent({ message: "stopping worker", port: this.portName });
294
- clearInterval(this.bikeCmdWorker);
295
- this.bikeCmdWorker = undefined;
283
+ forceClose(updateState = false) {
284
+ console.log('~~~ forceClose', updateState);
285
+ const sp = this.sp;
286
+ if (!this.sp)
287
+ return;
288
+ this.sp.removeAllListeners();
289
+ try {
290
+ sp.unpipe();
291
+ sp.flush();
296
292
  }
293
+ catch (_a) { }
294
+ sp.close();
295
+ this.connected = false;
296
+ if (updateState)
297
+ this.state = { opened: false, closed: true, busy: false };
298
+ }
299
+ close() {
300
+ this.logger.logEvent({ message: 'close request', port: this.portName });
301
+ var sp = this.sp;
297
302
  let connected = this.connected;
298
303
  try {
299
304
  if (connected) {
300
- if (port) {
301
- port.unpipe();
302
- port.flush();
303
- port.close();
305
+ if (sp) {
306
+ sp.unpipe();
307
+ sp.flush();
308
+ sp.close();
304
309
  }
305
- this.connected = false;
306
310
  if (this.queue !== undefined) {
307
311
  this.queue.clear();
308
312
  this.queue = undefined;
309
313
  }
310
314
  }
311
315
  else {
312
- if (port)
313
- port.close();
316
+ if (sp)
317
+ sp.close();
314
318
  }
315
319
  }
316
320
  catch (err) {
317
- this.LOG.logEvent({ message: 'close: Exception', port: this.portName, error: err.message });
321
+ this.logger.logEvent({ message: 'close: Exception', port: this.portName, error: err.message });
318
322
  }
323
+ this.connected = false;
319
324
  const start = Date.now();
320
325
  if (this.state.closing === undefined)
321
326
  this.state.closing = { start, timeout: start + this.getTimeoutValue(), retry: 0, maxRetries: MAX_RETRIES };
@@ -327,7 +332,7 @@ class Daum8i {
327
332
  this.state.busy = false;
328
333
  }
329
334
  sendTimeout(message) {
330
- this.LOG.logEvent({ message: `sendCommand:${message || 'timeout'}`, port: this.portName, cmd: this.cmdCurrent });
335
+ this.logger.logEvent({ message: `sendCommand:${message || 'timeout'}`, port: this.portName, cmd: this.cmdCurrent });
331
336
  delete this.state.commandsInQueue[this.cmdCurrent.command];
332
337
  if (this.cmdCurrent.callbackErr !== undefined) {
333
338
  let cb = this.cmdCurrent.callbackErr;
@@ -337,29 +342,39 @@ class Daum8i {
337
342
  cb(408, { message: message || "timeout" });
338
343
  }
339
344
  }
340
- checkForTimeout(reject) {
345
+ checkForResponse() {
341
346
  const d = Date.now();
342
347
  const s = this.state.sending;
343
348
  if (s === undefined)
344
- return;
349
+ return false;
350
+ const rejectFn = s.reject;
351
+ const reject = (err) => {
352
+ if (rejectFn && typeof rejectFn === 'function') {
353
+ rejectFn(err);
354
+ }
355
+ };
356
+ const error = this.state.error;
357
+ if (error !== undefined) {
358
+ reject(error);
359
+ return false;
360
+ }
345
361
  try {
346
- if (!this.state.sending)
347
- return;
348
362
  if (this.state.waitingForACK) {
349
363
  const timeoutACK = this.state.ack ? this.state.ack.timeout : this.state.sending.timeout;
350
364
  if (d < timeoutACK)
351
- return;
365
+ return true;
352
366
  reject(new Error('ACK timeout'));
353
- return;
367
+ return false;
354
368
  }
355
369
  if (d < this.state.sending.timeout)
356
- return;
357
- reject(new Error('timeout'));
358
- return;
370
+ return true;
371
+ reject(new Error('RESP timeout'));
372
+ return false;
359
373
  }
360
374
  catch (err) {
361
- this.LOG.logEvent({ message: 'checkForTimeout: Exception', port: this.portName, error: err.message, stack: err.stack });
375
+ this.logger.logEvent({ message: 'checkForResponse: Exception', port: this.portName, error: err.message, stack: err.stack });
362
376
  }
377
+ return true;
363
378
  }
364
379
  getTimeoutValue(cmd) {
365
380
  let timeout = DEFAULT_TIMEOUT;
@@ -374,103 +389,153 @@ class Daum8i {
374
389
  }
375
390
  onData(data) {
376
391
  let cmd = '';
377
- if (!Buffer.isBuffer(data)) {
378
- data = Buffer.from(data, 'latin1');
392
+ if (this.state.waitingForEnd) {
393
+ cmd = this.state.partialCmd;
379
394
  }
395
+ const bufferData = Buffer.isBuffer(data) ? data : Buffer.from(data, 'latin1');
380
396
  const s = this.state.sending;
381
397
  if (s === undefined) {
382
398
  if (this.state.input === undefined)
383
- this.state.input = Buffer.from(data);
399
+ this.state.input = bufferData;
384
400
  return;
385
401
  }
386
402
  const { portName, resolve } = this.state.sending;
387
403
  let incoming;
388
404
  if (this.state.input !== undefined) {
389
- const arr = [this.state.input, data];
405
+ const arr = [this.state.input, bufferData];
390
406
  incoming = Buffer.concat(arr);
391
407
  }
392
408
  else {
393
- incoming = data;
409
+ incoming = bufferData;
394
410
  }
411
+ const response = [...incoming];
412
+ this.logger.logEvent({ message: 'sendCommand:RECV', data: (0, utils_1.hexstr)(response) });
395
413
  for (let i = 0; i < incoming.length; i++) {
414
+ const getRemaining = () => {
415
+ let remaining = '';
416
+ const done = i === (incoming.length - 1);
417
+ if (!done) {
418
+ for (let j = i + 1; j < incoming.length; j++)
419
+ remaining += String.fromCharCode(incoming.readUInt8(j));
420
+ }
421
+ return remaining;
422
+ };
396
423
  const c = incoming.readUInt8(i);
397
424
  if (c === 0x06) {
398
- this.LOG.logEvent({ message: "sendCommand:ACK received:", port: portName });
425
+ this.logger.logEvent({ message: "sendCommand:ACK received:", port: portName });
399
426
  this.state.waitingForStart = true;
400
427
  this.state.waitingForACK = false;
428
+ const remaining = getRemaining();
429
+ if (remaining && remaining !== '')
430
+ return this.onData(remaining);
401
431
  }
402
432
  else if (c === 0x15) {
403
433
  this.state.waitingForStart = true;
404
434
  this.state.waitingForACK = false;
405
- this.LOG.logEvent({ message: "sendCommand:NAK received:", port: portName });
435
+ this.logger.logEvent({ message: "sendCommand:NAK received:", port: portName });
436
+ const remaining = getRemaining();
437
+ if (remaining && remaining !== '')
438
+ return this.onData(remaining);
406
439
  }
407
440
  else if (c === 0x01) {
408
441
  this.state.waitingForEnd = true;
409
442
  }
410
443
  else if (c === 0x17) {
411
- this.LOG.logEvent({ message: "sendCommand:received:", port: portName, cmd: `${cmd} [${utils_1.hexstr(cmd)}]` });
412
- if (this.state.sending.timeoutCheckIv)
413
- clearInterval(this.state.sending.timeoutCheckIv);
414
- this.state = {
415
- sending: undefined,
416
- busy: false,
417
- writeBusy: false,
418
- waitingForStart: false,
419
- waitingForEnd: false,
420
- waitingForACK: false,
421
- };
422
- this.sendACK();
423
- const payload = cmd.substring(3, cmd.length - 2);
424
- resolve(payload);
444
+ const remaining = getRemaining();
445
+ this.logger.logEvent({ message: "sendCommand:received:", duration: Date.now() - this.state.sending.tsRequest, port: portName, cmd: `${cmd} [${(0, utils_1.hexstr)(cmd)}]`, remaining: (0, utils_1.hexstr)(remaining) });
446
+ this.state.waitingForEnd = false;
447
+ const cmdStr = cmd.substring(0, cmd.length - 2);
448
+ const checksumExtracted = cmd.slice(-2);
449
+ const checksumCalculated = (0, utils_1.checkSum)((0, utils_1.getAsciiArrayFromStr)(cmdStr), []);
450
+ if (checksumExtracted === checksumCalculated) {
451
+ this.sendACK();
452
+ if (this.state.sending && this.state.sending.responseCheckIv) {
453
+ clearInterval(this.state.sending.responseCheckIv);
454
+ }
455
+ this.state = {
456
+ sending: undefined,
457
+ busy: false,
458
+ writeBusy: false,
459
+ waitingForStart: false,
460
+ waitingForEnd: false,
461
+ waitingForACK: false,
462
+ };
463
+ const payload = cmd.substring(3, cmd.length - 2);
464
+ resolve(payload);
465
+ }
466
+ else {
467
+ this.sendNAK();
468
+ }
425
469
  cmd = '';
470
+ if (remaining)
471
+ return this.onData(remaining);
426
472
  }
427
473
  else {
428
474
  if (this.state.waitingForEnd)
429
475
  cmd += String.fromCharCode(c);
430
476
  }
431
477
  }
478
+ if (this.state.waitingForEnd) {
479
+ this.state.partialCmd = cmd;
480
+ }
432
481
  }
433
- sendDaum8iCommand(command, queryType, payload, cb) {
482
+ sendDaum8iCommand(command, queryType, payload) {
483
+ const tsRequest = Date.now();
434
484
  return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
435
485
  if (this.blocked)
436
- reject(new Error('blocked'));
486
+ return reject(new Error('blocked'));
437
487
  if (!this.state.busy) {
438
488
  this.state.busy = true;
439
- if (!this.connected) {
440
- reject(new Error('not connected'));
441
- if (!this.state.connecting) {
442
- this.saveConnect(true)
443
- .then(() => { this.state.busy = false; })
444
- .catch((reason) => { this.state.busy = reason === 'busy'; });
445
- }
446
- return;
447
- }
448
489
  }
449
490
  else {
450
- let start = Date.now();
451
- while (this.state.busy && Date.now() - start < 5000) {
452
- yield utils_2.sleep(500);
453
- }
454
- if (this.state.busy) {
455
- reject(new Error('timeout'));
456
- return;
491
+ const message = (0, utils_1.buildMessage)(command, payload);
492
+ this.logger.logEvent({ message: 'sendCommand:waiting', port: this.portName, cmd: command, hex: (0, utils_1.hexstr)(message) });
493
+ const busyWait = () => {
494
+ return new Promise((done) => {
495
+ let start = Date.now();
496
+ let timeout = start + 5000;
497
+ const iv = setInterval(() => {
498
+ if (this.state.busy) {
499
+ if (Date.now() > timeout) {
500
+ clearInterval(iv);
501
+ done(false);
502
+ }
503
+ }
504
+ else {
505
+ clearInterval(iv);
506
+ done(true);
507
+ }
508
+ }, 10);
509
+ });
510
+ };
511
+ const res = yield busyWait();
512
+ if (!res) {
513
+ this.logger.logEvent({ message: 'sendCommand:busy timeout', port: this.portName, cmd: command, hex: (0, utils_1.hexstr)(message), duration: Date.now() - tsRequest });
514
+ return reject(new Error('BUSY timeout'));
457
515
  }
458
516
  this.state.busy = true;
459
517
  }
518
+ const writeDone = () => {
519
+ this.state.sending = undefined;
520
+ this.state.writeBusy = false;
521
+ this.state.busy = false;
522
+ this.state.sending = undefined;
523
+ this.state.waitingForStart = false;
524
+ this.state.waitingForEnd = false;
525
+ this.state.waitingForACK = false;
526
+ };
460
527
  const port = this.sp;
461
528
  const portName = this.portName;
462
529
  this.state.received = [];
463
530
  try {
464
- const message = utils_1.buildMessage(command, payload);
531
+ const message = (0, utils_1.buildMessage)(command, payload);
465
532
  const start = Date.now();
466
533
  const timeout = start + this.getTimeoutValue();
467
- this.LOG.logEvent({ message: "sendCommand:sending:", port: this.portName, cmd: command, hex: utils_1.hexstr(message) });
534
+ this.logger.logEvent({ message: "sendCommand:sending:", port: this.portName, cmd: command, hex: (0, utils_1.hexstr)(message) });
468
535
  this.state.writeBusy = true;
469
- if (!this.connected) {
470
- this.LOG.logEvent({ message: "sendCommand:error: not connected", port: this.portName });
471
- this.state.writeBusy = false;
472
- this.state.busy = false;
473
- this.state.sending = undefined;
536
+ if (!this.connected || port === undefined) {
537
+ this.logger.logEvent({ message: "sendCommand:error: not connected", port: this.portName });
538
+ writeDone();
474
539
  return reject(new Error('not connected'));
475
540
  }
476
541
  port.write(message);
@@ -478,34 +543,18 @@ class Daum8i {
478
543
  this.state.writeBusy = false;
479
544
  this.state.retry = 0;
480
545
  this.state.ack = { start, timeout };
481
- this.state.sending = { command, payload, start, timeout, port, portName, resolve, reject };
482
- this.state.sending.timeoutCheckIv = setInterval(() => {
483
- this.checkForTimeout((err) => {
484
- console.log(err.message, this.state.retry, this.state.retryBusy);
485
- if (this.state.retry > 2) {
486
- clearInterval(this.state.sending.timeoutCheckIv);
487
- this.state.sending.timeoutCheckIv = undefined;
488
- return reject(err);
489
- }
490
- if (this.state.retryBusy)
491
- return;
492
- this.state.retryBusy = true;
493
- this.state.retry++;
494
- setTimeout(() => {
495
- const restart = Date.now();
496
- this.state.ack = { start: restart, timeout: restart + this.getTimeoutValue() };
497
- this.LOG.logEvent({ message: "sendCommand:retry:", port: this.portName, cmd: command, hex: utils_1.hexstr(message) });
498
- port.write(message);
499
- this.state.retryBusy = false;
500
- }, this.sendRetryDelay);
501
- });
502
- });
546
+ this.state.sending = { command, payload, start, timeout, port, portName, tsRequest, resolve, reject };
547
+ const iv = this.state.sending.responseCheckIv = setInterval(() => {
548
+ const stillWaiting = this.checkForResponse();
549
+ if (!stillWaiting) {
550
+ clearInterval(iv);
551
+ writeDone();
552
+ }
553
+ }, 10);
503
554
  }
504
555
  catch (err) {
505
- this.LOG.logEvent({ message: "sendCommand:error:", port: portName, error: err.message, stack: err.stack });
506
- this.state.writeBusy = false;
507
- this.state.busy = false;
508
- this.state.sending = undefined;
556
+ this.logger.logEvent({ message: "sendCommand:error:", port: portName, error: err.message, stack: err.stack });
557
+ writeDone();
509
558
  reject(err);
510
559
  }
511
560
  }));
@@ -518,7 +567,7 @@ class Daum8i {
518
567
  }
519
568
  catch (err) { }
520
569
  this.state.writeBusy = false;
521
- this.LOG.logEvent({ message: "sendCommand:sending ACK", port, queue: this.state.commandsInQueue });
570
+ this.logger.logEvent({ message: "sendCommand:sending ACK", port, queue: this.state.commandsInQueue });
522
571
  }
523
572
  sendNAK() {
524
573
  const port = this.portName;
@@ -526,22 +575,22 @@ class Daum8i {
526
575
  this.sp.write([0x15]);
527
576
  }
528
577
  catch (err) { }
529
- this.LOG.logEvent({ message: "sendCommand:sending NAK", port });
578
+ this.logger.logEvent({ message: "sendCommand:sending NAK", port });
530
579
  }
531
- sendReservedDaum8iCommand(command, cmdType, data, onData, onError) {
580
+ sendReservedDaum8iCommand(command, cmdType, data) {
532
581
  let cmdData = [];
533
- const key = utils_1.getReservedCommandKey(command);
534
- utils_1.append(cmdData, utils_1.Int16ToIntArray(key));
582
+ const key = (0, utils_1.getReservedCommandKey)(command);
583
+ (0, utils_1.append)(cmdData, (0, utils_1.Int16ToIntArray)(key));
535
584
  if (data !== undefined && data.length > 0) {
536
- utils_1.append(cmdData, utils_1.Int16ToIntArray(data.length));
537
- utils_1.append(cmdData, data);
585
+ (0, utils_1.append)(cmdData, (0, utils_1.Int16ToIntArray)(data.length));
586
+ (0, utils_1.append)(cmdData, data);
538
587
  }
539
588
  else {
540
- utils_1.append(cmdData, utils_1.Int16ToIntArray(0));
589
+ (0, utils_1.append)(cmdData, (0, utils_1.Int16ToIntArray)(0));
541
590
  }
542
- return this.sendDaum8iCommand('M70', cmdType, utils_1.bin2esc(cmdData))
591
+ return this.sendDaum8iCommand('M70', cmdType, (0, utils_1.bin2esc)(cmdData))
543
592
  .then((resData) => {
544
- const cmd = utils_1.esc2bin(resData);
593
+ const cmd = (0, utils_1.esc2bin)(resData);
545
594
  cmd.splice(0, 4);
546
595
  return cmd;
547
596
  });
@@ -567,7 +616,7 @@ class Daum8i {
567
616
  else if (str === '7')
568
617
  deviceType = 'lyps';
569
618
  else
570
- throw (new Error(`unknown device type ${typeof str === 'string' ? utils_1.ascii(str.charAt(0)) : str}`));
619
+ throw (new Error(`unknown device type ${typeof str === 'string' ? (0, utils_1.ascii)(str.charAt(0)) : str}`));
571
620
  return deviceType;
572
621
  });
573
622
  }
@@ -576,13 +625,13 @@ class Daum8i {
576
625
  .then((str) => {
577
626
  let deviceType;
578
627
  if (str === '0')
579
- deviceType = constants_js_1.ACTUAL_BIKE_TYPE.ALLROUND;
628
+ deviceType = constants_1.ACTUAL_BIKE_TYPE.ALLROUND;
580
629
  else if (str === '1')
581
- deviceType = constants_js_1.ACTUAL_BIKE_TYPE.RACE;
630
+ deviceType = constants_1.ACTUAL_BIKE_TYPE.RACE;
582
631
  else if (str === '2')
583
- deviceType = constants_js_1.ACTUAL_BIKE_TYPE.MOUNTAIN;
632
+ deviceType = constants_1.ACTUAL_BIKE_TYPE.MOUNTAIN;
584
633
  else {
585
- throw (new Error(`unknown actual device type ${typeof str === 'string' ? utils_1.ascii(str.charAt(0)) : str}`));
634
+ throw (new Error(`unknown actual device type ${typeof str === 'string' ? (0, utils_1.ascii)(str.charAt(0)) : str}`));
586
635
  }
587
636
  this.state.actualBikeType = deviceType;
588
637
  return deviceType;
@@ -591,16 +640,16 @@ class Daum8i {
591
640
  setActualBikeType(actualBikeType) {
592
641
  let bikeType;
593
642
  switch (actualBikeType) {
594
- case constants_js_1.ACTUAL_BIKE_TYPE.ALLROUND:
643
+ case constants_1.ACTUAL_BIKE_TYPE.ALLROUND:
595
644
  bikeType = '0';
596
645
  break;
597
- case constants_js_1.ACTUAL_BIKE_TYPE.RACE:
646
+ case constants_1.ACTUAL_BIKE_TYPE.RACE:
598
647
  bikeType = '1';
599
648
  break;
600
- case constants_js_1.ACTUAL_BIKE_TYPE.TRIATHLON:
649
+ case constants_1.ACTUAL_BIKE_TYPE.TRIATHLON:
601
650
  bikeType = '1';
602
651
  break;
603
- case constants_js_1.ACTUAL_BIKE_TYPE.MOUNTAIN:
652
+ case constants_1.ACTUAL_BIKE_TYPE.MOUNTAIN:
604
653
  bikeType = '2';
605
654
  break;
606
655
  default:
@@ -610,11 +659,11 @@ class Daum8i {
610
659
  .then((str) => {
611
660
  let deviceType;
612
661
  if (str === '0')
613
- deviceType = constants_js_1.ACTUAL_BIKE_TYPE.ALLROUND;
662
+ deviceType = constants_1.ACTUAL_BIKE_TYPE.ALLROUND;
614
663
  else if (str === '1')
615
- deviceType = constants_js_1.ACTUAL_BIKE_TYPE.RACE;
664
+ deviceType = constants_1.ACTUAL_BIKE_TYPE.RACE;
616
665
  else if (str === '2')
617
- deviceType = constants_js_1.ACTUAL_BIKE_TYPE.MOUNTAIN;
666
+ deviceType = constants_1.ACTUAL_BIKE_TYPE.MOUNTAIN;
618
667
  else
619
668
  throw (new Error('unknown actual device type'));
620
669
  this.state.actualBikeType = deviceType;
@@ -624,12 +673,12 @@ class Daum8i {
624
673
  getTrainingData() {
625
674
  return this.sendDaum8iCommand('X70', 'AF', [])
626
675
  .then((data) => {
627
- const td = utils_1.parseTrainingData(data);
676
+ const td = (0, utils_1.parseTrainingData)(data);
628
677
  return td;
629
678
  });
630
679
  }
631
680
  setLoadControl(enabled) {
632
- const val = enabled ? utils_1.ascii('1') : utils_1.ascii('0');
681
+ const val = enabled ? (0, utils_1.ascii)('1') : (0, utils_1.ascii)('0');
633
682
  return this.sendDaum8iCommand('S20', 'BF', [val])
634
683
  .then((data) => {
635
684
  const res = data === '1';
@@ -644,7 +693,7 @@ class Daum8i {
644
693
  });
645
694
  }
646
695
  setSlope(slope) {
647
- this.LOG.logEvent({ message: 'setSlope not implemted' });
696
+ this.logger.logEvent({ message: 'setSlope not implemted' });
648
697
  return;
649
698
  }
650
699
  setPower(power) {
@@ -681,23 +730,25 @@ class Daum8iTcp extends Daum8i {
681
730
  static getClassName() { return "Daum8i"; }
682
731
  getType() { return "Daum8iTcp"; }
683
732
  static setSerialPort(spClass) { }
733
+ getInterface() { return constants_1.BIKE_INTERFACE.TCPIP; }
684
734
  static setNetImpl(netClass) {
685
735
  net = netClass;
686
736
  }
687
737
  static getSupportedInterfaces() {
688
- return [constants_js_1.BIKE_INTERFACE.TCPIP];
738
+ return [constants_1.BIKE_INTERFACE.TCPIP];
689
739
  }
690
740
  }
691
741
  exports.Daum8iTcp = Daum8iTcp;
692
742
  class Daum8iSerial extends Daum8i {
693
743
  static getClassName() { return "Daum8i"; }
694
744
  getType() { return "Daum8iSerial"; }
745
+ getInterface() { return constants_1.BIKE_INTERFACE.SERIAL; }
695
746
  static setSerialPort(spClass) {
696
747
  __SerialPort = spClass;
697
748
  }
698
749
  static setNetImpl(netClass) { }
699
750
  static getSupportedInterfaces() {
700
- return [constants_js_1.BIKE_INTERFACE.SERIAL];
751
+ return [constants_1.BIKE_INTERFACE.SERIAL];
701
752
  }
702
753
  }
703
754
  exports.Daum8iSerial = Daum8iSerial;