Try-fix webserial disconnect not actually disconnecting (#796)
* Try-fix webserial disconnects * Instantiate abort controller on new connection
This commit is contained in:
@@ -5,6 +5,8 @@ export class TransportWebSerial implements Types.Transport {
|
|||||||
private _toDevice: WritableStream<Uint8Array>;
|
private _toDevice: WritableStream<Uint8Array>;
|
||||||
private _fromDevice: ReadableStream<Types.DeviceOutput>;
|
private _fromDevice: ReadableStream<Types.DeviceOutput>;
|
||||||
private connection: SerialPort;
|
private connection: SerialPort;
|
||||||
|
private pipePromise: Promise<void> | null = null;
|
||||||
|
private abortController: AbortController;
|
||||||
|
|
||||||
public static async create(baudRate?: number): Promise<TransportWebSerial> {
|
public static async create(baudRate?: number): Promise<TransportWebSerial> {
|
||||||
const port = await navigator.serial.requestPort();
|
const port = await navigator.serial.requestPort();
|
||||||
@@ -26,8 +28,13 @@ export class TransportWebSerial implements Types.Transport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
|
this.abortController = new AbortController();
|
||||||
|
|
||||||
Utils.toDeviceStream.readable.pipeTo(connection.writable);
|
// Set up the pipe with abort signal for clean cancellation
|
||||||
|
this.pipePromise = Utils.toDeviceStream.readable.pipeTo(
|
||||||
|
connection.writable,
|
||||||
|
{ signal: this.abortController.signal }
|
||||||
|
);
|
||||||
|
|
||||||
this._toDevice = Utils.toDeviceStream.writable;
|
this._toDevice = Utils.toDeviceStream.writable;
|
||||||
this._fromDevice = connection.readable.pipeThrough(
|
this._fromDevice = connection.readable.pipeThrough(
|
||||||
@@ -43,7 +50,54 @@ export class TransportWebSerial implements Types.Transport {
|
|||||||
return this._fromDevice;
|
return this._fromDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect() {
|
/**
|
||||||
return this.connection.close();
|
* Safely disconnects the serial port, following best practices from
|
||||||
|
* https://github.com/WICG/serial/. Cancels any active pipe
|
||||||
|
* operations and only closes the port after streams are unlocked.
|
||||||
|
*/
|
||||||
|
async disconnect() {
|
||||||
|
try {
|
||||||
|
this.abortController.abort();
|
||||||
|
|
||||||
|
if (this.pipePromise) {
|
||||||
|
try {
|
||||||
|
await this.pipePromise;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error && error.name !== 'AbortError') {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel any remaining streams
|
||||||
|
if (this._fromDevice && this._fromDevice.locked) {
|
||||||
|
try {
|
||||||
|
await this._fromDevice.cancel();
|
||||||
|
} catch (error) {
|
||||||
|
// Stream cancellation might fail if already cancelled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.connection.close();
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
// If we can't close cleanly, let the browser handle cleanup
|
||||||
|
console.warn('Could not cleanly disconnect serial port:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reconnects the transport by creating a new AbortController and re-establishing
|
||||||
|
* the pipe connection. Only call this after disconnect() or if the connection failed.
|
||||||
|
*/
|
||||||
|
async reconnect() {
|
||||||
|
// Create a new AbortController for the new connection
|
||||||
|
this.abortController = new AbortController();
|
||||||
|
|
||||||
|
// Re-establish the pipe connection
|
||||||
|
this.pipePromise = Utils.toDeviceStream.readable.pipeTo(
|
||||||
|
this.connection.writable,
|
||||||
|
{ signal: this.abortController.signal }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user