@microbit/microbit-connection 0.0.0-alpha.1 → 0.0.0-alpha.11

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 (57) hide show
  1. package/build/accelerometer-service.d.ts +10 -9
  2. package/build/accelerometer-service.js +61 -27
  3. package/build/accelerometer-service.js.map +1 -1
  4. package/build/bluetooth-device-wrapper.d.ts +30 -14
  5. package/build/bluetooth-device-wrapper.js +143 -27
  6. package/build/bluetooth-device-wrapper.js.map +1 -1
  7. package/build/bluetooth-utils.d.ts +5 -0
  8. package/build/bluetooth-utils.js +14 -0
  9. package/build/bluetooth-utils.js.map +1 -0
  10. package/build/bluetooth.d.ts +12 -13
  11. package/build/bluetooth.js +59 -75
  12. package/build/bluetooth.js.map +1 -1
  13. package/build/board-id.d.ts +2 -1
  14. package/build/board-id.js +8 -0
  15. package/build/board-id.js.map +1 -1
  16. package/build/board-serial-info.d.ts +1 -1
  17. package/build/board-serial-info.js +1 -1
  18. package/build/board-serial-info.js.map +1 -1
  19. package/build/button-service.d.ts +13 -0
  20. package/build/button-service.js +76 -0
  21. package/build/button-service.js.map +1 -0
  22. package/build/buttons.d.ts +10 -0
  23. package/build/buttons.js +18 -0
  24. package/build/buttons.js.map +1 -0
  25. package/build/constants.d.ts +1 -0
  26. package/build/constants.js +1 -0
  27. package/build/constants.js.map +1 -1
  28. package/build/device.d.ts +19 -29
  29. package/build/device.js +19 -2
  30. package/build/device.js.map +1 -1
  31. package/build/hex-flash-data-source.d.ts +7 -9
  32. package/build/hex-flash-data-source.js +16 -44
  33. package/build/hex-flash-data-source.js.map +1 -1
  34. package/build/index.d.ts +10 -7
  35. package/build/index.js +7 -6
  36. package/build/index.js.map +1 -1
  37. package/build/logging.js +3 -1
  38. package/build/logging.js.map +1 -1
  39. package/build/service-events.d.ts +7 -3
  40. package/build/service-events.js +12 -0
  41. package/build/service-events.js.map +1 -1
  42. package/build/usb-device-wrapper.d.ts +9 -3
  43. package/build/usb-device-wrapper.js +41 -4
  44. package/build/usb-device-wrapper.js.map +1 -1
  45. package/build/usb-partial-flashing-utils.js +1 -1
  46. package/build/usb-partial-flashing-utils.js.map +1 -1
  47. package/build/usb-partial-flashing.d.ts +13 -7
  48. package/build/usb-partial-flashing.js +55 -12
  49. package/build/usb-partial-flashing.js.map +1 -1
  50. package/build/usb-radio-bridge.d.ts +28 -19
  51. package/build/usb-radio-bridge.js +284 -192
  52. package/build/usb-radio-bridge.js.map +1 -1
  53. package/build/usb.d.ts +13 -14
  54. package/build/usb.js +117 -48
  55. package/build/usb.js.map +1 -1
  56. package/package.json +3 -2
  57. package/vite.config.ts +1 -1
@@ -3,26 +3,35 @@
3
3
  *
4
4
  * SPDX-License-Identifier: MIT
5
5
  */
