sportident.js 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dependency-cruiser.js +233 -0
- package/.editorconfig +12 -0
- package/.eslintignore +6 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +35 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +17 -0
- package/.github/workflows/npm.yml +17 -0
- package/LICENSE +21 -0
- package/README.md +31 -0
- package/babel.config.js +21 -0
- package/build-docs.sh +25 -0
- package/docs/index.md +9 -0
- package/docs/typedoc/index.md +11 -0
- package/eslint.base.js +232 -0
- package/install.sh +6 -0
- package/jest.config.ts +49 -0
- package/nx.json +39 -0
- package/package.json +51 -0
- package/src/SiCard/BaseSiCard.test.ts +187 -0
- package/src/SiCard/BaseSiCard.ts +101 -0
- package/src/SiCard/IRaceResultData.ts +16 -0
- package/src/SiCard/ISiCard.ts +23 -0
- package/src/SiCard/ISiCardExamples.ts +4 -0
- package/src/SiCard/index.ts +2 -0
- package/src/SiCard/raceResultTools.test.ts +260 -0
- package/src/SiCard/raceResultTools.ts +150 -0
- package/src/SiCard/types/FCard.test.ts +19 -0
- package/src/SiCard/types/FCard.ts +14 -0
- package/src/SiCard/types/ModernSiCard.test.ts +186 -0
- package/src/SiCard/types/ModernSiCard.ts +241 -0
- package/src/SiCard/types/PCard.test.ts +19 -0
- package/src/SiCard/types/PCard.ts +14 -0
- package/src/SiCard/types/SIAC.test.ts +84 -0
- package/src/SiCard/types/SIAC.ts +19 -0
- package/src/SiCard/types/SiCard10.test.ts +85 -0
- package/src/SiCard/types/SiCard10.ts +17 -0
- package/src/SiCard/types/SiCard11.test.ts +84 -0
- package/src/SiCard/types/SiCard11.ts +19 -0
- package/src/SiCard/types/SiCard5.test.ts +149 -0
- package/src/SiCard/types/SiCard5.ts +129 -0
- package/src/SiCard/types/SiCard6.test.ts +179 -0
- package/src/SiCard/types/SiCard6.ts +222 -0
- package/src/SiCard/types/SiCard8.test.ts +137 -0
- package/src/SiCard/types/SiCard8.ts +129 -0
- package/src/SiCard/types/SiCard9.test.ts +132 -0
- package/src/SiCard/types/SiCard9.ts +128 -0
- package/src/SiCard/types/TCard.test.ts +19 -0
- package/src/SiCard/types/TCard.ts +14 -0
- package/src/SiCard/types/index.test.ts +26 -0
- package/src/SiCard/types/index.ts +15 -0
- package/src/SiCard/types/modernSiCardExamples.ts +364 -0
- package/src/SiCard/types/siCard5Examples.ts +73 -0
- package/src/SiCard/types/siCard6Examples.ts +262 -0
- package/src/SiCard/types/siCard8Examples.ts +152 -0
- package/src/SiCard/types/siCard9Examples.ts +143 -0
- package/src/SiDevice/INavigatorWebSerial.ts +78 -0
- package/src/SiDevice/INavigatorWebUsb.ts +62 -0
- package/src/SiDevice/ISiDevice.ts +48 -0
- package/src/SiDevice/ISiDeviceDriver.ts +35 -0
- package/src/SiDevice/README.md +13 -0
- package/src/SiDevice/SiDevice.test.ts +354 -0
- package/src/SiDevice/SiDevice.ts +132 -0
- package/src/SiDevice/WebSerialSiDeviceDriver.ts +146 -0
- package/src/SiDevice/WebUsbSiDeviceDriver.ts +343 -0
- package/src/SiDevice/index.ts +3 -0
- package/src/SiDevice/testUtils/index.ts +2 -0
- package/src/SiDevice/testUtils/testISiDeviceDriver.ts +63 -0
- package/src/SiDevice/testUtils/testISiDeviceDriverWithAutodetection.ts +72 -0
- package/src/SiStation/BaseSiStation.test.ts +221 -0
- package/src/SiStation/BaseSiStation.ts +253 -0
- package/src/SiStation/CoupledSiStation.test.ts +41 -0
- package/src/SiStation/CoupledSiStation.ts +130 -0
- package/src/SiStation/ISiMainStation.ts +29 -0
- package/src/SiStation/ISiSendTask.ts +9 -0
- package/src/SiStation/ISiStation.ts +88 -0
- package/src/SiStation/ISiTargetMultiplexer.ts +51 -0
- package/src/SiStation/SiMainStation.test.ts +222 -0
- package/src/SiStation/SiMainStation.ts +133 -0
- package/src/SiStation/SiSendTask.ts +50 -0
- package/src/SiStation/SiTargetMultiplexer.targeting.test.ts +112 -0
- package/src/SiStation/SiTargetMultiplexer.test.ts +605 -0
- package/src/SiStation/SiTargetMultiplexer.ts +241 -0
- package/src/SiStation/index.ts +5 -0
- package/src/SiStation/siStationExamples.ts +103 -0
- package/src/constants.test.ts +17 -0
- package/src/constants.ts +92 -0
- package/src/fakes/FakeSiCard/BaseFakeSiCard.test.ts +11 -0
- package/src/fakes/FakeSiCard/BaseFakeSiCard.ts +10 -0
- package/src/fakes/FakeSiCard/IFakeSiCard.ts +6 -0
- package/src/fakes/FakeSiCard/index.ts +2 -0
- package/src/fakes/FakeSiCard/types/FakeModernSiCard.test.ts +62 -0
- package/src/fakes/FakeSiCard/types/FakeModernSiCard.ts +43 -0
- package/src/fakes/FakeSiCard/types/FakeSIAC.ts +17 -0
- package/src/fakes/FakeSiCard/types/FakeSiCard10.ts +17 -0
- package/src/fakes/FakeSiCard/types/FakeSiCard11.ts +17 -0
- package/src/fakes/FakeSiCard/types/FakeSiCard5.test.ts +42 -0
- package/src/fakes/FakeSiCard/types/FakeSiCard5.ts +40 -0
- package/src/fakes/FakeSiCard/types/FakeSiCard6.test.ts +62 -0
- package/src/fakes/FakeSiCard/types/FakeSiCard6.ts +44 -0
- package/src/fakes/FakeSiCard/types/FakeSiCard8.ts +16 -0
- package/src/fakes/FakeSiCard/types/FakeSiCard9.ts +16 -0
- package/src/fakes/FakeSiCard/types/index.ts +7 -0
- package/src/fakes/FakeSiDeviceDriver.ts +148 -0
- package/src/fakes/FakeSiMainStation.test.ts +141 -0
- package/src/fakes/FakeSiMainStation.ts +118 -0
- package/src/fakes/IFakeSiMainStation.ts +15 -0
- package/src/fakes/index.ts +2 -0
- package/src/index.ts +24 -0
- package/src/siProtocol.test.ts +509 -0
- package/src/siProtocol.ts +417 -0
- package/src/storage/SiArray.test.ts +103 -0
- package/src/storage/SiArray.ts +56 -0
- package/src/storage/SiBool.test.ts +81 -0
- package/src/storage/SiBool.ts +47 -0
- package/src/storage/SiDataType.test.ts +81 -0
- package/src/storage/SiDataType.ts +60 -0
- package/src/storage/SiDict.test.ts +115 -0
- package/src/storage/SiDict.ts +60 -0
- package/src/storage/SiEnum.test.ts +77 -0
- package/src/storage/SiEnum.ts +48 -0
- package/src/storage/SiFieldValue.test.ts +58 -0
- package/src/storage/SiFieldValue.ts +23 -0
- package/src/storage/SiInt.test.ts +80 -0
- package/src/storage/SiInt.ts +84 -0
- package/src/storage/SiModified.test.ts +135 -0
- package/src/storage/SiModified.ts +59 -0
- package/src/storage/SiStorage.test.ts +51 -0
- package/src/storage/SiStorage.ts +44 -0
- package/src/storage/index.test.ts +222 -0
- package/src/storage/index.ts +12 -0
- package/src/storage/interfaces.ts +41 -0
- package/src/storage/siStringEncoding.ts +1361 -0
- package/src/testUtils.test.ts +266 -0
- package/src/testUtils.ts +75 -0
- package/src/utils/NumberRange.test.ts +66 -0
- package/src/utils/NumberRange.ts +46 -0
- package/src/utils/NumberRangeRegistry.test.ts +49 -0
- package/src/utils/NumberRangeRegistry.ts +43 -0
- package/src/utils/bytes.test.ts +126 -0
- package/src/utils/bytes.ts +69 -0
- package/src/utils/errors.test.ts +29 -0
- package/src/utils/errors.ts +20 -0
- package/src/utils/events.test.ts +112 -0
- package/src/utils/events.ts +68 -0
- package/src/utils/general.test.ts +139 -0
- package/src/utils/general.ts +69 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/mixins.test.ts +40 -0
- package/src/utils/mixins.ts +13 -0
- package/src/utils/typed.ts +3 -0
- package/tsconfig.base.json +22 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import * as utils from '../utils';
|
|
2
|
+
import { DeviceClosedError, SiDeviceState } from '../SiDevice/ISiDevice';
|
|
3
|
+
import type { ISiDevice, ISiDeviceDriverData } from '../SiDevice/ISiDevice';
|
|
4
|
+
import { SiDeviceAddEvent, SiDeviceRemoveEvent } from '../SiDevice/ISiDeviceDriver';
|
|
5
|
+
import type { ISiDeviceDriver, ISiDeviceDriverWithAutodetection, ISiDeviceDriverWithDetection, SiDeviceDriverWithAutodetectionEvents } from '../SiDevice/ISiDeviceDriver';
|
|
6
|
+
import { SiDevice } from '../SiDevice/SiDevice';
|
|
7
|
+
import type * as nav from './INavigatorWebUsb';
|
|
8
|
+
|
|
9
|
+
const siConfiguration = 1;
|
|
10
|
+
const siAlternate = 0;
|
|
11
|
+
const siPacketSize = 64;
|
|
12
|
+
const siUsbClassCode = 255;
|
|
13
|
+
const siDeviceFilters = [{ vendorId: 0x10c4, productId: 0x800a }];
|
|
14
|
+
const matchesSiDeviceFilters = (vendorId: number, productId: number) => siDeviceFilters.some((filter) => vendorId === filter.vendorId && productId === filter.productId);
|
|
15
|
+
|
|
16
|
+
const getIdent = (device: nav.WebUsbDevice) => `${device.serialNumber}`;
|
|
17
|
+
|
|
18
|
+
function findEndpoint(iface: USBInterface, direction: USBDirection): USBEndpoint {
|
|
19
|
+
const alternate = iface.alternates[0];
|
|
20
|
+
for (const endpoint of alternate.endpoints) {
|
|
21
|
+
if (endpoint.direction == direction) {
|
|
22
|
+
return endpoint;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
throw new TypeError(`Interface ${iface.interfaceNumber} does not have an ` + `${direction} endpoint.`);
|
|
26
|
+
}
|
|
27
|
+
function findInterface(device: nav.WebUsbDevice): USBInterface {
|
|
28
|
+
const configuration = device.configurations[0];
|
|
29
|
+
for (const iface of configuration.interfaces) {
|
|
30
|
+
const alternate = iface.alternates[0];
|
|
31
|
+
if (alternate.interfaceClass === siUsbClassCode) {
|
|
32
|
+
return iface;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
throw new TypeError(`Unable to find interface with class ${siUsbClassCode}.`);
|
|
36
|
+
}
|
|
37
|
+
export interface WebUsbSiDeviceDriverData extends ISiDeviceDriverData<WebUsbSiDeviceDriver> {
|
|
38
|
+
driver: WebUsbSiDeviceDriver;
|
|
39
|
+
device: nav.WebUsbDevice;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface WebUsbAutodetectionCallbacks {
|
|
43
|
+
onConnect: utils.EventCallback<nav.WebUsbConnectEvent>;
|
|
44
|
+
onDisconnect: utils.EventCallback<nav.WebUsbDisconnectEvent>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type IWebUsbSiDevice = ISiDevice<WebUsbSiDeviceDriverData>;
|
|
48
|
+
export type WebUsbSiDevice = SiDevice<WebUsbSiDeviceDriverData>;
|
|
49
|
+
|
|
50
|
+
class WebUsbSiDeviceDriver
|
|
51
|
+
implements ISiDeviceDriver<WebUsbSiDeviceDriverData>, ISiDeviceDriverWithDetection<WebUsbSiDeviceDriverData, []>, ISiDeviceDriverWithAutodetection<WebUsbSiDeviceDriverData>
|
|
52
|
+
{
|
|
53
|
+
public name = 'WebUSB';
|
|
54
|
+
|
|
55
|
+
private siDeviceByIdent: { [ident: string]: WebUsbSiDevice } = {};
|
|
56
|
+
|
|
57
|
+
private autodetectedSiDevices: { [ident: string]: WebUsbSiDevice } = {};
|
|
58
|
+
|
|
59
|
+
private autodetectionCallbacks?: WebUsbAutodetectionCallbacks;
|
|
60
|
+
|
|
61
|
+
constructor(private navigatorUsb: nav.WebUsb) {}
|
|
62
|
+
|
|
63
|
+
detect(): Promise<WebUsbSiDevice> {
|
|
64
|
+
return this.navigatorUsb
|
|
65
|
+
.requestDevice({
|
|
66
|
+
filters: siDeviceFilters
|
|
67
|
+
})
|
|
68
|
+
.catch((e) => {
|
|
69
|
+
return Promise.reject(new Error('Failed to get access to USB device! ' + e.toString()));
|
|
70
|
+
})
|
|
71
|
+
.then((navigatorWebUsbDevice: nav.WebUsbDevice) => this.autodetectSiDevice(navigatorWebUsbDevice))
|
|
72
|
+
.catch((e) => {
|
|
73
|
+
return Promise.reject(new Error('Failed to initialize Si Device! ' + e.toString()));
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
getSiDevice(navigatorWebUsbDevice: nav.WebUsbDevice): WebUsbSiDevice {
|
|
78
|
+
const ident = getIdent(navigatorWebUsbDevice);
|
|
79
|
+
if (this.siDeviceByIdent[ident] !== undefined) {
|
|
80
|
+
return this.siDeviceByIdent[ident];
|
|
81
|
+
}
|
|
82
|
+
const newSiDeviceData: WebUsbSiDeviceDriverData = {
|
|
83
|
+
driver: this,
|
|
84
|
+
device: navigatorWebUsbDevice
|
|
85
|
+
};
|
|
86
|
+
const newSiDevice = new SiDevice(ident, newSiDeviceData);
|
|
87
|
+
this.siDeviceByIdent[ident] = newSiDevice;
|
|
88
|
+
return newSiDevice;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
forgetSiDevice(siDevice: IWebUsbSiDevice) {
|
|
92
|
+
const navigatorWebUsbDevice = siDevice.data.device;
|
|
93
|
+
const ident = getIdent(navigatorWebUsbDevice);
|
|
94
|
+
delete this.siDeviceByIdent[ident];
|
|
95
|
+
if (this.autodetectedSiDevices[ident] !== undefined) {
|
|
96
|
+
this.dispatchEvent('remove', new SiDeviceRemoveEvent(siDevice));
|
|
97
|
+
}
|
|
98
|
+
delete this.autodetectedSiDevices[ident];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
startAutoDetection(): Promise<IWebUsbSiDevice[]> {
|
|
102
|
+
this.registerAutodetectionCallbacks();
|
|
103
|
+
return this.getAutodetectedDevices();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
getAutodetectedDevices(): Promise<WebUsbSiDevice[]> {
|
|
107
|
+
return this.navigatorUsb.getDevices().then((navigatorWebUsbDevices: nav.WebUsbDevice[]) => this.autodetectSiDevices(navigatorWebUsbDevices));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
autodetectSiDevices(navigatorWebUsbDevices: nav.WebUsbDevice[]): Promise<WebUsbSiDevice[]> {
|
|
111
|
+
// TODO: Make this easier when Promise.allSettled polyfill is available
|
|
112
|
+
return new Promise((resolve) => {
|
|
113
|
+
let numSettled = 0;
|
|
114
|
+
const devices: WebUsbSiDevice[] = [];
|
|
115
|
+
const onSettled = () => {
|
|
116
|
+
numSettled += 1;
|
|
117
|
+
if (numSettled === navigatorWebUsbDevices.length) {
|
|
118
|
+
resolve(devices);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
navigatorWebUsbDevices.forEach((navigatorWebUsbDevice: nav.WebUsbDevice) =>
|
|
122
|
+
this.autodetectSiDevice(navigatorWebUsbDevice)
|
|
123
|
+
.then((siDevice: WebUsbSiDevice) => {
|
|
124
|
+
devices.push(siDevice);
|
|
125
|
+
onSettled();
|
|
126
|
+
})
|
|
127
|
+
.catch(() => onSettled())
|
|
128
|
+
);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
autodetectSiDevice(navigatorWebUsbDevice: nav.WebUsbDevice): Promise<WebUsbSiDevice> {
|
|
133
|
+
if (!matchesSiDeviceFilters(navigatorWebUsbDevice.vendorId, navigatorWebUsbDevice.productId)) {
|
|
134
|
+
return Promise.reject(new Error('Not a SI device'));
|
|
135
|
+
}
|
|
136
|
+
const ident = getIdent(navigatorWebUsbDevice);
|
|
137
|
+
if (this.autodetectedSiDevices[ident] !== undefined) {
|
|
138
|
+
return Promise.reject(new Error('Duplicate SI device'));
|
|
139
|
+
}
|
|
140
|
+
const siDevice = this.getSiDevice(navigatorWebUsbDevice);
|
|
141
|
+
this.autodetectedSiDevices[ident] = siDevice;
|
|
142
|
+
return siDevice.open();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
registerAutodetectionCallbacks(): void {
|
|
146
|
+
if (this.autodetectionCallbacks !== undefined) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const onConnectCallback = (event: nav.WebUsbConnectEvent) => {
|
|
150
|
+
const navigatorWebUsbDevice = event.device;
|
|
151
|
+
this.autodetectSiDevice(navigatorWebUsbDevice)
|
|
152
|
+
.then((openedDevice: WebUsbSiDevice) => {
|
|
153
|
+
this.dispatchEvent('add', new SiDeviceAddEvent(openedDevice));
|
|
154
|
+
})
|
|
155
|
+
};
|
|
156
|
+
this.navigatorUsb.addEventListener('connect', onConnectCallback);
|
|
157
|
+
const onDisconnectCallback = (event: nav.WebUsbDisconnectEvent) => {
|
|
158
|
+
const navigatorWebUsbDevice = event.device;
|
|
159
|
+
const ident = getIdent(navigatorWebUsbDevice);
|
|
160
|
+
const siDevice = this.siDeviceByIdent[ident];
|
|
161
|
+
if (siDevice === undefined) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
this.forgetSiDevice(siDevice);
|
|
165
|
+
};
|
|
166
|
+
this.navigatorUsb.addEventListener('disconnect', onDisconnectCallback);
|
|
167
|
+
this.autodetectionCallbacks = {
|
|
168
|
+
onConnect: onConnectCallback,
|
|
169
|
+
onDisconnect: onDisconnectCallback
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
stopAutoDetection(): Promise<void> {
|
|
174
|
+
this.deregisterAutodetectionCallbacks();
|
|
175
|
+
return this.closeAutoOpened();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
deregisterAutodetectionCallbacks(): void {
|
|
179
|
+
if (this.autodetectionCallbacks === undefined) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
this.navigatorUsb.removeEventListener('connect', this.autodetectionCallbacks.onConnect);
|
|
183
|
+
this.navigatorUsb.removeEventListener('disconnect', this.autodetectionCallbacks.onDisconnect);
|
|
184
|
+
this.autodetectionCallbacks = undefined;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
closeAutoOpened(): Promise<void> {
|
|
188
|
+
return Promise.all(Object.values(this.autodetectedSiDevices).map((autoOpenedDevice) => autoOpenedDevice.close())).then(() => {
|
|
189
|
+
this.autodetectedSiDevices = {};
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
open(device: IWebUsbSiDevice): Promise<void> {
|
|
194
|
+
console.debug('Opening...');
|
|
195
|
+
const navigatorDevice = device.data.device;
|
|
196
|
+
return navigatorDevice
|
|
197
|
+
.open()
|
|
198
|
+
.then(() => {
|
|
199
|
+
try {
|
|
200
|
+
console.debug('Resetting...');
|
|
201
|
+
return navigatorDevice.reset();
|
|
202
|
+
} catch (e) {
|
|
203
|
+
console.error('Failed resetting web usb device:', e);
|
|
204
|
+
}
|
|
205
|
+
})
|
|
206
|
+
.catch((_e) => {
|
|
207
|
+
return Promise.reject(new Error('Failed to reset usb device'));
|
|
208
|
+
})
|
|
209
|
+
.then(() => {
|
|
210
|
+
try {
|
|
211
|
+
console.debug('Selecting Configuration...');
|
|
212
|
+
return navigatorDevice.selectConfiguration(siConfiguration);
|
|
213
|
+
} catch (e) {
|
|
214
|
+
console.error('Failed selecting configuration of web usb device:', e);
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
.catch((_e) => {
|
|
218
|
+
return Promise.reject(new Error('Failed to select configuration of usb device'));
|
|
219
|
+
})
|
|
220
|
+
.then(() => {
|
|
221
|
+
try {
|
|
222
|
+
console.debug('Claiming Interface...');
|
|
223
|
+
return navigatorDevice.claimInterface(findInterface(navigatorDevice).interfaceNumber);
|
|
224
|
+
} catch (e) {
|
|
225
|
+
console.error('Failed claiming web usb device interface:', e);
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
.catch((_e) => {
|
|
229
|
+
return Promise.reject(new Error('Failed to claim usb device interface'));
|
|
230
|
+
})
|
|
231
|
+
.then(() => {
|
|
232
|
+
try {
|
|
233
|
+
console.debug('Selection Alternate Interface...');
|
|
234
|
+
return navigatorDevice.selectAlternateInterface(findInterface(navigatorDevice).interfaceNumber, siAlternate);
|
|
235
|
+
} catch (e) {
|
|
236
|
+
console.error('Failed selecting alternate interface web usb device:', e);
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
.catch((_e) => {
|
|
240
|
+
return Promise.reject(new Error('Failed to select alternate interface of usb device'));
|
|
241
|
+
})
|
|
242
|
+
.then(() => {
|
|
243
|
+
try {
|
|
244
|
+
console.debug('Enabling Serial...');
|
|
245
|
+
return navigatorDevice.controlTransferOut({
|
|
246
|
+
requestType: 'vendor',
|
|
247
|
+
recipient: 'interface',
|
|
248
|
+
request: 0x00,
|
|
249
|
+
value: 0x01,
|
|
250
|
+
index: findInterface(navigatorDevice).interfaceNumber
|
|
251
|
+
});
|
|
252
|
+
} catch (e) {
|
|
253
|
+
console.error('Failed enabling serial on web usb device:', e);
|
|
254
|
+
}
|
|
255
|
+
})
|
|
256
|
+
.catch((_e) => {
|
|
257
|
+
return Promise.reject(new Error('Failed to enable serial on usb device'));
|
|
258
|
+
})
|
|
259
|
+
.then(() => {
|
|
260
|
+
try {
|
|
261
|
+
console.debug('Setting Baudrate...');
|
|
262
|
+
return navigatorDevice.controlTransferOut(
|
|
263
|
+
{
|
|
264
|
+
requestType: 'vendor',
|
|
265
|
+
recipient: 'interface',
|
|
266
|
+
request: 0x1e,
|
|
267
|
+
value: 0x00,
|
|
268
|
+
index: findInterface(navigatorDevice).interfaceNumber
|
|
269
|
+
},
|
|
270
|
+
new Uint8Array([0x00, 0x96, 0x00, 0x00]).buffer
|
|
271
|
+
);
|
|
272
|
+
} catch (e) {
|
|
273
|
+
console.error('Failed setting baudrate on web usb device:', e);
|
|
274
|
+
}
|
|
275
|
+
})
|
|
276
|
+
.catch((_e) => {
|
|
277
|
+
return Promise.reject(new Error('Failed to set baudrate on usb device'));
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async close(device: IWebUsbSiDevice): Promise<void> {
|
|
282
|
+
console.debug('Disabling Serial...');
|
|
283
|
+
const navigatorDevice = device.data.device;
|
|
284
|
+
navigatorDevice
|
|
285
|
+
.controlTransferOut({
|
|
286
|
+
requestType: 'vendor',
|
|
287
|
+
recipient: 'interface',
|
|
288
|
+
request: 0x00,
|
|
289
|
+
value: 0x00,
|
|
290
|
+
index: findInterface(navigatorDevice).interfaceNumber
|
|
291
|
+
})
|
|
292
|
+
.catch((_e) => {
|
|
293
|
+
return Promise.reject(new Error('Failed to shutdown usb device'));
|
|
294
|
+
})
|
|
295
|
+
.then(() => {
|
|
296
|
+
console.debug('Releasing Interface...');
|
|
297
|
+
return navigatorDevice.releaseInterface(findInterface(navigatorDevice).interfaceNumber);
|
|
298
|
+
})
|
|
299
|
+
.catch((_e) => {
|
|
300
|
+
return Promise.reject(new Error('Failed to release claimed usb device interface'));
|
|
301
|
+
})
|
|
302
|
+
.then(() => {
|
|
303
|
+
console.debug('Closing Device...');
|
|
304
|
+
return navigatorDevice.close();
|
|
305
|
+
})
|
|
306
|
+
.catch((_e) => {
|
|
307
|
+
return Promise.reject(new Error('Failed to close usb device'));
|
|
308
|
+
})
|
|
309
|
+
.then(() => {
|
|
310
|
+
return this.forgetSiDevice(device);
|
|
311
|
+
})
|
|
312
|
+
.catch((_e) => {
|
|
313
|
+
return Promise.reject(new Error('Failed to forget usb device'));
|
|
314
|
+
})
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
receive(device: IWebUsbSiDevice): Promise<number[]> {
|
|
318
|
+
const navigatorDevice = device.data.device;
|
|
319
|
+
if (navigatorDevice.opened !== true) {
|
|
320
|
+
console.warn('Device has been closed. Stopping receive loop.');
|
|
321
|
+
device.setState(SiDeviceState.Closed);
|
|
322
|
+
throw new DeviceClosedError();
|
|
323
|
+
}
|
|
324
|
+
return navigatorDevice.transferIn(findEndpoint(findInterface(navigatorDevice), 'in').endpointNumber, siPacketSize).then((response) => {
|
|
325
|
+
if (!response.data) {
|
|
326
|
+
return [];
|
|
327
|
+
}
|
|
328
|
+
const uint8Data = new Uint8Array(response.data.buffer);
|
|
329
|
+
return [...uint8Data];
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
async send(device: IWebUsbSiDevice, uint8Data: number[]): Promise<void> {
|
|
334
|
+
const navigatorDevice = device.data.device;
|
|
335
|
+
const buffer = new Uint8Array(uint8Data);
|
|
336
|
+
navigatorDevice.transferOut(findEndpoint(findInterface(navigatorDevice), 'out').endpointNumber, buffer).then(() => true);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
340
|
+
interface WebUsbSiDeviceDriver extends utils.EventTarget<SiDeviceDriverWithAutodetectionEvents<WebUsbSiDeviceDriverData>> {}
|
|
341
|
+
utils.applyMixins(WebUsbSiDeviceDriver, [utils.EventTarget]);
|
|
342
|
+
|
|
343
|
+
export const getWebUsbSiDeviceDriver = (navigatorWebUsb: USB): WebUsbSiDeviceDriver => new WebUsbSiDeviceDriver(<nav.WebUsb>(<unknown>navigatorWebUsb));
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { expect, test } from '@jest/globals';
|
|
2
|
+
import * as testUtils from '../../testUtils';
|
|
3
|
+
import type { ISiDevice, ISiDeviceDriverData } from '../ISiDevice';
|
|
4
|
+
import type { ISiDeviceDriver } from '../ISiDeviceDriver';
|
|
5
|
+
|
|
6
|
+
export interface TestISiDeviceDriverOptions {
|
|
7
|
+
numTicks: number;
|
|
8
|
+
waitFor: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const testISiDeviceDriver = <T extends ISiDeviceDriverData<ISiDeviceDriver<T>>>(
|
|
12
|
+
siDeviceDriverData: T,
|
|
13
|
+
options: TestISiDeviceDriverOptions = {
|
|
14
|
+
numTicks: 10,
|
|
15
|
+
waitFor: 1
|
|
16
|
+
}
|
|
17
|
+
): (() => void) => {
|
|
18
|
+
const testFunction = () => {
|
|
19
|
+
const advanceTime = () => testUtils.advanceTimersByTime(options.waitFor);
|
|
20
|
+
const waitForDriver = () => testUtils.nTimesAsync(options.numTicks, advanceTime);
|
|
21
|
+
test('driver has a name', () => {
|
|
22
|
+
expect(siDeviceDriverData.driver.name).not.toBe('');
|
|
23
|
+
});
|
|
24
|
+
test('driver can open-receive-send-close', () => {
|
|
25
|
+
const asyncCall = async () => {
|
|
26
|
+
const siDevice = {
|
|
27
|
+
data: siDeviceDriverData
|
|
28
|
+
} as ISiDevice<T>;
|
|
29
|
+
|
|
30
|
+
let openSuccessful = false;
|
|
31
|
+
siDeviceDriverData.driver.open(siDevice).then(() => {
|
|
32
|
+
openSuccessful = true;
|
|
33
|
+
});
|
|
34
|
+
await waitForDriver();
|
|
35
|
+
expect(openSuccessful).toBe(true);
|
|
36
|
+
|
|
37
|
+
let successfullyReceived: number[] | undefined = undefined;
|
|
38
|
+
siDeviceDriverData.driver.receive(siDevice).then((received: number[]) => {
|
|
39
|
+
successfullyReceived = received;
|
|
40
|
+
});
|
|
41
|
+
await waitForDriver();
|
|
42
|
+
expect(successfullyReceived).toEqual([0x01, 0x02, 0x03]);
|
|
43
|
+
|
|
44
|
+
let sendSuccessful = false;
|
|
45
|
+
siDeviceDriverData.driver.send(siDevice, [0x03, 0x02, 0x01]).then(() => {
|
|
46
|
+
sendSuccessful = true;
|
|
47
|
+
});
|
|
48
|
+
await waitForDriver();
|
|
49
|
+
expect(sendSuccessful).toBe(true);
|
|
50
|
+
|
|
51
|
+
let closeSuccessful = false;
|
|
52
|
+
siDeviceDriverData.driver.close(siDevice).then(() => {
|
|
53
|
+
closeSuccessful = true;
|
|
54
|
+
});
|
|
55
|
+
await waitForDriver();
|
|
56
|
+
expect(closeSuccessful).toBe(true);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
asyncCall();
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
return testFunction;
|
|
63
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { expect, test } from '@jest/globals';
|
|
2
|
+
import * as testUtils from '../../testUtils';
|
|
3
|
+
import { type ISiDeviceDriverData, SiDeviceState } from '../ISiDevice';
|
|
4
|
+
import type { ISiDeviceDriver, ISiDeviceDriverWithAutodetection } from '../ISiDeviceDriver';
|
|
5
|
+
|
|
6
|
+
export interface TestISiDeviceDriverWithAutodetectionOptions {
|
|
7
|
+
numTicks: number;
|
|
8
|
+
waitFor: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const testISiDeviceDriverWithAutodetection = <T extends ISiDeviceDriverData<ISiDeviceDriver<T> & ISiDeviceDriverWithAutodetection<T>>>(
|
|
12
|
+
siDeviceDriverData: T,
|
|
13
|
+
nonSiDeviceDriverData: T,
|
|
14
|
+
simulateConnect: (data: T) => void,
|
|
15
|
+
simulateDisconnect: (data: T) => void,
|
|
16
|
+
options: TestISiDeviceDriverWithAutodetectionOptions = {
|
|
17
|
+
numTicks: 50,
|
|
18
|
+
waitFor: 1
|
|
19
|
+
}
|
|
20
|
+
): (() => void) => {
|
|
21
|
+
const testFunction = () => {
|
|
22
|
+
const advanceTime = () => testUtils.advanceTimersByTime(options.waitFor);
|
|
23
|
+
const waitForDriver = () => testUtils.nTimesAsync(options.numTicks, advanceTime);
|
|
24
|
+
test('driver can startAutoDetection-connect-disconnect-stopAutodetection', async () => {
|
|
25
|
+
const driver = siDeviceDriverData.driver;
|
|
26
|
+
|
|
27
|
+
let numAddCalled = 0;
|
|
28
|
+
let numRemoveCalled = 0;
|
|
29
|
+
driver.addEventListener('add', () => numAddCalled++);
|
|
30
|
+
driver.addEventListener('remove', () => numRemoveCalled++);
|
|
31
|
+
|
|
32
|
+
simulateConnect(siDeviceDriverData);
|
|
33
|
+
await waitForDriver();
|
|
34
|
+
expect([numAddCalled, numRemoveCalled]).toEqual([0, 0]);
|
|
35
|
+
|
|
36
|
+
simulateDisconnect(siDeviceDriverData);
|
|
37
|
+
await waitForDriver();
|
|
38
|
+
expect([numAddCalled, numRemoveCalled]).toEqual([0, 0]);
|
|
39
|
+
|
|
40
|
+
const initialDevices = await driver.startAutoDetection();
|
|
41
|
+
expect(initialDevices.length).toBe(1);
|
|
42
|
+
expect(initialDevices[0].state).toBe(SiDeviceState.Opened);
|
|
43
|
+
|
|
44
|
+
simulateConnect(siDeviceDriverData);
|
|
45
|
+
await waitForDriver();
|
|
46
|
+
expect([numAddCalled, numRemoveCalled]).toEqual([1, 0]);
|
|
47
|
+
|
|
48
|
+
simulateDisconnect(siDeviceDriverData);
|
|
49
|
+
await waitForDriver();
|
|
50
|
+
expect([numAddCalled, numRemoveCalled]).toEqual([1, 1]);
|
|
51
|
+
|
|
52
|
+
simulateConnect(nonSiDeviceDriverData);
|
|
53
|
+
await waitForDriver();
|
|
54
|
+
expect([numAddCalled, numRemoveCalled]).toEqual([1, 1]);
|
|
55
|
+
|
|
56
|
+
simulateDisconnect(nonSiDeviceDriverData);
|
|
57
|
+
await waitForDriver();
|
|
58
|
+
expect([numAddCalled, numRemoveCalled]).toEqual([1, 1]);
|
|
59
|
+
|
|
60
|
+
simulateConnect(siDeviceDriverData);
|
|
61
|
+
await waitForDriver();
|
|
62
|
+
expect([numAddCalled, numRemoveCalled]).toEqual([2, 1]);
|
|
63
|
+
|
|
64
|
+
simulateDisconnect(siDeviceDriverData);
|
|
65
|
+
await waitForDriver();
|
|
66
|
+
expect([numAddCalled, numRemoveCalled]).toEqual([2, 2]);
|
|
67
|
+
|
|
68
|
+
await driver.stopAutoDetection();
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
return testFunction;
|
|
72
|
+
};
|