@microbit/microbit-connection 0.0.0-alpha.1
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.
- package/README.md +36 -0
- package/build/accelerometer-service.d.ts +17 -0
- package/build/accelerometer-service.js +84 -0
- package/build/accelerometer-service.js.map +1 -0
- package/build/accelerometer.d.ts +9 -0
- package/build/accelerometer.js +12 -0
- package/build/accelerometer.js.map +1 -0
- package/build/async-util.d.ts +13 -0
- package/build/async-util.js +22 -0
- package/build/async-util.js.map +1 -0
- package/build/bluetooth-device-wrapper.d.ts +39 -0
- package/build/bluetooth-device-wrapper.js +316 -0
- package/build/bluetooth-device-wrapper.js.map +1 -0
- package/build/bluetooth-profile.d.ts +139 -0
- package/build/bluetooth-profile.js +83 -0
- package/build/bluetooth-profile.js.map +1 -0
- package/build/bluetooth.d.ts +50 -0
- package/build/bluetooth.js +247 -0
- package/build/bluetooth.js.map +1 -0
- package/build/board-id.d.ts +35 -0
- package/build/board-id.js +69 -0
- package/build/board-id.js.map +1 -0
- package/build/board-serial-info.d.ts +14 -0
- package/build/board-serial-info.js +47 -0
- package/build/board-serial-info.js.map +1 -0
- package/build/constants.d.ts +47 -0
- package/build/constants.js +69 -0
- package/build/constants.js.map +1 -0
- package/build/device.d.ts +202 -0
- package/build/device.js +153 -0
- package/build/device.js.map +1 -0
- package/build/events.d.ts +101 -0
- package/build/events.js +19 -0
- package/build/events.js.map +1 -0
- package/build/hex-flash-data-source.d.ts +9 -0
- package/build/hex-flash-data-source.js +50 -0
- package/build/hex-flash-data-source.js.map +1 -0
- package/build/index.d.ts +7 -0
- package/build/index.js +7 -0
- package/build/index.js.map +1 -0
- package/build/logging.d.ts +21 -0
- package/build/logging.js +10 -0
- package/build/logging.js.map +1 -0
- package/build/service-events.d.ts +9 -0
- package/build/service-events.js +11 -0
- package/build/service-events.js.map +1 -0
- package/build/setupTests.d.ts +6 -0
- package/build/setupTests.js.map +1 -0
- package/build/usb-device-wrapper.d.ts +46 -0
- package/build/usb-device-wrapper.js +347 -0
- package/build/usb-device-wrapper.js.map +1 -0
- package/build/usb-partial-flashing-utils.d.ts +17 -0
- package/build/usb-partial-flashing-utils.js +122 -0
- package/build/usb-partial-flashing-utils.js.map +1 -0
- package/build/usb-partial-flashing.d.ts +63 -0
- package/build/usb-partial-flashing.js +270 -0
- package/build/usb-partial-flashing.js.map +1 -0
- package/build/usb-radio-bridge.d.ts +28 -0
- package/build/usb-radio-bridge.js +318 -0
- package/build/usb-radio-bridge.js.map +1 -0
- package/build/usb-serial-protocol.d.ts +66 -0
- package/build/usb-serial-protocol.js +171 -0
- package/build/usb-serial-protocol.js.map +1 -0
- package/build/usb.d.ts +61 -0
- package/build/usb.js +451 -0
- package/build/usb.js.map +1 -0
- package/package.json +32 -0
- package/vite.config.ts +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# JavaScript library for micro:bit connections in browsers via USB and Bluetooth
|
|
2
|
+
|
|
3
|
+
This project is a work in progress. We are extracting WebUSB and Web Bluetooth code from the [micro:bit Python Editor](https://github.com/microbit-foundation/python-editor-v3/) and other projects.
|
|
4
|
+
|
|
5
|
+
It is intended to be used by other Micro:bit Educational Foundation projects that need to connect to a BBC micro:bit.
|
|
6
|
+
|
|
7
|
+
The API is not stable and it's not yet recommended that third parties use this project.
|
|
8
|
+
|
|
9
|
+
## License
|
|
10
|
+
|
|
11
|
+
This software is under the MIT open source license.
|
|
12
|
+
|
|
13
|
+
[SPDX-License-Identifier: MIT](LICENSE)
|
|
14
|
+
|
|
15
|
+
We use dependencies via the NPM registry as specified by the package.json file under common Open Source licenses.
|
|
16
|
+
|
|
17
|
+
Full details of each package can be found by running `license-checker`:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
$ npx license-checker --direct --summary --production
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Omit the flags as desired to obtain more detail.
|
|
24
|
+
|
|
25
|
+
## Code of Conduct
|
|
26
|
+
|
|
27
|
+
Trust, partnership, simplicity and passion are our core values we live and
|
|
28
|
+
breathe in our daily work life and within our projects. Our open-source
|
|
29
|
+
projects are no exception. We have an active community which spans the globe
|
|
30
|
+
and we welcome and encourage participation and contributions to our projects
|
|
31
|
+
by everyone. We work to foster a positive, open, inclusive and supportive
|
|
32
|
+
environment and trust that our community respects the micro:bit code of
|
|
33
|
+
conduct. Please see our [code of conduct](https://microbit.org/safeguarding/)
|
|
34
|
+
which outlines our expectations for all those that participate in our
|
|
35
|
+
community and details on how to report any concerns and what would happen
|
|
36
|
+
should breaches occur.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { AccelerometerData } from "./accelerometer";
|
|
2
|
+
import { TypedServiceEventDispatcher } from "./service-events";
|
|
3
|
+
export declare class AccelerometerService {
|
|
4
|
+
private accelerometerDataCharacteristic;
|
|
5
|
+
private accelerometerPeriodCharacteristic;
|
|
6
|
+
private dispatchTypedEvent;
|
|
7
|
+
private isNotifying;
|
|
8
|
+
constructor(accelerometerDataCharacteristic: BluetoothRemoteGATTCharacteristic, accelerometerPeriodCharacteristic: BluetoothRemoteGATTCharacteristic, dispatchTypedEvent: TypedServiceEventDispatcher, isNotifying: boolean);
|
|
9
|
+
static createService(gattServer: BluetoothRemoteGATTServer, dispatcher: TypedServiceEventDispatcher, isNotifying: boolean): Promise<AccelerometerService>;
|
|
10
|
+
private dataViewToData;
|
|
11
|
+
getData(): Promise<AccelerometerData>;
|
|
12
|
+
getPeriod(): Promise<number>;
|
|
13
|
+
setPeriod(value: number): Promise<void>;
|
|
14
|
+
private addDataEventListener;
|
|
15
|
+
startNotifications(): void;
|
|
16
|
+
stopNotifications(): void;
|
|
17
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { AccelerometerDataEvent } from "./accelerometer";
|
|
2
|
+
import { profile } from "./bluetooth-profile";
|
|
3
|
+
export class AccelerometerService {
|
|
4
|
+
constructor(accelerometerDataCharacteristic, accelerometerPeriodCharacteristic, dispatchTypedEvent, isNotifying) {
|
|
5
|
+
Object.defineProperty(this, "accelerometerDataCharacteristic", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
writable: true,
|
|
9
|
+
value: accelerometerDataCharacteristic
|
|
10
|
+
});
|
|
11
|
+
Object.defineProperty(this, "accelerometerPeriodCharacteristic", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: true,
|
|
15
|
+
value: accelerometerPeriodCharacteristic
|
|
16
|
+
});
|
|
17
|
+
Object.defineProperty(this, "dispatchTypedEvent", {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true,
|
|
21
|
+
value: dispatchTypedEvent
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(this, "isNotifying", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
writable: true,
|
|
27
|
+
value: isNotifying
|
|
28
|
+
});
|
|
29
|
+
this.addDataEventListener();
|
|
30
|
+
if (this.isNotifying) {
|
|
31
|
+
this.startNotifications();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
static async createService(gattServer, dispatcher, isNotifying) {
|
|
35
|
+
const accelerometerService = await gattServer.getPrimaryService(profile.accelerometer.id);
|
|
36
|
+
const accelerometerDataCharacteristic = await accelerometerService.getCharacteristic(profile.accelerometer.characteristics.data.id);
|
|
37
|
+
const accelerometerPeriodCharacteristic = await accelerometerService.getCharacteristic(profile.accelerometer.characteristics.period.id);
|
|
38
|
+
return new AccelerometerService(accelerometerDataCharacteristic, accelerometerPeriodCharacteristic, dispatcher, isNotifying);
|
|
39
|
+
}
|
|
40
|
+
dataViewToData(dataView) {
|
|
41
|
+
return {
|
|
42
|
+
x: dataView.getInt16(0, true),
|
|
43
|
+
y: dataView.getInt16(2, true),
|
|
44
|
+
z: dataView.getInt16(4, true),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
async getData() {
|
|
48
|
+
const dataView = await this.accelerometerDataCharacteristic.readValue();
|
|
49
|
+
return this.dataViewToData(dataView);
|
|
50
|
+
}
|
|
51
|
+
async getPeriod() {
|
|
52
|
+
const dataView = await this.accelerometerPeriodCharacteristic.readValue();
|
|
53
|
+
return dataView.getUint16(0, true);
|
|
54
|
+
}
|
|
55
|
+
async setPeriod(value) {
|
|
56
|
+
if (value === 0) {
|
|
57
|
+
// Writing 0 causes the device to crash.
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
// Allowed values: 2, 5, 10, 20, 40, 100, 1000
|
|
61
|
+
// Values passed are rounded up to the allowed values on device.
|
|
62
|
+
// Documentation for allowed values looks wrong.
|
|
63
|
+
// https://lancaster-university.github.io/microbit-docs/resources/bluetooth/bluetooth_profile.html
|
|
64
|
+
const dataView = new DataView(new ArrayBuffer(2));
|
|
65
|
+
dataView.setUint16(0, value, true);
|
|
66
|
+
await this.accelerometerPeriodCharacteristic.writeValueWithoutResponse(dataView);
|
|
67
|
+
}
|
|
68
|
+
addDataEventListener() {
|
|
69
|
+
this.accelerometerDataCharacteristic.addEventListener("characteristicvaluechanged", (event) => {
|
|
70
|
+
const target = event.target;
|
|
71
|
+
const data = this.dataViewToData(target.value);
|
|
72
|
+
this.dispatchTypedEvent("accelerometerdatachanged", new AccelerometerDataEvent(data));
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
startNotifications() {
|
|
76
|
+
this.accelerometerDataCharacteristic.startNotifications();
|
|
77
|
+
this.isNotifying = true;
|
|
78
|
+
}
|
|
79
|
+
stopNotifications() {
|
|
80
|
+
this.isNotifying = false;
|
|
81
|
+
this.accelerometerDataCharacteristic.stopNotifications();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=accelerometer-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accelerometer-service.js","sourceRoot":"","sources":["../lib/accelerometer-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAM9C,MAAM,OAAO,oBAAoB;IAC/B,YACU,+BAAkE,EAClE,iCAAoE,EACpE,kBAA+C,EAC/C,WAAoB;QAH5B;;;;mBAAQ,+BAA+B;WAAmC;QAC1E;;;;mBAAQ,iCAAiC;WAAmC;QAC5E;;;;mBAAQ,kBAAkB;WAA6B;QACvD;;;;mBAAQ,WAAW;WAAS;QAE5B,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC5B,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,aAAa,CACxB,UAAqC,EACrC,UAAuC,EACvC,WAAoB;QAEpB,MAAM,oBAAoB,GAAG,MAAM,UAAU,CAAC,iBAAiB,CAC7D,OAAO,CAAC,aAAa,CAAC,EAAE,CACzB,CAAC;QACF,MAAM,+BAA+B,GACnC,MAAM,oBAAoB,CAAC,iBAAiB,CAC1C,OAAO,CAAC,aAAa,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAC9C,CAAC;QACJ,MAAM,iCAAiC,GACrC,MAAM,oBAAoB,CAAC,iBAAiB,CAC1C,OAAO,CAAC,aAAa,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,CAChD,CAAC;QACJ,OAAO,IAAI,oBAAoB,CAC7B,+BAA+B,EAC/B,iCAAiC,EACjC,UAAU,EACV,WAAW,CACZ,CAAC;IACJ,CAAC;IAEO,cAAc,CAAC,QAAkB;QACvC,OAAO;YACL,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC;YAC7B,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC;YAC7B,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC;SAC9B,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,+BAA+B,CAAC,SAAS,EAAE,CAAC;QACxE,OAAO,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,SAAS;QACb,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,iCAAiC,CAAC,SAAS,EAAE,CAAC;QAC1E,OAAO,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,KAAa;QAC3B,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YAChB,wCAAwC;YACxC,OAAO;QACT,CAAC;QACD,8CAA8C;QAC9C,gEAAgE;QAChE,gDAAgD;QAChD,kGAAkG;QAClG,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;QAClD,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QACnC,MAAM,IAAI,CAAC,iCAAiC,CAAC,yBAAyB,CACpE,QAAQ,CACT,CAAC;IACJ,CAAC;IAEO,oBAAoB;QAC1B,IAAI,CAAC,+BAA+B,CAAC,gBAAgB,CACnD,4BAA4B,EAC5B,CAAC,KAAY,EAAE,EAAE;YACf,MAAM,MAAM,GAAG,KAAK,CAAC,MAAkC,CAAC;YACxD,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC/C,IAAI,CAAC,kBAAkB,CACrB,0BAA0B,EAC1B,IAAI,sBAAsB,CAAC,IAAI,CAAC,CACjC,CAAC;QACJ,CAAC,CACF,CAAC;IACJ,CAAC;IAED,kBAAkB;QAChB,IAAI,CAAC,+BAA+B,CAAC,kBAAkB,EAAE,CAAC;QAC1D,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED,iBAAiB;QACf,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,IAAI,CAAC,+BAA+B,CAAC,iBAAiB,EAAE,CAAC;IAC3D,CAAC;CACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export class AccelerometerDataEvent extends Event {
|
|
2
|
+
constructor(data) {
|
|
3
|
+
super("accelerometerdatachanged");
|
|
4
|
+
Object.defineProperty(this, "data", {
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true,
|
|
8
|
+
value: data
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=accelerometer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accelerometer.js","sourceRoot":"","sources":["../lib/accelerometer.ts"],"names":[],"mappings":"AAMA,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAC/C,YAA4B,IAAuB;QACjD,KAAK,CAAC,0BAA0B,CAAC,CAAC;QADxB;;;;mBAAgB,IAAI;WAAmB;IAEnD,CAAC;CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* (c) 2021, Micro:bit Educational Foundation and contributors
|
|
3
|
+
*
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
export declare class TimeoutError extends Error {
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Utility to time out an action after a delay.
|
|
10
|
+
*
|
|
11
|
+
* The action cannot be cancelled; it may still proceed after the timeout.
|
|
12
|
+
*/
|
|
13
|
+
export declare function withTimeout<T>(actionPromise: Promise<T>, timeout: number): Promise<T>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* (c) 2021, Micro:bit Educational Foundation and contributors
|
|
3
|
+
*
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
export class TimeoutError extends Error {
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Utility to time out an action after a delay.
|
|
10
|
+
*
|
|
11
|
+
* The action cannot be cancelled; it may still proceed after the timeout.
|
|
12
|
+
*/
|
|
13
|
+
export async function withTimeout(actionPromise, timeout) {
|
|
14
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
15
|
+
setTimeout(() => {
|
|
16
|
+
reject(new TimeoutError());
|
|
17
|
+
}, timeout);
|
|
18
|
+
});
|
|
19
|
+
// timeoutPromise never resolves so result must be from action
|
|
20
|
+
return Promise.race([actionPromise, timeoutPromise]);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=async-util.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-util.js","sourceRoot":"","sources":["../lib/async-util.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;CAAG;AAE1C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,aAAyB,EACzB,OAAe;IAEf,MAAM,cAAc,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;QAC/C,UAAU,CAAC,GAAG,EAAE;YACd,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC,CAAC;QAC7B,CAAC,EAAE,OAAO,CAAC,CAAC;IACd,CAAC,CAAC,CAAC;IACH,8DAA8D;IAC9D,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,cAAc,CAAC,CAAe,CAAC;AACrE,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* (c) 2023, Center for Computational Thinking and Design at Aarhus University and contributors
|
|
3
|
+
*
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { AccelerometerService } from "./accelerometer-service";
|
|
7
|
+
import { BoardVersion } from "./device";
|
|
8
|
+
import { Logging } from "./logging";
|
|
9
|
+
import { TypedServiceEventDispatcher } from "./service-events";
|
|
10
|
+
export declare class BluetoothDeviceWrapper {
|
|
11
|
+
readonly device: BluetoothDevice;
|
|
12
|
+
private logging;
|
|
13
|
+
private dispatchTypedEvent;
|
|
14
|
+
private duringExplicitConnectDisconnect;
|
|
15
|
+
private gattConnectPromise;
|
|
16
|
+
private disconnectPromise;
|
|
17
|
+
private connecting;
|
|
18
|
+
private isReconnect;
|
|
19
|
+
private reconnectReadyPromise;
|
|
20
|
+
private accelerometerService;
|
|
21
|
+
boardVersion: BoardVersion | undefined;
|
|
22
|
+
serviceListeners: {
|
|
23
|
+
accelerometerdatachanged: {
|
|
24
|
+
notifying: boolean;
|
|
25
|
+
service: () => Promise<AccelerometerService>;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
constructor(device: BluetoothDevice, logging: Logging, dispatchTypedEvent: TypedServiceEventDispatcher);
|
|
29
|
+
connect(): Promise<void>;
|
|
30
|
+
disconnect(): Promise<void>;
|
|
31
|
+
private disconnectInternal;
|
|
32
|
+
reconnect(): Promise<void>;
|
|
33
|
+
handleDisconnectEvent: () => Promise<void>;
|
|
34
|
+
private assertGattServer;
|
|
35
|
+
private getBoardVersion;
|
|
36
|
+
getAccelerometerService(): Promise<AccelerometerService>;
|
|
37
|
+
private disposeServices;
|
|
38
|
+
}
|
|
39
|
+
export declare const createBluetoothDeviceWrapper: (device: BluetoothDevice, logging: Logging, dispatchTypedEvent: TypedServiceEventDispatcher) => Promise<BluetoothDeviceWrapper | undefined>;
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* (c) 2023, Center for Computational Thinking and Design at Aarhus University and contributors
|
|
3
|
+
*
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { AccelerometerService } from "./accelerometer-service";
|
|
7
|
+
import { profile } from "./bluetooth-profile";
|
|
8
|
+
import { NullLogging } from "./logging";
|
|
9
|
+
const deviceIdToWrapper = new Map();
|
|
10
|
+
const connectTimeoutDuration = 10000;
|
|
11
|
+
function findPlatform() {
|
|
12
|
+
const navigator = window.navigator;
|
|
13
|
+
const platform = navigator.userAgentData?.platform;
|
|
14
|
+
if (platform) {
|
|
15
|
+
return platform;
|
|
16
|
+
}
|
|
17
|
+
const isAndroid = /android/.test(navigator.userAgent.toLowerCase());
|
|
18
|
+
return isAndroid ? "android" : navigator.platform ?? "unknown";
|
|
19
|
+
}
|
|
20
|
+
const platform = findPlatform();
|
|
21
|
+
const isWindowsOS = platform && /^Win/.test(platform);
|
|
22
|
+
export class BluetoothDeviceWrapper {
|
|
23
|
+
constructor(device, logging = new NullLogging(), dispatchTypedEvent) {
|
|
24
|
+
Object.defineProperty(this, "device", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
configurable: true,
|
|
27
|
+
writable: true,
|
|
28
|
+
value: device
|
|
29
|
+
});
|
|
30
|
+
Object.defineProperty(this, "logging", {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
configurable: true,
|
|
33
|
+
writable: true,
|
|
34
|
+
value: logging
|
|
35
|
+
});
|
|
36
|
+
Object.defineProperty(this, "dispatchTypedEvent", {
|
|
37
|
+
enumerable: true,
|
|
38
|
+
configurable: true,
|
|
39
|
+
writable: true,
|
|
40
|
+
value: dispatchTypedEvent
|
|
41
|
+
});
|
|
42
|
+
// Used to avoid automatic reconnection during user triggered connect/disconnect
|
|
43
|
+
// or reconnection itself.
|
|
44
|
+
Object.defineProperty(this, "duringExplicitConnectDisconnect", {
|
|
45
|
+
enumerable: true,
|
|
46
|
+
configurable: true,
|
|
47
|
+
writable: true,
|
|
48
|
+
value: 0
|
|
49
|
+
});
|
|
50
|
+
// On ChromeOS and Mac there's no timeout and no clear way to abort
|
|
51
|
+
// device.gatt.connect(), so we accept that sometimes we'll still
|
|
52
|
+
// be trying to connect when we'd rather not be. If it succeeds when
|
|
53
|
+
// we no longer intend to be connected then we disconnect at that
|
|
54
|
+
// point. If we try to connect when a previous connection attempt is
|
|
55
|
+
// still around then we wait for it for our timeout period.
|
|
56
|
+
//
|
|
57
|
+
// On Windows it times out after 7s.
|
|
58
|
+
// https://bugs.chromium.org/p/chromium/issues/detail?id=684073
|
|
59
|
+
Object.defineProperty(this, "gattConnectPromise", {
|
|
60
|
+
enumerable: true,
|
|
61
|
+
configurable: true,
|
|
62
|
+
writable: true,
|
|
63
|
+
value: void 0
|
|
64
|
+
});
|
|
65
|
+
Object.defineProperty(this, "disconnectPromise", {
|
|
66
|
+
enumerable: true,
|
|
67
|
+
configurable: true,
|
|
68
|
+
writable: true,
|
|
69
|
+
value: void 0
|
|
70
|
+
});
|
|
71
|
+
Object.defineProperty(this, "connecting", {
|
|
72
|
+
enumerable: true,
|
|
73
|
+
configurable: true,
|
|
74
|
+
writable: true,
|
|
75
|
+
value: false
|
|
76
|
+
});
|
|
77
|
+
Object.defineProperty(this, "isReconnect", {
|
|
78
|
+
enumerable: true,
|
|
79
|
+
configurable: true,
|
|
80
|
+
writable: true,
|
|
81
|
+
value: false
|
|
82
|
+
});
|
|
83
|
+
Object.defineProperty(this, "reconnectReadyPromise", {
|
|
84
|
+
enumerable: true,
|
|
85
|
+
configurable: true,
|
|
86
|
+
writable: true,
|
|
87
|
+
value: void 0
|
|
88
|
+
});
|
|
89
|
+
Object.defineProperty(this, "accelerometerService", {
|
|
90
|
+
enumerable: true,
|
|
91
|
+
configurable: true,
|
|
92
|
+
writable: true,
|
|
93
|
+
value: void 0
|
|
94
|
+
});
|
|
95
|
+
Object.defineProperty(this, "boardVersion", {
|
|
96
|
+
enumerable: true,
|
|
97
|
+
configurable: true,
|
|
98
|
+
writable: true,
|
|
99
|
+
value: void 0
|
|
100
|
+
});
|
|
101
|
+
Object.defineProperty(this, "serviceListeners", {
|
|
102
|
+
enumerable: true,
|
|
103
|
+
configurable: true,
|
|
104
|
+
writable: true,
|
|
105
|
+
value: {
|
|
106
|
+
accelerometerdatachanged: {
|
|
107
|
+
notifying: false,
|
|
108
|
+
service: this.getAccelerometerService,
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
Object.defineProperty(this, "handleDisconnectEvent", {
|
|
113
|
+
enumerable: true,
|
|
114
|
+
configurable: true,
|
|
115
|
+
writable: true,
|
|
116
|
+
value: async () => {
|
|
117
|
+
// this.outputWriteQueue = { busy: false, queue: [] };
|
|
118
|
+
try {
|
|
119
|
+
if (!this.duringExplicitConnectDisconnect) {
|
|
120
|
+
this.logging.log("Bluetooth GATT disconnected... automatically trying reconnect");
|
|
121
|
+
// stateOnReconnectionAttempt();
|
|
122
|
+
this.disposeServices();
|
|
123
|
+
await this.reconnect();
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
this.logging.log("Bluetooth GATT disconnect ignored during explicit disconnect");
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (e) {
|
|
130
|
+
this.logging.error("Bluetooth connect triggered by disconnect listener failed", e);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
device.addEventListener("gattserverdisconnected", this.handleDisconnectEvent);
|
|
135
|
+
}
|
|
136
|
+
async connect() {
|
|
137
|
+
this.logging.event({
|
|
138
|
+
type: this.isReconnect ? "Reconnect" : "Connect",
|
|
139
|
+
message: "Bluetooth connect start",
|
|
140
|
+
});
|
|
141
|
+
if (this.duringExplicitConnectDisconnect) {
|
|
142
|
+
this.logging.log("Skipping connect attempt when one is already in progress");
|
|
143
|
+
// Wait for the gattConnectPromise while showing a "connecting" dialog.
|
|
144
|
+
// If the user clicks disconnect while the automatic reconnect is in progress,
|
|
145
|
+
// then clicks reconnect, we need to wait rather than return immediately.
|
|
146
|
+
await this.gattConnectPromise;
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
this.duringExplicitConnectDisconnect++;
|
|
150
|
+
if (this.device.gatt === undefined) {
|
|
151
|
+
throw new Error("BluetoothRemoteGATTServer for micro:bit device is undefined");
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
// A previous connect might have completed in the background as a device was replugged etc.
|
|
155
|
+
await this.disconnectPromise;
|
|
156
|
+
this.gattConnectPromise =
|
|
157
|
+
this.gattConnectPromise ??
|
|
158
|
+
this.device.gatt
|
|
159
|
+
.connect()
|
|
160
|
+
.then(async () => {
|
|
161
|
+
// We always do this even if we might immediately disconnect as disconnecting
|
|
162
|
+
// without using services causes getPrimaryService calls to hang on subsequent
|
|
163
|
+
// reconnect - probably a device-side issue.
|
|
164
|
+
this.boardVersion = await this.getBoardVersion();
|
|
165
|
+
// This connection could be arbitrarily later when our manual timeout may have passed.
|
|
166
|
+
// Do we still want to be connected?
|
|
167
|
+
if (!this.connecting) {
|
|
168
|
+
this.logging.log("Bluetooth GATT server connect after timeout, triggering disconnect");
|
|
169
|
+
this.disconnectPromise = (async () => {
|
|
170
|
+
await this.disconnectInternal(false);
|
|
171
|
+
this.disconnectPromise = undefined;
|
|
172
|
+
})();
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
this.logging.log("Bluetooth GATT server connected when connecting");
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
.catch((e) => {
|
|
179
|
+
if (this.connecting) {
|
|
180
|
+
// Error will be logged by main connect error handling.
|
|
181
|
+
throw e;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
this.logging.error("Bluetooth GATT server connect error after our timeout", e);
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
.finally(() => {
|
|
189
|
+
this.logging.log("Bluetooth GATT server promise field cleared");
|
|
190
|
+
this.gattConnectPromise = undefined;
|
|
191
|
+
});
|
|
192
|
+
this.connecting = true;
|
|
193
|
+
try {
|
|
194
|
+
const gattConnectResult = await Promise.race([
|
|
195
|
+
this.gattConnectPromise,
|
|
196
|
+
new Promise((resolve) => setTimeout(() => resolve("timeout"), connectTimeoutDuration)),
|
|
197
|
+
]);
|
|
198
|
+
if (gattConnectResult === "timeout") {
|
|
199
|
+
this.logging.log("Bluetooth GATT server connect timeout");
|
|
200
|
+
throw new Error("Bluetooth GATT server connect timeout");
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
this.connecting = false;
|
|
205
|
+
}
|
|
206
|
+
// Restart notifications for services and characteristics
|
|
207
|
+
// the user has listened to.
|
|
208
|
+
for (const serviceListener of Object.values(this.serviceListeners)) {
|
|
209
|
+
if (serviceListener.notifying) {
|
|
210
|
+
serviceListener.service.call(this);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
this.logging.event({
|
|
214
|
+
type: this.isReconnect ? "Reconnect" : "Connect",
|
|
215
|
+
message: "Bluetooth connect success",
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
catch (e) {
|
|
219
|
+
this.logging.error("Bluetooth connect error", e);
|
|
220
|
+
this.logging.event({
|
|
221
|
+
type: this.isReconnect ? "Reconnect" : "Connect",
|
|
222
|
+
message: "Bluetooth connect failed",
|
|
223
|
+
});
|
|
224
|
+
await this.disconnectInternal(false);
|
|
225
|
+
throw new Error("Failed to establish a connection!");
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
228
|
+
this.duringExplicitConnectDisconnect--;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
async disconnect() {
|
|
232
|
+
return this.disconnectInternal(true);
|
|
233
|
+
}
|
|
234
|
+
async disconnectInternal(userTriggered) {
|
|
235
|
+
this.logging.log(`Bluetooth disconnect ${userTriggered ? "(user triggered)" : "(programmatic)"}`);
|
|
236
|
+
this.duringExplicitConnectDisconnect++;
|
|
237
|
+
try {
|
|
238
|
+
if (this.device.gatt?.connected) {
|
|
239
|
+
this.device.gatt?.disconnect();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
catch (e) {
|
|
243
|
+
this.logging.error("Bluetooth GATT disconnect error (ignored)", e);
|
|
244
|
+
// We might have already lost the connection.
|
|
245
|
+
}
|
|
246
|
+
finally {
|
|
247
|
+
this.disposeServices();
|
|
248
|
+
this.duringExplicitConnectDisconnect--;
|
|
249
|
+
}
|
|
250
|
+
this.reconnectReadyPromise = new Promise((resolve) => setTimeout(resolve, 3_500));
|
|
251
|
+
}
|
|
252
|
+
async reconnect() {
|
|
253
|
+
this.logging.log("Bluetooth reconnect");
|
|
254
|
+
this.isReconnect = true;
|
|
255
|
+
if (isWindowsOS) {
|
|
256
|
+
// On Windows, the micro:bit can take around 3 seconds to respond to gatt.disconnect().
|
|
257
|
+
// Attempting to reconnect before the micro:bit has responded results in another
|
|
258
|
+
// gattserverdisconnected event being fired. We then fail to get primaryService on a
|
|
259
|
+
// disconnected GATT server.
|
|
260
|
+
await this.reconnectReadyPromise;
|
|
261
|
+
}
|
|
262
|
+
await this.connect();
|
|
263
|
+
}
|
|
264
|
+
assertGattServer() {
|
|
265
|
+
if (!this.device.gatt?.connected) {
|
|
266
|
+
throw new Error("Could not listen to services, no microbit connected!");
|
|
267
|
+
}
|
|
268
|
+
return this.device.gatt;
|
|
269
|
+
}
|
|
270
|
+
async getBoardVersion() {
|
|
271
|
+
this.assertGattServer();
|
|
272
|
+
const serviceMeta = profile.deviceInformation;
|
|
273
|
+
try {
|
|
274
|
+
const deviceInfo = await this.assertGattServer().getPrimaryService(serviceMeta.id);
|
|
275
|
+
const characteristic = await deviceInfo.getCharacteristic(serviceMeta.characteristics.modelNumber.id);
|
|
276
|
+
const modelNumberBytes = await characteristic.readValue();
|
|
277
|
+
const modelNumber = new TextDecoder().decode(modelNumberBytes);
|
|
278
|
+
if (modelNumber.toLowerCase() === "BBC micro:bit".toLowerCase()) {
|
|
279
|
+
return "V1";
|
|
280
|
+
}
|
|
281
|
+
if (modelNumber.toLowerCase().includes("BBC micro:bit v2".toLowerCase())) {
|
|
282
|
+
return "V2";
|
|
283
|
+
}
|
|
284
|
+
throw new Error(`Unexpected model number ${modelNumber}`);
|
|
285
|
+
}
|
|
286
|
+
catch (e) {
|
|
287
|
+
this.logging.error("Could not read model number", e);
|
|
288
|
+
throw new Error("Could not read model number");
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
async getAccelerometerService() {
|
|
292
|
+
if (!this.accelerometerService) {
|
|
293
|
+
const gattServer = this.assertGattServer();
|
|
294
|
+
this.accelerometerService = await AccelerometerService.createService(gattServer, this.dispatchTypedEvent, this.serviceListeners.accelerometerdatachanged.notifying);
|
|
295
|
+
}
|
|
296
|
+
return this.accelerometerService;
|
|
297
|
+
}
|
|
298
|
+
disposeServices() {
|
|
299
|
+
this.accelerometerService = undefined;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
export const createBluetoothDeviceWrapper = async (device, logging, dispatchTypedEvent) => {
|
|
303
|
+
try {
|
|
304
|
+
// Reuse our connection objects for the same device as they
|
|
305
|
+
// track the GATT connect promise that never resolves.
|
|
306
|
+
const bluetooth = deviceIdToWrapper.get(device.id) ??
|
|
307
|
+
new BluetoothDeviceWrapper(device, logging, dispatchTypedEvent);
|
|
308
|
+
deviceIdToWrapper.set(device.id, bluetooth);
|
|
309
|
+
await bluetooth.connect();
|
|
310
|
+
return bluetooth;
|
|
311
|
+
}
|
|
312
|
+
catch (e) {
|
|
313
|
+
return undefined;
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
//# sourceMappingURL=bluetooth-device-wrapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bluetooth-device-wrapper.js","sourceRoot":"","sources":["../lib/bluetooth-device-wrapper.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAE9C,OAAO,EAAW,WAAW,EAAE,MAAM,WAAW,CAAC;AAGjD,MAAM,iBAAiB,GAAwC,IAAI,GAAG,EAAE,CAAC;AAEzE,MAAM,sBAAsB,GAAW,KAAK,CAAC;AAE7C,SAAS,YAAY;IACnB,MAAM,SAAS,GAAQ,MAAM,CAAC,SAAS,CAAC;IACxC,MAAM,QAAQ,GAAG,SAAS,CAAC,aAAa,EAAE,QAAQ,CAAC;IACnD,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;IACpE,OAAO,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,IAAI,SAAS,CAAC;AACjE,CAAC;AACD,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;AAChC,MAAM,WAAW,GAAG,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAEtD,MAAM,OAAO,sBAAsB;IA6BjC,YACkB,MAAuB,EAC/B,UAAmB,IAAI,WAAW,EAAE,EACpC,kBAA+C;QAFvD;;;;mBAAgB,MAAM;WAAiB;QACvC;;;;mBAAQ,OAAO;WAA6B;QAC5C;;;;mBAAQ,kBAAkB;WAA6B;QA/BzD,gFAAgF;QAChF,0BAA0B;QAClB;;;;mBAA0C,CAAC;WAAC;QAEpD,mEAAmE;QACnE,iEAAiE;QACjE,oEAAoE;QACpE,iEAAiE;QACjE,oEAAoE;QACpE,2DAA2D;QAC3D,EAAE;QACF,oCAAoC;QACpC,+DAA+D;QACvD;;;;;WAA8C;QAC9C;;;;;WAAgD;QAChD;;;;mBAAa,KAAK;WAAC;QACnB;;;;mBAAc,KAAK;WAAC;QACpB;;;;;WAAiD;QACjD;;;;;WAAuD;QAE/D;;;;;WAAuC;QACvC;;;;mBAAmB;gBACjB,wBAAwB,EAAE;oBACxB,SAAS,EAAE,KAAK;oBAChB,OAAO,EAAE,IAAI,CAAC,uBAAuB;iBACtC;aACF;WAAC;QA8JF;;;;mBAAwB,KAAK,IAAmB,EAAE;gBAChD,sDAAsD;gBAEtD,IAAI,CAAC;oBACH,IAAI,CAAC,IAAI,CAAC,+BAA+B,EAAE,CAAC;wBAC1C,IAAI,CAAC,OAAO,CAAC,GAAG,CACd,+DAA+D,CAChE,CAAC;wBACF,gCAAgC;wBAChC,IAAI,CAAC,eAAe,EAAE,CAAC;wBACvB,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;oBACzB,CAAC;yBAAM,CAAC;wBACN,IAAI,CAAC,OAAO,CAAC,GAAG,CACd,8DAA8D,CAC/D,CAAC;oBACJ,CAAC;gBACH,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACX,IAAI,CAAC,OAAO,CAAC,KAAK,CAChB,2DAA2D,EAC3D,CAAC,CACF,CAAC;gBACJ,CAAC;YACH,CAAC;WAAC;QA7KA,MAAM,CAAC,gBAAgB,CACrB,wBAAwB,EACxB,IAAI,CAAC,qBAAqB,CAC3B,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;YACjB,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;YAChD,OAAO,EAAE,yBAAyB;SACnC,CAAC,CAAC;QACH,IAAI,IAAI,CAAC,+BAA+B,EAAE,CAAC;YACzC,IAAI,CAAC,OAAO,CAAC,GAAG,CACd,0DAA0D,CAC3D,CAAC;YACF,uEAAuE;YACvE,8EAA8E;YAC9E,yEAAyE;YACzE,MAAM,IAAI,CAAC,kBAAkB,CAAC;YAC9B,OAAO;QACT,CAAC;QACD,IAAI,CAAC,+BAA+B,EAAE,CAAC;QACvC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CACb,6DAA6D,CAC9D,CAAC;QACJ,CAAC;QACD,IAAI,CAAC;YACH,2FAA2F;YAC3F,MAAM,IAAI,CAAC,iBAAiB,CAAC;YAC7B,IAAI,CAAC,kBAAkB;gBACrB,IAAI,CAAC,kBAAkB;oBACvB,IAAI,CAAC,MAAM,CAAC,IAAI;yBACb,OAAO,EAAE;yBACT,IAAI,CAAC,KAAK,IAAI,EAAE;wBACf,6EAA6E;wBAC7E,8EAA8E;wBAC9E,4CAA4C;wBAC5C,IAAI,CAAC,YAAY,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;wBACjD,sFAAsF;wBACtF,oCAAoC;wBACpC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;4BACrB,IAAI,CAAC,OAAO,CAAC,GAAG,CACd,oEAAoE,CACrE,CAAC;4BACF,IAAI,CAAC,iBAAiB,GAAG,CAAC,KAAK,IAAI,EAAE;gCACnC,MAAM,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;gCACrC,IAAI,CAAC,iBAAiB,GAAG,SAAS,CAAC;4BACrC,CAAC,CAAC,EAAE,CAAC;wBACP,CAAC;6BAAM,CAAC;4BACN,IAAI,CAAC,OAAO,CAAC,GAAG,CACd,iDAAiD,CAClD,CAAC;wBACJ,CAAC;oBACH,CAAC,CAAC;yBACD,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;wBACX,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;4BACpB,uDAAuD;4BACvD,MAAM,CAAC,CAAC;wBACV,CAAC;6BAAM,CAAC;4BACN,IAAI,CAAC,OAAO,CAAC,KAAK,CAChB,uDAAuD,EACvD,CAAC,CACF,CAAC;4BACF,OAAO,SAAS,CAAC;wBACnB,CAAC;oBACH,CAAC,CAAC;yBACD,OAAO,CAAC,GAAG,EAAE;wBACZ,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC;wBAChE,IAAI,CAAC,kBAAkB,GAAG,SAAS,CAAC;oBACtC,CAAC,CAAC,CAAC;YAEP,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;YACvB,IAAI,CAAC;gBACH,MAAM,iBAAiB,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;oBAC3C,IAAI,CAAC,kBAAkB;oBACvB,IAAI,OAAO,CAAY,CAAC,OAAO,EAAE,EAAE,CACjC,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,sBAAsB,CAAC,CAC7D;iBACF,CAAC,CAAC;gBACH,IAAI,iBAAiB,KAAK,SAAS,EAAE,CAAC;oBACpC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;oBAC1D,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;gBAC3D,CAAC;YACH,CAAC;oBAAS,CAAC;gBACT,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;YAC1B,CAAC;YAED,yDAAyD;YACzD,4BAA4B;YAC5B,KAAK,MAAM,eAAe,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBACnE,IAAI,eAAe,CAAC,SAAS,EAAE,CAAC;oBAC9B,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACrC,CAAC;YACH,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;gBACjB,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;gBAChD,OAAO,EAAE,2BAA2B;aACrC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,CAAC,CAAC,CAAC;YACjD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;gBACjB,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;gBAChD,OAAO,EAAE,0BAA0B;aACpC,CAAC,CAAC;YACH,MAAM,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;QACvD,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,+BAA+B,EAAE,CAAC;QACzC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,UAAU;QACd,OAAO,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IAEO,KAAK,CAAC,kBAAkB,CAAC,aAAsB;QACrD,IAAI,CAAC,OAAO,CAAC,GAAG,CACd,wBAAwB,aAAa,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAChF,CAAC;QACF,IAAI,CAAC,+BAA+B,EAAE,CAAC;QACvC,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC;gBAChC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC;YACjC,CAAC;QACH,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,2CAA2C,EAAE,CAAC,CAAC,CAAC;YACnE,6CAA6C;QAC/C,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,eAAe,EAAE,CAAC;YACvB,IAAI,CAAC,+BAA+B,EAAE,CAAC;QACzC,CAAC;QACD,IAAI,CAAC,qBAAqB,GAAG,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CACnD,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAC3B,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,SAAS;QACb,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QACxC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,WAAW,EAAE,CAAC;YAChB,uFAAuF;YACvF,gFAAgF;YAChF,oFAAoF;YACpF,4BAA4B;YAC5B,MAAM,IAAI,CAAC,qBAAqB,CAAC;QACnC,CAAC;QACD,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;IACvB,CAAC;IA0BO,gBAAgB;QACtB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;QAC1E,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;IAC1B,CAAC;IAEO,KAAK,CAAC,eAAe;QAC3B,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxB,MAAM,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC;QAC9C,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC,iBAAiB,CAChE,WAAW,CAAC,EAAE,CACf,CAAC;YACF,MAAM,cAAc,GAAG,MAAM,UAAU,CAAC,iBAAiB,CACvD,WAAW,CAAC,eAAe,CAAC,WAAW,CAAC,EAAE,CAC3C,CAAC;YACF,MAAM,gBAAgB,GAAG,MAAM,cAAc,CAAC,SAAS,EAAE,CAAC;YAC1D,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;YAC/D,IAAI,WAAW,CAAC,WAAW,EAAE,KAAK,eAAe,CAAC,WAAW,EAAE,EAAE,CAAC;gBAChE,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IACE,WAAW,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,kBAAkB,CAAC,WAAW,EAAE,CAAC,EACpE,CAAC;gBACD,OAAO,IAAI,CAAC;YACd,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,2BAA2B,WAAW,EAAE,CAAC,CAAC;QAC5D,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,CAAC,CAAC,CAAC;YACrD,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IAED,KAAK,CAAC,uBAAuB;QAC3B,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC/B,MAAM,UAAU,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC3C,IAAI,CAAC,oBAAoB,GAAG,MAAM,oBAAoB,CAAC,aAAa,CAClE,UAAU,EACV,IAAI,CAAC,kBAAkB,EACvB,IAAI,CAAC,gBAAgB,CAAC,wBAAwB,CAAC,SAAS,CACzD,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,oBAAoB,CAAC;IACnC,CAAC;IAEO,eAAe;QACrB,IAAI,CAAC,oBAAoB,GAAG,SAAS,CAAC;IACxC,CAAC;CACF;AAED,MAAM,CAAC,MAAM,4BAA4B,GAAG,KAAK,EAC/C,MAAuB,EACvB,OAAgB,EAChB,kBAA+C,EACF,EAAE;IAC/C,IAAI,CAAC;QACH,2DAA2D;QAC3D,sDAAsD;QACtD,MAAM,SAAS,GACb,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAChC,IAAI,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC;QAClE,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;QAC5C,MAAM,SAAS,CAAC,OAAO,EAAE,CAAC;QAC1B,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC,CAAC"}
|