6
- import { MicrobitWebUSBConnection } from "./usb";
7
- import { Logging } from "./logging";
8
- export declare class MicrobitRadioBridgeConnection {
9
- private usb;
6
+ import { BoardVersion, ConnectionStatus, DeviceConnection, DeviceConnectionEventMap } from "./device.js";
7
+ import { TypedEventTarget } from "./events.js";
8
+ import { Logging } from "./logging.js";
9
+ import { ServiceConnectionEventMap } from "./service-events.js";
10
+ import { MicrobitWebUSBConnection } from "./usb.js";
11
+ export interface MicrobitRadioBridgeConnectionOptions {
12
+ logging: Logging;
13
+ }
14
+ /**
15
+ * Wraps around a USB connection to implement a subset of services over a serial protocol.
16
+ *
17
+ * When it connects/disconnects it affects the delegate connection.
18
+ */
19
+ export declare class MicrobitRadioBridgeConnection extends TypedEventTarget<DeviceConnectionEventMap & ServiceConnectionEventMap> implements DeviceConnection {
20
+ private delegate;
21
+ status: ConnectionStatus;
10
22
  private logging;
23
+ private serialSession;
11
24
  private remoteDeviceId;
12
- private responseMap;
13
- private isConnecting;
14
- private connectionCheckIntervalId;
15
- private lastReceivedMessageTimestamp;
16
- private isReconnect;
17
- private finalAttempt;
18
- constructor(usb: MicrobitWebUSBConnection, logging: Logging, remoteDeviceId: number);
19
- connect(): Promise<void>;
25
+ private delegateStatusListner;
26
+ constructor(delegate: MicrobitWebUSBConnection, options?: MicrobitRadioBridgeConnectionOptions);
27
+ getBoardVersion(): BoardVersion | undefined;
28
+ serialWrite(data: string): Promise<void>;
29
+ initialize(): Promise<void>;
30
+ dispose(): void;
31
+ clearDevice(): void;
32
+ setRemoteDeviceId(remoteDeviceId: number): void;
33
+ connect(): Promise<ConnectionStatus>;
20
34
  disconnect(): Promise<void>;
21
- private stopConnectionCheck;
22
- private disconnectInternal;
23
- handleReconnect(): Promise<void>;
24
- reconnect(finalAttempt?: boolean): Promise<void>;
25
- private sendCmdWaitResponse;
26
- private handshake;
35
+ private setStatus;
36
+ private statusFromDelegate;
27
37
  }
28
- export declare const startSerialConnection: (logging: Logging, usb: MicrobitWebUSBConnection, remoteDeviceId: number) => Promise<MicrobitRadioBridgeConnection | undefined>;
@@ -1,23 +1,168 @@
1
- // @ts-nocheck
2
1
  /**
3
2
  * (c) 2023, Center for Computational Thinking and Design at Aarhus University and contributors
4
3
  *
5
4
  * SPDX-License-Identifier: MIT
6
5
  */
7
- import * as protocol from "./usb-serial-protocol";
6
+ import { AccelerometerDataEvent } from "./accelerometer.js";
7
+ import { ButtonEvent, ButtonState } from "./buttons.js";
8
+ import { ConnectionStatus, ConnectionStatusEvent, } from "./device.js";
9
+ import { TypedEventTarget } from "./events.js";
10
+ import { NullLogging } from "./logging.js";
11
+ import * as protocol from "./usb-serial-protocol.js";
8
12
  const connectTimeoutDuration = 10000;
9
13
  class BridgeError extends Error {
10
14
  }
11
15
  class RemoteError extends Error {
12
16
  }
13
- export class MicrobitRadioBridgeConnection {
14
- constructor(usb, logging, remoteDeviceId) {
15
- Object.defineProperty(this, "usb", {
17
+ /**
18
+ * Wraps around a USB connection to implement a subset of services over a serial protocol.
19
+ *
20
+ * When it connects/disconnects it affects the delegate connection.
21
+ */
22
+ export class MicrobitRadioBridgeConnection extends TypedEventTarget {
23
+ constructor(delegate, options) {
24
+ super();
25
+ Object.defineProperty(this, "delegate", {
16
26
  enumerable: true,
17
27
  configurable: true,
18
28
  writable: true,
19
- value: usb
29
+ value: delegate
30
+ });
31
+ Object.defineProperty(this, "status", {
32
+ enumerable: true,
33
+ configurable: true,
34
+ writable: true,
35
+ value: void 0
20
36
  });
37
+ Object.defineProperty(this, "logging", {
38
+ enumerable: true,
39
+ configurable: true,
40
+ writable: true,
41
+ value: void 0
42
+ });
43
+ Object.defineProperty(this, "serialSession", {
44
+ enumerable: true,
45
+ configurable: true,
46
+ writable: true,
47
+ value: void 0
48
+ });
49
+ Object.defineProperty(this, "remoteDeviceId", {
50
+ enumerable: true,
51
+ configurable: true,
52
+ writable: true,
53
+ value: void 0
54
+ });
55
+ Object.defineProperty(this, "delegateStatusListner", {
56
+ enumerable: true,
57
+ configurable: true,
58
+ writable: true,
59
+ value: (e) => {
60
+ const currentStatus = this.status;
61
+ if (e.status !== ConnectionStatus.CONNECTED) {
62
+ this.setStatus(e.status);
63
+ this.serialSession?.dispose();
64
+ }
65
+ else {
66
+ this.status = ConnectionStatus.NOT_CONNECTED;
67
+ if (currentStatus === ConnectionStatus.NOT_CONNECTED) {
68
+ this.serialSession?.connect();
69
+ }
70
+ }
71
+ }
72
+ });
73
+ this.logging = options?.logging ?? new NullLogging();
74
+ this.status = this.statusFromDelegate();
75
+ }
76
+ getBoardVersion() {
77
+ return this.delegate.getBoardVersion();
78
+ }
79
+ serialWrite(data) {
80
+ return this.delegate.serialWrite(data);
81
+ }
82
+ async initialize() {
83
+ await this.delegate.initialize();
84
+ this.setStatus(this.statusFromDelegate());
85
+ this.delegate.addEventListener("status", this.delegateStatusListner);
86
+ }
87
+ dispose() {
88
+ this.delegate.removeEventListener("status", this.delegateStatusListner);
89
+ this.delegate.dispose();
90
+ }
91
+ clearDevice() {
92
+ this.delegate.clearDevice();
93
+ }
94
+ setRemoteDeviceId(remoteDeviceId) {
95
+ this.remoteDeviceId = remoteDeviceId;
96
+ }
97
+ async connect() {
98
+ // TODO: previously this skipped overlapping connect attempts but that seems awkward
99
+ // can we... just not do that? or wait?
100
+ if (this.remoteDeviceId === undefined) {
101
+ throw new BridgeError(`Missing remote micro:bit ID`);
102
+ }
103
+ this.logging.event({
104
+ type: "Connect",
105
+ message: "Serial connect start",
106
+ });
107
+ await this.delegate.connect();
108
+ try {
109
+ this.serialSession = new RadioBridgeSerialSession(this.logging, this.remoteDeviceId, this.delegate, this.dispatchTypedEvent.bind(this), this.setStatus.bind(this), () => {
110
+ // Remote connection lost
111
+ this.logging.event({
112
+ type: "Serial",
113
+ message: "Serial connection lost 1",
114
+ });
115
+ // This is the point we tell the consumer that we're trying to reconnect
116
+ // in the background.
117
+ // Leave serial connection running in case the remote device comes back.
118
+ }, () => {
119
+ // Remote connection... even more lost?
120
+ this.logging.event({
121
+ type: "Serial",
122
+ message: "Serial connection lost 2",
123
+ });
124
+ this.serialSession?.dispose();
125
+ });
126
+ await this.serialSession.connect();
127
+ this.logging.event({
128
+ type: "Connect",
129
+ message: "Serial connect success",
130
+ });
131
+ return this.status;
132
+ }
133
+ catch (e) {
134
+ this.logging.error("Failed to initialise serial protocol", e);
135
+ this.logging.event({
136
+ type: "Connect",
137
+ message: "Serial connect failed",
138
+ });
139
+ throw e;
140
+ }
141
+ }
142
+ async disconnect() {
143
+ await this.serialSession?.dispose();
144
+ }
145
+ setStatus(status) {
146
+ this.status = status;
147
+ this.dispatchTypedEvent("status", new ConnectionStatusEvent(status));
148
+ }
149
+ statusFromDelegate() {
150
+ return this.delegate.status == ConnectionStatus.CONNECTED
151
+ ? ConnectionStatus.NOT_CONNECTED
152
+ : this.delegate.status;
153
+ }
154
+ }
155
+ /**
156
+ * Wraps a connected delegate for a single session from attempted serial handshake to error/dispose.
157
+ */
158
+ class RadioBridgeSerialSession {
159
+ processButton(button, type, sensorData) {
160
+ if (sensorData[button] !== this.previousButtonState[button]) {
161
+ this.previousButtonState[button] = sensorData[button];
162
+ this.dispatchTypedEvent(type, new ButtonEvent(type, sensorData[button] ? ButtonState.ShortPress : ButtonState.NotPressed));
163
+ }
164
+ }
165
+ constructor(logging, remoteDeviceId, delegate, dispatchTypedEvent, onStatusChanged, onRemoteConnectionLost1, onRemoteConnectionLost2) {
21
166
  Object.defineProperty(this, "logging", {
22
167
  enumerable: true,
23
168
  configurable: true,
@@ -30,20 +175,49 @@ export class MicrobitRadioBridgeConnection {
30
175
  writable: true,
31
176
  value: remoteDeviceId
32
177
  });
33
- Object.defineProperty(this, "responseMap", {
178
+ Object.defineProperty(this, "delegate", {
34
179
  enumerable: true,
35
180
  configurable: true,
36
181
  writable: true,
37
- value: new Map()
182
+ value: delegate
38
183
  });
39
- // To avoid concurrent connect attempts
40
- Object.defineProperty(this, "isConnecting", {
184
+ Object.defineProperty(this, "dispatchTypedEvent", {
41
185
  enumerable: true,
42
186
  configurable: true,
43
187
  writable: true,
44
- value: false
188
+ value: dispatchTypedEvent
45
189
  });
46
- Object.defineProperty(this, "connectionCheckIntervalId", {
190
+ Object.defineProperty(this, "onStatusChanged", {
191
+ enumerable: true,
192
+ configurable: true,
193
+ writable: true,
194
+ value: onStatusChanged
195
+ });
196
+ Object.defineProperty(this, "onRemoteConnectionLost1", {
197
+ enumerable: true,
198
+ configurable: true,
199
+ writable: true,
200
+ value: onRemoteConnectionLost1
201
+ });
202
+ Object.defineProperty(this, "onRemoteConnectionLost2", {
203
+ enumerable: true,
204
+ configurable: true,
205
+ writable: true,
206
+ value: onRemoteConnectionLost2
207
+ });
208
+ Object.defineProperty(this, "unprocessedData", {
209
+ enumerable: true,
210
+ configurable: true,
211
+ writable: true,
212
+ value: ""
213
+ });
214
+ Object.defineProperty(this, "previousButtonState", {
215
+ enumerable: true,
216
+ configurable: true,
217
+ writable: true,
218
+ value: { buttonA: 0, buttonB: 0 }
219
+ });
220
+ Object.defineProperty(this, "onPeriodicMessageReceived", {
47
221
  enumerable: true,
48
222
  configurable: true,
49
223
  writable: true,
@@ -55,95 +229,71 @@ export class MicrobitRadioBridgeConnection {
55
229
  writable: true,
56
230
  value: void 0
57
231
  });
58
- Object.defineProperty(this, "isReconnect", {
232
+ Object.defineProperty(this, "connectionCheckIntervalId", {
59
233
  enumerable: true,
60
234
  configurable: true,
61
235
  writable: true,
62
- value: false
236
+ value: void 0
63
237
  });
64
- // Whether this is the final reconnection attempt.
65
- Object.defineProperty(this, "finalAttempt", {
238
+ Object.defineProperty(this, "serialErrorListener", {
66
239
  enumerable: true,
67
240
  configurable: true,
68
241
  writable: true,
69
- value: false
70
- });
71
- }
72
- async connect() {
73
- this.logging.event({
74
- type: this.isReconnect ? "Reconnect" : "Connect",
75
- message: "Serial connect start",
242
+ value: (e) => {
243
+ this.logging.error("Serial error", e);
244
+ void this.dispose();
245
+ }
76
246
  });
77
- if (this.isConnecting) {
78
- this.logging.log("Skipping connect attempt when one is already in progress");
79
- return;
80
- }
81
- this.isConnecting = true;
82
- let unprocessedData = "";
83
- let previousButtonState = { A: 0, B: 0 };
84
- let onPeriodicMessageRecieved;
85
- const handleError = (e) => {
86
- this.logging.error("Serial error", e);
87
- void this.disconnectInternal(false, "bridge");
88
- };
89
- const processMessage = (data) => {
90
- const messages = protocol.splitMessages(unprocessedData + data);
91
- unprocessedData = messages.remainingInput;
92
- messages.messages.forEach(async (msg) => {
93
- this.lastReceivedMessageTimestamp = Date.now();
94
- // Messages are either periodic sensor data or command/response
95
- const sensorData = protocol.processPeriodicMessage(msg);
96
- if (sensorData) {
97
- // stateOnReconnected();
98
- // if (onPeriodicMessageRecieved) {
99
- // onPeriodicMessageRecieved();
100
- // onPeriodicMessageRecieved = undefined;
101
- // }
102
- // onAccelerometerChange(
103
- // sensorData.accelerometerX,
104
- // sensorData.accelerometerY,
105
- // sensorData.accelerometerZ
106
- // );
107
- // if (sensorData.buttonA !== previousButtonState.A) {
108
- // previousButtonState.A = sensorData.buttonA;
109
- // onButtonChange(sensorData.buttonA, "A");
110
- // }
111
- // if (sensorData.buttonB !== previousButtonState.B) {
112
- // previousButtonState.B = sensorData.buttonB;
113
- // onButtonChange(sensorData.buttonB, "B");
114
- // }
115
- }
116
- else {
117
- const messageResponse = protocol.processResponseMessage(msg);
118
- if (!messageResponse) {
119
- return;
247
+ Object.defineProperty(this, "serialDataListener", {
248
+ enumerable: true,
249
+ configurable: true,
250
+ writable: true,
251
+ value: (event) => {
252
+ const { data } = event;
253
+ const messages = protocol.splitMessages(this.unprocessedData + data);
254
+ this.unprocessedData = messages.remainingInput;
255
+ messages.messages.forEach(async (msg) => {
256
+ this.lastReceivedMessageTimestamp = Date.now();
257
+ // Messages are either periodic sensor data or command/response
258
+ const sensorData = protocol.processPeriodicMessage(msg);
259
+ if (sensorData) {
260
+ this.onPeriodicMessageReceived?.();
261
+ this.dispatchTypedEvent("accelerometerdatachanged", new AccelerometerDataEvent({
262
+ x: sensorData.accelerometerX,
263
+ y: sensorData.accelerometerY,
264
+ z: sensorData.accelerometerZ,
265
+ }));
266
+ this.processButton("buttonA", "buttonachanged", sensorData);
267
+ this.processButton("buttonB", "buttonbchanged", sensorData);
120
268
  }
121
- const responseResolve = this.responseMap.get(messageResponse.messageId);
122
- if (responseResolve) {
123
- this.responseMap.delete(messageResponse.messageId);
124
- responseResolve(messageResponse);
269
+ else {
270
+ const messageResponse = protocol.processResponseMessage(msg);
271
+ if (!messageResponse) {
272
+ return;
273
+ }
274
+ const responseResolve = this.responseMap.get(messageResponse.messageId);
275
+ if (responseResolve) {
276
+ this.responseMap.delete(messageResponse.messageId);
277
+ responseResolve(messageResponse);
278
+ }
125
279
  }
126
- }
127
- });
128
- };
280
+ });
281
+ }
282
+ });
283
+ Object.defineProperty(this, "responseMap", {
284
+ enumerable: true,
285
+ configurable: true,
286
+ writable: true,
287
+ value: new Map()
288
+ });
289
+ }
290
+ async connect() {
291
+ this.delegate.addEventListener("serialdata", this.serialDataListener);
292
+ this.delegate.addEventListener("serialerror", this.serialErrorListener);
129
293
  try {
130
- await this.usb.startSerial(processMessage, handleError);
294
+ await this.delegate.softwareReset();
131
295
  await this.handshake();
132
- // stateOnConnected(DeviceRequestStates.INPUT);
133
- // Check for connection lost
134
- if (this.connectionCheckIntervalId === undefined) {
135
- this.connectionCheckIntervalId = setInterval(async () => {
136
- if (this.lastReceivedMessageTimestamp &&
137
- Date.now() - this.lastReceivedMessageTimestamp > 1_000) {
138
- // stateOnReconnectionAttempt();
139
- }
140
- if (this.lastReceivedMessageTimestamp &&
141
- Date.now() - this.lastReceivedMessageTimestamp >
142
- connectTimeoutDuration) {
143
- await this.handleReconnect();
144
- }
145
- }, 1000);
146
- }
296
+ this.onStatusChanged(ConnectionStatus.CONNECTED);
147
297
  this.logging.log(`Serial: using remote device id ${this.remoteDeviceId}`);
148
298
  const remoteMbIdCommand = protocol.generateCmdRemoteMbId(this.remoteDeviceId);
149
299
  const remoteMbIdResponse = await this.sendCmdWaitResponse(remoteMbIdCommand);
@@ -151,67 +301,31 @@ export class MicrobitRadioBridgeConnection {
151
301
  remoteMbIdResponse.value !== this.remoteDeviceId) {
152
302
  throw new BridgeError(`Failed to set remote micro:bit ID. Expected ${this.remoteDeviceId}, got ${remoteMbIdResponse.value}`);
153
303
  }
154
- // For now we only support input state.
155
- // TODO: when do we do this?
156
- if (false) {
157
- // Request the micro:bit to start sending the periodic messages
158
- const startCmd = protocol.generateCmdStart({
159
- accelerometer: true,
160
- buttons: true,
161
- });
162
- const periodicMessagePromise = new Promise((resolve, reject) => {
163
- onPeriodicMessageRecieved = resolve;
164
- setTimeout(() => {
165
- onPeriodicMessageRecieved = undefined;
166
- reject(new Error("Failed to receive data from remote micro:bit"));
167
- }, 500);
168
- });
169
- const startCmdResponse = await this.sendCmdWaitResponse(startCmd);
170
- if (startCmdResponse.type === protocol.ResponseTypes.Error) {
171
- throw new RemoteError(`Failed to start streaming sensors data. Error response received: ${startCmdResponse.message}`);
172
- }
173
- if (this.isReconnect) {
174
- await periodicMessagePromise;
175
- }
176
- else {
177
- periodicMessagePromise.catch(async (e) => {
178
- this.logging.error("Failed to initialise serial protocol", e);
179
- await this.disconnectInternal(false, "remote");
180
- this.isConnecting = false;
181
- });
182
- }
183
- }
184
- // stateOnAssigned(DeviceRequestStates.INPUT, this.usb.getModelNumber());
185
- // stateOnReady(DeviceRequestStates.INPUT);
186
- this.logging.event({
187
- type: this.isReconnect ? "Reconnect" : "Connect",
188
- message: "Serial connect success",
304
+ // Request the micro:bit to start sending the periodic messages
305
+ const startCmd = protocol.generateCmdStart({
306
+ accelerometer: true,
307
+ buttons: true,
189
308
  });
190
- }
191
- catch (e) {
192
- this.logging.error("Failed to initialise serial protocol", e);
193
- this.logging.event({
194
- type: this.isReconnect ? "Reconnect" : "Connect",
195
- message: "Serial connect failed",
309
+ const periodicMessagePromise = new Promise((resolve, reject) => {
310
+ this.onPeriodicMessageReceived = resolve;
311
+ setTimeout(() => {
312
+ this.onPeriodicMessageReceived = undefined;
313
+ reject(new Error("Failed to receive data from remote micro:bit"));
314
+ }, 500);
196
315
  });
197
- const reconnectHelp = e instanceof BridgeError ? "bridge" : "remote";
198
- await this.disconnectInternal(false, reconnectHelp);
199
- throw e;
316
+ const startCmdResponse = await this.sendCmdWaitResponse(startCmd);
317
+ if (startCmdResponse.type === protocol.ResponseTypes.Error) {
318
+ throw new RemoteError(`Failed to start streaming sensors data. Error response received: ${startCmdResponse.message}`);
319
+ }
320
+ // TODO: in the first-time connection case we used to move the error/disconnect to the background here, why? timing?
321
+ await periodicMessagePromise;
322
+ this.startConnectionCheck();
200
323
  }
201
- finally {
202
- this.finalAttempt = false;
203
- this.isConnecting = false;
324
+ catch (e) {
325
+ this.dispose();
204
326
  }
205
327
  }
206
- async disconnect() {
207
- return this.disconnectInternal(true, "bridge");
208
- }
209
- stopConnectionCheck() {
210
- clearInterval(this.connectionCheckIntervalId);
211
- this.connectionCheckIntervalId = undefined;
212
- this.lastReceivedMessageTimestamp = undefined;
213
- }
214
- async disconnectInternal(userDisconnect) {
328
+ async dispose() {
215
329
  this.stopConnectionCheck();
216
330
  try {
217
331
  await this.sendCmdWaitResponse(protocol.generateCmdStop());
@@ -220,42 +334,9 @@ export class MicrobitRadioBridgeConnection {
220
334
  // If this fails the remote micro:bit has already gone away.
221
335
  }
222
336
  this.responseMap.clear();
223
- await this.usb.stopSerial();
224
- // stateOnDisconnected(
225
- // DeviceRequestStates.INPUT,
226
- // userDisconnect || this.finalAttempt
227
- // ? false
228
- // : this.isReconnect
229
- // ? "autoReconnect"
230
- // : "connect",
231
- // reconnectHelp
232
- // );
233
- }
234
- async handleReconnect() {
235
- if (this.isConnecting) {
236
- this.logging.log("Serial disconnect ignored... reconnect already in progress");
237
- return;
238
- }
239
- try {
240
- this.stopConnectionCheck();
241
- this.logging.log("Serial disconnected... automatically trying to reconnect");
242
- this.responseMap.clear();
243
- await this.usb.stopSerial();
244
- await this.usb.softwareReset();
245
- await this.reconnect();
246
- }
247
- catch (e) {
248
- this.logging.error("Serial connect triggered by disconnect listener failed", e);
249
- }
250
- finally {
251
- this.isConnecting = false;
252
- }
253
- }
254
- async reconnect(finalAttempt = false) {
255
- this.finalAttempt = finalAttempt;
256
- this.logging.log("Serial reconnect");
257
- this.isReconnect = true;
258
- await this.connect();
337
+ this.delegate.removeEventListener("serialdata", this.serialDataListener);
338
+ this.delegate.removeEventListener("serialerror", this.serialErrorListener);
339
+ this.onStatusChanged(ConnectionStatus.NOT_CONNECTED);
259
340
  }
260
341
  async sendCmdWaitResponse(cmd) {
261
342
  const responsePromise = new Promise((resolve, reject) => {
@@ -265,9 +346,30 @@ export class MicrobitRadioBridgeConnection {
265
346
  reject(new Error(`Timeout waiting for response ${cmd.messageId}`));
266
347
  }, 1_000);
267
348
  });
268
- await this.usb.serialWrite(cmd.message);
349
+ await this.delegate.serialWrite(cmd.message);
269
350
  return responsePromise;
270
351
  }
352
+ startConnectionCheck() {
353
+ // Check for connection lost
354
+ if (this.connectionCheckIntervalId === undefined) {
355
+ this.connectionCheckIntervalId = setInterval(async () => {
356
+ if (this.lastReceivedMessageTimestamp &&
357
+ Date.now() - this.lastReceivedMessageTimestamp > 1_000) {
358
+ this.onRemoteConnectionLost1();
359
+ }
360
+ if (this.lastReceivedMessageTimestamp &&
361
+ Date.now() - this.lastReceivedMessageTimestamp >
362
+ connectTimeoutDuration) {
363
+ this.onRemoteConnectionLost2();
364
+ }
365
+ }, 1000);
366
+ }
367
+ }
368
+ stopConnectionCheck() {
369
+ clearInterval(this.connectionCheckIntervalId);
370
+ this.connectionCheckIntervalId = undefined;
371
+ this.lastReceivedMessageTimestamp = undefined;
372
+ }
271
373
  async handshake() {
272
374
  // There is an issue where we cannot read data out from the micro:bit serial
273
375
  // buffer until the buffer has been filled.
@@ -305,14 +407,4 @@ export class MicrobitRadioBridgeConnection {
305
407
  }
306
408
  }
307
409
  }
308
- export const startSerialConnection = async (logging, usb, remoteDeviceId) => {
309
- try {
310
- const serial = new MicrobitRadioBridgeConnection(usb, logging, remoteDeviceId);
311
- await serial.connect();
312
- return serial;
313
- }
314
- catch (e) {
315
- return undefined;
316
- }
317
- };
318
410
  //# sourceMappingURL=usb-radio-bridge.js.map