refactor(usb-deployer): serial monitor + flashBuffer + lifecycle use pump

- startSerialMonitor/setBaudRate stop+restart pump, subscribe rxBuffer.onData
- flashBuffer stops pump, reuses/reopens port, starts pump, drains before sync
- stopReaderWriter now just releases locks; cleanup stops pump first
- remove dead keepReading field
This commit is contained in:
a2nr 2026-07-19 19:09:10 +07:00
parent f7c088013a
commit 056b1a47b6
1 changed files with 30 additions and 44 deletions

View File

@ -135,7 +135,6 @@ export class USBHardwareDeployer implements HardwareDeployer {
private port: SerialPort | null = null; private port: SerialPort | null = null;
private reader: ReadableStreamDefaultReader<Uint8Array> | null = null; private reader: ReadableStreamDefaultReader<Uint8Array> | null = null;
private writer: WritableStreamDefaultWriter<Uint8Array> | null = null; private writer: WritableStreamDefaultWriter<Uint8Array> | null = null;
private keepReading = false;
private _onDisconnected: (() => void) | null = null; private _onDisconnected: (() => void) | null = null;
private _deviceName: string | null = null; private _deviceName: string | null = null;
private currentBaudRate: number | null = null; private currentBaudRate: number | null = null;
@ -263,11 +262,11 @@ export class USBHardwareDeployer implements HardwareDeployer {
async startSerialMonitor(onData: (text: string) => void): Promise<void> { async startSerialMonitor(onData: (text: string) => void): Promise<void> {
if (!this.port) throw new Error('Belum terhubung ke perangkat'); if (!this.port) throw new Error('Belum terhubung ke perangkat');
/* Release flash-mode reader/writer, then close port */ /* Stop pump + release reader/writer, reopen at monitor baud. */
this.rxBuffer.onData = null;
await this.stopPump();
await this.stopReaderWriter(); await this.stopReaderWriter();
await this.port.close(); await this.port.close();
/* Reopen at serial-monitor baud rate (9600 default) */
await this.port.open({ await this.port.open({
baudRate: SERIAL_MONITOR_BAUD, baudRate: SERIAL_MONITOR_BAUD,
dataBits: 8, dataBits: 8,
@ -275,43 +274,24 @@ export class USBHardwareDeployer implements HardwareDeployer {
parity: 'none', parity: 'none',
flowControl: 'none' flowControl: 'none'
}); });
console.log('[USB-SERIAL] port opened at', SERIAL_MONITOR_BAUD);
this.currentBaudRate = SERIAL_MONITOR_BAUD; this.currentBaudRate = SERIAL_MONITOR_BAUD;
console.log('[USB-SERIAL] port opened at', SERIAL_MONITOR_BAUD);
this.writer = this.port.writable!.getWriter(); this.writer = this.port.writable!.getWriter();
this.reader = this.port.readable!.getReader(); this.reader = this.port.readable!.getReader();
this.keepReading = true; this.rxBuffer.clear();
const decoder = new TextDecoder(); const decoder = new TextDecoder();
this.readLoop(onData, decoder).catch((e) => this.rxBuffer.onData = (chunk) => {
console.error('[USB-SERIAL] read loop error:', e) const text = decoder.decode(chunk, { stream: true });
); if (text) onData(text);
} };
this.startPump();
private async readLoop(
onData: (text: string) => void,
decoder: TextDecoder
): Promise<void> {
while (this.keepReading && this.reader) {
try {
const { value, done } = await this.reader.read();
if (done) break;
if (value && value.length > 0) {
const text = decoder.decode(value, { stream: true });
onData(text);
}
} catch (e) {
if (this.keepReading) {
console.error('[USB-SERIAL] read error:', e);
}
break;
}
}
} }
stopSerialMonitor(): void { stopSerialMonitor(): void {
console.log('[USB-SERIAL] stopSerialMonitor'); console.log('[USB-SERIAL] stopSerialMonitor');
this.keepReading = false; this.rxBuffer.onData = null;
} }
async sendSerialInput(text: string): Promise<void> { async sendSerialInput(text: string): Promise<void> {
@ -322,7 +302,9 @@ export class USBHardwareDeployer implements HardwareDeployer {
async setBaudRate(baud: number): Promise<void> { async setBaudRate(baud: number): Promise<void> {
if (!this.port) throw new Error('Belum terhubung ke perangkat'); if (!this.port) throw new Error('Belum terhubung ke perangkat');
const prevOnData = this.rxBuffer.onData;
/* Web Serial cannot change baud without close+reopen */ /* Web Serial cannot change baud without close+reopen */
await this.stopPump();
await this.stopReaderWriter(); await this.stopReaderWriter();
await this.port.close(); await this.port.close();
await this.port.open({ await this.port.open({
@ -334,6 +316,9 @@ export class USBHardwareDeployer implements HardwareDeployer {
}); });
this.writer = this.port.writable!.getWriter(); this.writer = this.port.writable!.getWriter();
this.reader = this.port.readable!.getReader(); this.reader = this.port.readable!.getReader();
this.rxBuffer.clear();
this.rxBuffer.onData = prevOnData;
this.startPump();
console.log('[USB-SERIAL] baud rate changed to', baud); console.log('[USB-SERIAL] baud rate changed to', baud);
this.currentBaudRate = baud; this.currentBaudRate = baud;
} }
@ -352,19 +337,11 @@ export class USBHardwareDeployer implements HardwareDeployer {
/* ── Private helpers ──────────────────────────────────────────── */ /* ── Private helpers ──────────────────────────────────────────── */
private async stopReaderWriter(): Promise<void> { private async stopReaderWriter(): Promise<void> {
this.keepReading = false; /* Pump owns reader.cancel via stopPump; here we just release locks. */
try { try {
await this.reader?.cancel(); this.reader?.releaseLock();
/* Wait for reader lock to release before close */
if (this.reader) {
try {
await this.reader.closed.catch(() => {});
} catch { } catch {
/* ignore */ /* reader may already be released/cancelled */
}
}
} catch {
/* ignore */
} }
this.reader = null; this.reader = null;
try { try {
@ -423,8 +400,10 @@ export class USBHardwareDeployer implements HardwareDeployer {
); );
this._boundDisconnectHandler = null; this._boundDisconnectHandler = null;
} }
this.keepReading = false; await this.stopPump();
await this.stopReaderWriter(); await this.stopReaderWriter();
this.rxBuffer.clear();
this.rxBuffer.onData = null;
this.currentBaudRate = null; this.currentBaudRate = null;
try { try {
await this.port?.close(); await this.port?.close();
@ -647,7 +626,8 @@ export class USBHardwareDeployer implements HardwareDeployer {
}); });
/* ── Ensure port is at flash baud rate ── */ /* ── Ensure port is at flash baud rate ── */
this.stopSerialMonitor(); this.rxBuffer.onData = null; /* detach serial monitor sink */
await this.stopPump();
await this.stopReaderWriter(); await this.stopReaderWriter();
if (!this.port) { if (!this.port) {
@ -680,10 +660,16 @@ export class USBHardwareDeployer implements HardwareDeployer {
this.writer = this.port.writable!.getWriter(); this.writer = this.port.writable!.getWriter();
this.reader = this.port.readable!.getReader(); this.reader = this.port.readable!.getReader();
this.rxBuffer.clear();
this.startPump();
/* 1. Auto-reset → optiboot */ /* 1. Auto-reset → optiboot */
await this.resetArduino(); await this.resetArduino();
/* Drain stray bytes from prior serial session / boot chatter
before sync (mirrors firmware ESP32 drain_cdc). */
await this.drainStray();
/* 2. Get sync (retry within optiboot ~1s window) */ /* 2. Get sync (retry within optiboot ~1s window) */
onProgress({ onProgress({
state: 'flashing', state: 'flashing',