incyclist-devices 2.0.32 → 2.0.33

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.
@@ -190,9 +190,7 @@ class DaumPremiumAdapter extends DaumAdapter_1.default {
190
190
  const { route, onStatusUpdate, gear } = props;
191
191
  var info = {};
192
192
  this.initData();
193
- if (isRelaunch) {
194
- yield this.stop();
195
- }
193
+ yield this.stop();
196
194
  return (0, utils_1.runWithRetries)(() => __awaiter(this, void 0, void 0, function* () {
197
195
  try {
198
196
  yield this.connect();
@@ -244,7 +242,7 @@ class DaumPremiumAdapter extends DaumAdapter_1.default {
244
242
  if (!this.bike.isConnected()) {
245
243
  const connected = yield this.bike.connect();
246
244
  if (!connected)
247
- return;
245
+ throw new Error('not connected');
248
246
  }
249
247
  return this.getBike().getTrainingData();
250
248
  });
@@ -1,13 +1,39 @@
1
1
  /// <reference types="node" />
2
2
  import { ReservedCommands } from './utils';
3
3
  import { SerialInterface, SerialPortProvider } from '../..';
4
- import { Queue } from '../../../utils/utils';
5
4
  import { EventLogger } from 'gd-eventlog';
6
5
  import { User } from "../../../types/user";
7
6
  import { Route } from "../../../types/route";
8
7
  import { SerialCommProps } from "../../comm";
9
8
  import { SerialPortStream } from "@serialport/stream";
10
9
  import { OnDeviceStartCallback } from "./types";
10
+ import { Queue } from "../../../utils/utils";
11
+ export type ResponseObject = {
12
+ type: ResponseType;
13
+ data?: string;
14
+ error?: Error;
15
+ };
16
+ export type ResponseType = 'ACK' | 'NAK' | 'Response' | 'Error';
17
+ export type Daum8iCommsState = {
18
+ waitingForStart?: boolean;
19
+ waitingForACK?: boolean;
20
+ waitingForEnd?: boolean;
21
+ partialCmd?: any;
22
+ data: Queue<ResponseObject>;
23
+ };
24
+ export type ConnectionState = 'Connecting' | 'Connected' | 'Disconnected' | 'Disconnecting';
25
+ export declare class CheckSumError extends Error {
26
+ constructor();
27
+ }
28
+ export declare class ACKTimeout extends Error {
29
+ constructor();
30
+ }
31
+ export declare class BusyTimeout extends Error {
32
+ constructor();
33
+ }
34
+ export declare class ResponseTimeout extends Error {
35
+ constructor();
36
+ }
11
37
  export default class Daum8i {
12
38
  logger: EventLogger;
13
39
  serial: SerialInterface;
@@ -18,17 +44,16 @@ export default class Daum8i {
18
44
  };
19
45
  port: string;
20
46
  settings: any;
21
- sp: SerialPortStream;
22
47
  props: any;
23
- connected: boolean;
24
- blocked: boolean;
25
- state: any;
48
+ protected sp: SerialPortStream;
49
+ protected connectState: ConnectionState;
50
+ protected connectPromise: Promise<SerialPortStream>;
51
+ protected disconnectPromise: Promise<boolean>;
52
+ protected writePromise: Promise<void>;
53
+ protected sendCmdPromise: Promise<string>;
54
+ protected actualBikeType?: string;
55
+ protected recvState: Daum8iCommsState;
26
56
  bikeData: any;
27
- processor: any;
28
- error: Error;
29
- queue: Queue<any>;
30
- cmdCurrent: any;
31
- cmdStart: number;
32
57
  isLoggingPaused: boolean;
33
58
  spp: SerialPortProvider;
34
59
  serialportProps: any;
@@ -40,29 +65,25 @@ export default class Daum8i {
40
65
  resumeLogging(): void;
41
66
  logEvent(e: any): void;
42
67
  connect(): Promise<boolean>;
68
+ closePort(): Promise<boolean>;
69
+ cleanupPort(): void;
43
70
  close(): Promise<boolean>;
44
71
  flush(): Promise<void>;
45
- getLogState(): {
46
- sending: any;
47
- busy: any;
48
- writeBusy: any;
49
- opening: any;
50
- connecting: any;
51
- waitingForStart: any;
52
- waitingForEnd: any;
53
- waitingForAck: any;
54
- retry: any;
55
- };
56
72
  onPortClose(): Promise<void>;
57
- onPortError(error: any): Promise<void>;
58
- forceClose(updateState?: boolean): Promise<void>;
59
- sendTimeout(message: any): void;
60
- checkForResponse(): boolean;
73
+ onPortError(error: Error): Promise<void>;
74
+ isSending(): boolean;
75
+ rejectCurrent(error: Error): void;
61
76
  getTimeoutValue(cmd?: any): number;
62
77
  onData(data: any, depth?: number): any;
78
+ waitWithTimeout(promise: Promise<any>, timeout: number, onTimeout?: () => void): Promise<any>;
63
79
  sendDaum8iCommand(command: string, payload?: string | any[]): Promise<string>;
64
- writeDone(): void;
65
- write(buffer: Buffer): Promise<void>;
80
+ onIgnored(payload: string): void;
81
+ onACK(): void;
82
+ onNAK: () => void;
83
+ waitForACK(): Promise<boolean>;
84
+ waitForResponse(): Promise<string>;
85
+ portWrite(buffer: Buffer): Promise<void>;
86
+ write(buffer: Buffer, ackExpected?: boolean): Promise<void>;
66
87
  sendACK(): Promise<void>;
67
88
  sendNAK(): Promise<void>;
68
89
  sendReservedDaum8iCommand(command: ReservedCommands, data?: Buffer): Promise<any[]>;
@@ -9,9 +9,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ResponseTimeout = exports.BusyTimeout = exports.ACKTimeout = exports.CheckSumError = void 0;
12
13
  const constants_1 = require("../constants");
13
14
  const utils_1 = require("./utils");
14
15
  const gd_eventlog_1 = require("gd-eventlog");
16
+ const utils_2 = require("../../../utils/utils");
15
17
  const DEFAULT_TIMEOUT = 10000;
16
18
  const MAX_DATA_BLOCK_SIZE = 512;
17
19
  const DS_BITS_OFF = 0;
@@ -34,8 +36,38 @@ const validatePath = (path) => {
34
36
  const port = parts[1];
35
37
  return `${host}:${port}`;
36
38
  };
39
+ class CheckSumError extends Error {
40
+ constructor() {
41
+ super();
42
+ this.message = 'checksum incorrect';
43
+ }
44
+ }
45
+ exports.CheckSumError = CheckSumError;
46
+ class ACKTimeout extends Error {
47
+ constructor() {
48
+ super();
49
+ this.message = 'ACK timeout';
50
+ }
51
+ }
52
+ exports.ACKTimeout = ACKTimeout;
53
+ class BusyTimeout extends Error {
54
+ constructor() {
55
+ super();
56
+ this.message = 'BUSY timeout';
57
+ }
58
+ }
59
+ exports.BusyTimeout = BusyTimeout;
60
+ class ResponseTimeout extends Error {
61
+ constructor() {
62
+ super();
63
+ this.message = 'RESP timeout';
64
+ }
65
+ }
66
+ exports.ResponseTimeout = ResponseTimeout;
37
67
  class Daum8i {
38
68
  constructor(props) {
69
+ this.onNAK = () => {
70
+ };
39
71
  this.props = props || {};
40
72
  const { logger, serial, path } = props;
41
73
  this.serial = serial;
@@ -43,12 +75,9 @@ class Daum8i {
43
75
  const w = global.window;
44
76
  this.logger = logger || ((w === null || w === void 0 ? void 0 : w.DEVICE_DEBUG) || process.env.DEBUG ? DEBUG_LOGGER : new gd_eventlog_1.EventLogger('DaumPremium'));
45
77
  this.isLoggingPaused = false;
46
- this.connected = false;
47
- this.blocked = false;
48
- this.state = {
49
- ack: { wait: false, startWait: undefined },
50
- commandsInQueue: {},
51
- };
78
+ this.connectState = 'Disconnected';
79
+ this.connectPromise = null;
80
+ this.recvState = { data: new utils_2.Queue() };
52
81
  this.settings = {};
53
82
  this.bikeData = {
54
83
  userWeight: 75,
@@ -64,7 +93,7 @@ class Daum8i {
64
93
  return this.path;
65
94
  }
66
95
  isConnected() {
67
- return this.connected;
96
+ return this.connectState === 'Connected' || this.connectState === 'Disconnecting';
68
97
  }
69
98
  pauseLogging() {
70
99
  this.isLoggingPaused = true;
@@ -86,159 +115,124 @@ class Daum8i {
86
115
  if (this.isConnected() && this.sp) {
87
116
  return true;
88
117
  }
118
+ if (this.connectState === 'Connecting') {
119
+ if (this.connectPromise) {
120
+ try {
121
+ yield this.connectPromise;
122
+ }
123
+ catch (_a) {
124
+ }
125
+ }
126
+ return this.isConnected();
127
+ }
89
128
  try {
90
- const port = yield this.serial.openPort(this.path);
129
+ this.connectState = 'Connecting';
130
+ this.connectPromise = this.serial.openPort(this.path);
131
+ const port = yield this.connectPromise;
132
+ this.connectPromise = null;
91
133
  if (port !== null) {
92
- this.connected = true;
134
+ this.connectState = 'Connected';
93
135
  this.sp = port;
94
136
  this.sp.on('close', this.onPortClose.bind(this));
95
- this.sp.on('error', (error) => { this.onPortError(error); });
96
- this.sp.on('data', (data) => { this.onData(data); });
137
+ this.sp.on('error', this.onPortError.bind(this));
138
+ this.sp.on('data', this.onData.bind(this));
97
139
  return true;
98
140
  }
99
141
  else {
142
+ this.connectState = 'Disconnected';
100
143
  return false;
101
144
  }
102
145
  }
103
- catch (_a) {
146
+ catch (_b) {
147
+ this.connectState = 'Disconnected';
104
148
  return false;
105
149
  }
106
150
  });
107
151
  }
152
+ closePort() {
153
+ return __awaiter(this, void 0, void 0, function* () {
154
+ if (!this.sp)
155
+ return true;
156
+ try {
157
+ yield this.flush();
158
+ yield this.serial.closePort(this.path);
159
+ return true;
160
+ }
161
+ catch (err) {
162
+ this.logEvent({ message: 'could not close ', reason: err.message });
163
+ return false;
164
+ }
165
+ });
166
+ }
167
+ cleanupPort() {
168
+ if (this.sp) {
169
+ this.sp.removeAllListeners();
170
+ }
171
+ this.sp = null;
172
+ this.recvState.data.clear();
173
+ }
108
174
  close() {
109
175
  return __awaiter(this, void 0, void 0, function* () {
110
- if (this.isConnected() && this.serial && this.sp) {
176
+ let isDisconnected = false;
177
+ if (this.disconnectPromise) {
111
178
  try {
112
- yield this.flush();
113
- yield this.serial.closePort(this.path);
179
+ isDisconnected = yield this.disconnectPromise;
114
180
  }
115
- catch (err) {
116
- this.logEvent({ message: 'could not close ', reason: err.message });
117
- return false;
181
+ catch (_a) {
118
182
  }
183
+ return isDisconnected;
119
184
  }
120
- this.connected = false;
121
- if (this.sp) {
122
- this.sp.removeAllListeners();
123
- this.sp = null;
185
+ if (this.connectState === 'Disconnected') {
186
+ this.cleanupPort();
187
+ return true;
124
188
  }
125
- return true;
189
+ else if (this.connectState === 'Disconnecting' || this.connectState === 'Connected' || this.connectState === 'Connecting') {
190
+ this.connectState = 'Disconnecting';
191
+ this.disconnectPromise = this.closePort();
192
+ isDisconnected = yield this.disconnectPromise;
193
+ this.connectPromise = null;
194
+ this.disconnectPromise = null;
195
+ if (isDisconnected)
196
+ this.connectState = 'Disconnected';
197
+ this.cleanupPort();
198
+ }
199
+ return isDisconnected;
126
200
  });
127
201
  }
128
202
  flush() {
129
203
  return __awaiter(this, void 0, void 0, function* () {
130
- if (!this.state.writeBusy)
131
- return;
132
- return new Promise(done => {
133
- const iv = setInterval(() => {
134
- if (!this.state.writeBusy) {
135
- clearInterval(iv);
136
- this.writeDone();
137
- done();
138
- }
139
- }, 100);
140
- });
204
+ if (this.writePromise) {
205
+ yield this.waitWithTimeout(this.writePromise, 1000);
206
+ this.writePromise = null;
207
+ }
141
208
  });
142
209
  }
143
- getLogState() {
144
- let s = undefined;
145
- const { sending, busy, opening, connecting, writeBusy, waitingForStart, waitingForAck, waitingForEnd, retry } = this.state;
146
- if (sending) {
147
- s = {};
148
- s.command = sending.command;
149
- s.payload = sending.payload;
150
- }
151
- return { sending: s, busy, writeBusy, opening, connecting, waitingForStart, waitingForEnd, waitingForAck, retry };
152
- }
153
210
  onPortClose() {
154
211
  return __awaiter(this, void 0, void 0, function* () {
155
- this.connected = false;
156
- if (this.sp) {
157
- this.sp.removeAllListeners();
158
- this.sp = null;
159
- }
212
+ if (this.connectState !== 'Disconnected' && this.connectState !== 'Disconnecting')
213
+ this.logEvent({ message: "port closed:", port: this.path });
214
+ this.connectState = 'Disconnected';
215
+ this.cleanupPort();
160
216
  });
161
217
  }
162
218
  onPortError(error) {
163
219
  return __awaiter(this, void 0, void 0, function* () {
164
- this.logEvent({ message: "port error:", port: this.path, error: error.message, connected: this.connected, state: this.getLogState() });
165
- this.error = error;
166
- if (this.blocked) {
167
- if (!this.state.closed) {
168
- yield this.close();
169
- }
220
+ if (this.connectState === 'Disconnecting' || this.connectState === 'Disconnected') {
170
221
  return;
171
222
  }
172
- if (this.state.sending) {
173
- if (this.state.sending.reject)
174
- this.state.sending.reject(error);
175
- this.writeDone();
176
- yield this.close();
177
- return;
223
+ this.logEvent({ message: "port error:", port: this.path, error: error.message, connected: this.isConnected(), state: this.connectState });
224
+ if (this.isSending()) {
225
+ this.rejectCurrent(error);
178
226
  }
179
- this.state.busy = false;
227
+ if (this.connectState === 'Connected')
228
+ this.close();
180
229
  });
181
230
  }
182
- forceClose(updateState = false) {
183
- return __awaiter(this, void 0, void 0, function* () {
184
- if (!this.sp)
185
- return;
186
- try {
187
- yield this.close();
188
- this.writeDone();
189
- if (this.queue !== undefined)
190
- this.queue.clear();
191
- }
192
- catch (_a) { }
193
- this.connected = false;
194
- if (updateState)
195
- this.state = { opened: false, closed: true, busy: false };
196
- });
197
- }
198
- sendTimeout(message) {
199
- this.logEvent({ message: `sendCommand:${message || 'timeout'}`, port: this.path, cmd: this.cmdCurrent });
200
- delete this.state.commandsInQueue[this.cmdCurrent.command];
201
- if (this.cmdCurrent.callbackErr !== undefined) {
202
- let cb = this.cmdCurrent.callbackErr;
203
- this.state.busy = false;
204
- this.cmdCurrent = undefined;
205
- this.cmdStart = undefined;
206
- cb(408, { message: message || "timeout" });
207
- }
231
+ isSending() {
232
+ return (this.writePromise !== undefined && this.writePromise !== null) || (this.sendCmdPromise !== null && this.sendCmdPromise !== undefined);
208
233
  }
209
- checkForResponse() {
210
- const d = Date.now();
211
- const s = this.state.sending;
212
- if (s === undefined)
213
- return false;
214
- const rejectFn = s.reject;
215
- const reject = (err) => {
216
- if (rejectFn && typeof rejectFn === 'function') {
217
- rejectFn(err);
218
- }
219
- };
220
- const error = this.state.error;
221
- if (error !== undefined) {
222
- reject(error);
223
- return false;
224
- }
225
- try {
226
- if (this.state.waitingForACK) {
227
- const timeoutACK = this.state.ack ? this.state.ack.timeout : this.state.sending.timeout;
228
- if (d < timeoutACK)
229
- return true;
230
- reject(new Error('ACK timeout'));
231
- return false;
232
- }
233
- if (d < this.state.sending.timeout)
234
- return true;
235
- reject(new Error('RESP timeout'));
236
- return false;
237
- }
238
- catch (err) {
239
- this.logEvent({ message: 'checkForResponse: Exception', port: this.path, error: err.message, stack: err.stack });
240
- }
241
- return true;
234
+ rejectCurrent(error) {
235
+ this.recvState.data.enqueue({ type: 'Error', error });
242
236
  }
243
237
  getTimeoutValue(cmd) {
244
238
  let timeout = DEFAULT_TIMEOUT;
@@ -253,18 +247,13 @@ class Daum8i {
253
247
  return __awaiter(this, void 0, void 0, function* () {
254
248
  let cmd = '';
255
249
  const MAX_DEPTH = 5;
256
- if (this.state.waitingForEnd) {
257
- cmd = this.state.partialCmd;
250
+ if (this.recvState.waitingForEnd) {
251
+ cmd = this.recvState.partialCmd;
258
252
  }
259
253
  const bufferData = Buffer.isBuffer(data) ? data : Buffer.from(data, 'latin1');
260
- const s = this.state.sending;
261
- if (s === undefined) {
262
- this.logEvent({ message: 'onData:IGNORED', data: bufferData.toString('hex') });
263
- return;
264
- }
265
- const { portName, resolve } = this.state.sending;
266
254
  let incoming = bufferData;
267
- this.logEvent({ message: 'sendCommand:RECV', data: (0, utils_1.hexstr)(incoming) });
255
+ if (depth === 0)
256
+ this.logEvent({ message: 'sendCommand:RECV', data: (0, utils_1.hexstr)(incoming), state: this.recvState });
268
257
  for (let i = 0; i < incoming.length; i++) {
269
258
  const getRemaining = () => {
270
259
  let remaining = '';
@@ -277,179 +266,222 @@ class Daum8i {
277
266
  };
278
267
  const c = incoming.readUInt8(i);
279
268
  if (c === 0x06) {
280
- this.state.waitingForStart = true;
281
- this.state.waitingForACK = false;
269
+ this.recvState.waitingForStart = true;
270
+ this.recvState.waitingForACK = false;
282
271
  const remaining = getRemaining();
283
- this.logEvent({ message: "sendCommand:ACK received:", port: portName, remaining: (0, utils_1.hexstr)(remaining) });
272
+ this.recvState.data.enqueue({ type: 'ACK' });
284
273
  if (remaining && remaining !== '' && depth < MAX_DEPTH)
285
274
  return this.onData(remaining, depth + 1);
286
275
  }
287
276
  else if (c === 0x15) {
288
- this.state.waitingForStart = true;
289
- this.state.waitingForACK = false;
277
+ this.recvState.waitingForStart = true;
278
+ this.recvState.waitingForACK = false;
290
279
  const remaining = getRemaining();
291
- this.logEvent({ message: "sendCommand:NAK received:", port: portName, remaining: (0, utils_1.hexstr)(remaining) });
280
+ this.recvState.data.enqueue({ type: 'NAK' });
292
281
  if (remaining && remaining !== '' && depth < MAX_DEPTH)
293
282
  return this.onData(remaining, depth + 1);
294
283
  }
295
284
  else if (c === 0x01) {
296
- this.state.waitingForEnd = true;
285
+ this.recvState.waitingForStart = false;
286
+ this.recvState.waitingForEnd = true;
297
287
  }
298
288
  else if (c === 0x17) {
299
289
  const remaining = getRemaining();
300
- if (this.state.waitingForACK) {
301
- this.logEvent({ message: "sendCommand:ignored:", duration: Date.now() - this.state.sending.tsRequest, port: portName, cmd: `${cmd} [${(0, utils_1.hexstr)(cmd)}]`, remaining: (0, utils_1.hexstr)(remaining) });
302
- this.state.waitingForEnd = false;
290
+ this.recvState.waitingForEnd = false;
291
+ const cmdStr = cmd.substring(0, cmd.length - 2);
292
+ const checksumExtracted = cmd.slice(-2);
293
+ const checksumCalculated = (0, utils_1.checkSum)((0, utils_1.getAsciiArrayFromStr)(cmdStr), []);
294
+ if (checksumExtracted === checksumCalculated) {
295
+ const payload = cmd.substring(3, cmd.length - 2);
296
+ this.recvState.data.enqueue({ type: 'Response', data: payload });
303
297
  }
304
298
  else {
305
- this.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) });
306
- this.state.waitingForEnd = false;
307
- const cmdStr = cmd.substring(0, cmd.length - 2);
308
- const checksumExtracted = cmd.slice(-2);
309
- const checksumCalculated = (0, utils_1.checkSum)((0, utils_1.getAsciiArrayFromStr)(cmdStr), []);
310
- if (checksumExtracted === checksumCalculated) {
311
- yield this.sendACK();
312
- if (this.state.sending && this.state.sending.responseCheckIv) {
313
- clearInterval(this.state.sending.responseCheckIv);
314
- }
315
- this.state = {
316
- sending: undefined,
317
- busy: false,
318
- writeBusy: false,
319
- waitingForStart: false,
320
- waitingForEnd: false,
321
- waitingForACK: false,
322
- };
323
- const payload = cmd.substring(3, cmd.length - 2);
324
- resolve(payload);
325
- }
326
- else {
327
- yield this.sendNAK();
328
- }
299
+ const error = new CheckSumError();
300
+ this.recvState.data.enqueue({ type: 'Error', error, data: cmd });
301
+ this.recvState.waitingForACK = false;
302
+ this.recvState.waitingForStart = true;
303
+ this.recvState.waitingForEnd = false;
329
304
  }
330
305
  cmd = '';
331
306
  if (remaining && depth < 5)
332
307
  return this.onData(remaining, depth + 1);
333
308
  }
334
309
  else {
335
- if (this.state.waitingForEnd)
310
+ if (this.recvState.waitingForEnd)
336
311
  cmd += String.fromCharCode(c);
337
312
  }
338
313
  }
339
- if (this.state.waitingForEnd) {
340
- this.state.partialCmd = cmd;
314
+ if (this.recvState.waitingForEnd) {
315
+ this.recvState.partialCmd = cmd;
341
316
  }
342
317
  });
343
318
  }
344
- sendDaum8iCommand(command, payload = '') {
345
- const tsRequest = Date.now();
346
- return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
347
- if (this.blocked)
348
- return reject(new Error('blocked'));
349
- if (!this.state.busy) {
350
- this.state.busy = true;
319
+ waitWithTimeout(promise, timeout, onTimeout) {
320
+ return __awaiter(this, void 0, void 0, function* () {
321
+ let to;
322
+ const toPromise = (ms) => {
323
+ return new Promise(resolve => { to = setTimeout(resolve, ms); });
324
+ };
325
+ let res;
326
+ try {
327
+ res = yield Promise.race([promise, toPromise(timeout).then(() => { if (onTimeout)
328
+ onTimeout(); })]);
351
329
  }
352
- else {
353
- const message = (0, utils_1.buildMessage)(command, payload);
330
+ catch (_a) {
331
+ }
332
+ clearTimeout(to);
333
+ return res;
334
+ });
335
+ }
336
+ sendDaum8iCommand(command, payload = '') {
337
+ return __awaiter(this, void 0, void 0, function* () {
338
+ const message = (0, utils_1.buildMessage)(command, payload);
339
+ const tsRequest = Date.now();
340
+ if (this.sendCmdPromise) {
354
341
  this.logEvent({ message: 'sendCommand:waiting', port: this.path, cmd: command, hex: (0, utils_1.hexstr)(message) });
355
- const busyWait = () => {
356
- return new Promise((done) => {
357
- let start = Date.now();
358
- let timeout = start + 5000;
359
- const iv = setInterval(() => {
360
- if (this.state.busy) {
361
- if (Date.now() > timeout) {
362
- clearInterval(iv);
363
- done(false);
364
- }
365
- }
366
- else {
367
- clearInterval(iv);
368
- done(true);
369
- }
370
- }, 10);
371
- });
372
- };
373
- const res = yield busyWait();
374
- if (!res) {
342
+ const onTimeout = () => {
375
343
  this.logEvent({ message: 'sendCommand:busy timeout', port: this.path, cmd: command, hex: (0, utils_1.hexstr)(message), duration: Date.now() - tsRequest });
376
- return reject(new Error('BUSY timeout'));
344
+ throw new Error('BUSY timeout');
345
+ };
346
+ this.waitWithTimeout(this.sendCmdPromise, 5000, onTimeout);
347
+ this.sendCmdPromise = null;
348
+ }
349
+ this.sendCmdPromise = new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
350
+ try {
351
+ this.logEvent({ message: "sendCommand:sending:", port: this.path, cmd: command, hex: (0, utils_1.hexstr)(message) });
352
+ if (!this.isConnected()) {
353
+ const connected = yield this.connect();
354
+ if (!connected) {
355
+ reject(new Error('not connected'));
356
+ return;
357
+ }
358
+ }
359
+ let retryCnt = 0;
360
+ let ok = false;
361
+ do {
362
+ yield this.write(Buffer.from(message));
363
+ ok = yield this.waitForACK();
364
+ if (!ok) {
365
+ yield (0, utils_2.sleep)(1000);
366
+ retryCnt++;
367
+ }
368
+ } while (!ok && retryCnt < 5);
369
+ const res = yield this.waitForResponse();
370
+ this.sendCmdPromise = null;
371
+ resolve(res);
372
+ }
373
+ catch (err) {
374
+ this.logEvent({ message: "sendCommand:error:", port: this.path, error: err.message, stack: err.stack });
375
+ this.sendCmdPromise = null;
376
+ reject(err);
377
+ }
378
+ }));
379
+ return this.sendCmdPromise;
380
+ });
381
+ }
382
+ onIgnored(payload) {
383
+ this.logEvent({ message: 'onData:IGNORED', port: this.path, data: payload });
384
+ }
385
+ onACK() {
386
+ }
387
+ waitForACK() {
388
+ return __awaiter(this, void 0, void 0, function* () {
389
+ this.recvState.waitingForACK = true;
390
+ const timeout = this.getTimeoutValue();
391
+ let waitingForACK = true;
392
+ let start = Date.now();
393
+ let tsTimeout = start + timeout;
394
+ while (waitingForACK && Date.now() < tsTimeout) {
395
+ const response = this.recvState.data.dequeue();
396
+ if (!response) {
397
+ yield (0, utils_2.sleep)(5);
398
+ }
399
+ else {
400
+ if (response.type === 'ACK' || response.type === 'NAK') {
401
+ this.logEvent({ message: `sendCommand:${response.type} received:`, port: this.path });
402
+ waitingForACK = false;
403
+ return (response.type === 'ACK');
404
+ }
377
405
  }
378
- this.state.busy = true;
379
406
  }
380
- const port = this.sp;
381
- const portName = this.path;
382
- this.state.received = [];
383
- try {
384
- const message = (0, utils_1.buildMessage)(command, payload);
385
- const start = Date.now();
386
- const timeout = start + this.getTimeoutValue();
387
- this.logEvent({ message: "sendCommand:sending:", port: this.path, cmd: command, hex: (0, utils_1.hexstr)(message) });
388
- this.state.writeBusy = true;
389
- if (!this.connected || port === undefined) {
390
- this.logEvent({ message: "sendCommand:error: not connected", port: this.path });
391
- this.writeDone();
392
- return reject(new Error('not connected'));
407
+ throw new ACKTimeout();
408
+ });
409
+ }
410
+ waitForResponse() {
411
+ return __awaiter(this, void 0, void 0, function* () {
412
+ const timeout = this.getTimeoutValue();
413
+ let waitingForResponse = true;
414
+ let start = Date.now();
415
+ let tsTimeout = start + timeout;
416
+ let retry = 0;
417
+ while (waitingForResponse && Date.now() < tsTimeout && retry < 5) {
418
+ const response = this.recvState.data.dequeue();
419
+ if (!response) {
420
+ yield (0, utils_2.sleep)(5);
393
421
  }
394
- yield this.write(Buffer.from(message));
395
- this.state.waitingForACK = true;
396
- this.state.writeBusy = false;
397
- this.state.retry = 0;
398
- this.state.ack = { start, timeout };
399
- this.state.sending = { command, payload, start, timeout, port, portName, tsRequest, resolve, reject };
400
- const iv = setInterval(() => {
401
- const stillWaiting = this.checkForResponse();
402
- if (!stillWaiting) {
403
- clearInterval(iv);
404
- this.writeDone();
422
+ else {
423
+ if (response.type === 'Response') {
424
+ this.logEvent({ message: `sendCommand:received:`, port: this.path, cmd: response.data });
425
+ yield this.sendACK();
426
+ waitingForResponse = false;
427
+ return response.data;
428
+ }
429
+ if (response.type === 'Error') {
430
+ this.logEvent({ message: `sendCommand:received:ERROR`, port: this.path, error: response.error.message });
431
+ if (response.error instanceof CheckSumError && retry < 5) {
432
+ yield this.sendNAK();
433
+ retry++;
434
+ }
435
+ else {
436
+ throw response.error;
437
+ }
405
438
  }
406
- }, 10);
407
- this.state.sending.responseCheckIv = iv;
439
+ }
440
+ }
441
+ throw new ResponseTimeout();
442
+ });
443
+ }
444
+ portWrite(buffer) {
445
+ return __awaiter(this, void 0, void 0, function* () {
446
+ if (!this.sp) {
447
+ this.logEvent({ message: 'write failed', error: 'port is not opened' });
448
+ return;
449
+ }
450
+ try {
451
+ yield this.sp.write(buffer);
408
452
  }
409
453
  catch (err) {
410
- this.logEvent({ message: "sendCommand:error:", port: portName, error: err.message, stack: err.stack });
411
- this.writeDone();
412
- reject(err);
454
+ this.logEvent({ message: 'write failed', error: err.message });
413
455
  }
414
- }));
415
- }
416
- writeDone() {
417
- this.state.writeBusy = false;
418
- this.state.busy = false;
419
- if (this.state.sending && this.state.sending.responseCheckIv) {
420
- clearInterval(this.state.sending.responseCheckIv);
421
- }
422
- this.state.sending = undefined;
423
- this.state.waitingForStart = false;
424
- this.state.waitingForEnd = false;
425
- this.state.waitingForACK = false;
456
+ });
426
457
  }
427
- write(buffer) {
458
+ write(buffer, ackExpected = true) {
428
459
  return __awaiter(this, void 0, void 0, function* () {
429
- return new Promise((done) => __awaiter(this, void 0, void 0, function* () {
430
- this.state.writeBusy = true;
460
+ if (this.writePromise) {
431
461
  try {
432
- yield this.sp.write(buffer);
433
- this.state.writeBusy = false;
434
- done();
462
+ yield this.writePromise;
435
463
  }
436
- catch (err) {
437
- this.state.writeBusy = false;
438
- done();
464
+ catch (_a) {
439
465
  }
440
- }));
466
+ this.writePromise = null;
467
+ }
468
+ this.writePromise = this.portWrite(buffer);
469
+ if (ackExpected)
470
+ this.recvState.waitingForACK = true;
471
+ yield this.writePromise;
472
+ this.writePromise = null;
441
473
  });
442
474
  }
443
475
  sendACK() {
444
476
  return __awaiter(this, void 0, void 0, function* () {
445
- this.logEvent({ message: "sendCommand:sending ACK", port: this.path, queue: this.state.commandsInQueue });
446
- yield this.write(Buffer.from([0x06]));
477
+ this.logEvent({ message: "sendCommand:sending ACK", port: this.path });
478
+ yield this.write(Buffer.from([0x06]), false);
447
479
  });
448
480
  }
449
481
  sendNAK() {
450
482
  return __awaiter(this, void 0, void 0, function* () {
451
- this.logEvent({ message: "sendCommand:sending NAK", port: this.path, queue: this.state.commandsInQueue });
452
- yield this.write(Buffer.from([0x15]));
483
+ this.logEvent({ message: "sendCommand:sending NAK", port: this.path });
484
+ yield this.write(Buffer.from([0x15]), false);
453
485
  });
454
486
  }
455
487
  sendReservedDaum8iCommand(command, data) {
@@ -511,7 +543,7 @@ class Daum8i {
511
543
  else {
512
544
  throw (new Error(`unknown actual device type ${typeof str === 'string' ? (0, utils_1.ascii)(str.charAt(0)) : str}`));
513
545
  }
514
- this.state.actualBikeType = deviceType;
546
+ this.actualBikeType = deviceType;
515
547
  return deviceType;
516
548
  });
517
549
  }
@@ -544,7 +576,7 @@ class Daum8i {
544
576
  deviceType = constants_1.ACTUAL_BIKE_TYPE.MOUNTAIN;
545
577
  else
546
578
  throw (new Error('unknown actual device type'));
547
- this.state.actualBikeType = deviceType;
579
+ this.actualBikeType = deviceType;
548
580
  return deviceType;
549
581
  });
550
582
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incyclist-devices",
3
- "version": "2.0.32",
3
+ "version": "2.0.33",
4
4
  "dependencies": {
5
5
  "@serialport/bindings-interface": "^1.2.2",
6
6
  "@serialport/parser-byte-length": "^9.0.1",