diff --git a/src/serialmonitor/outputBuffer.ts b/src/serialmonitor/outputBuffer.ts new file mode 100644 index 00000000..c9099e98 --- /dev/null +++ b/src/serialmonitor/outputBuffer.ts @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { performance } from "perf_hooks"; +import * as vscode from "vscode"; + +export class BufferedOutputChannel implements vscode.Disposable { + private _buffer: string[]; + private _timer: NodeJS.Timer; + private _lastFlushTime: number; + + public constructor(private readonly outputCallback: (value: string) => void, private readonly flushIntervalMs: number) { + this._buffer = []; + this._timer = setInterval(() => this.tryFlush(), this.flushIntervalMs); + this._lastFlushTime = Number.NEGATIVE_INFINITY; + } + + public append(value: string) { + this.add(value); + } + + public appendLine(value: string) { + this.add(value + "\n"); + } + + public dispose() { + this.tryFlush(); + clearInterval(this._timer); + } + + private add(value: string) { + this._buffer.push(value); + this.tryFlush(); + } + + private tryFlush() { + const currentTime = performance.now(); + if (this._buffer.length > 0 && currentTime - this._lastFlushTime > this.flushIntervalMs) { + this.outputCallback(this._buffer.join("")); + this._lastFlushTime = currentTime; + this._buffer = []; + } + } +} diff --git a/src/serialmonitor/serialMonitor.ts b/src/serialmonitor/serialMonitor.ts index b220fcd8..6683ce2f 100644 --- a/src/serialmonitor/serialMonitor.ts +++ b/src/serialmonitor/serialMonitor.ts @@ -6,6 +6,7 @@ import ArduinoContext from "../arduinoContext"; import * as constants from "../common/constants"; import { DeviceContext } from "../deviceContext"; import * as Logger from "../logger/logger"; +import { BufferedOutputChannel } from "./outputBuffer"; import { SerialPortCtrl } from "./serialportctrl"; export interface ISerialPortDetail { @@ -49,6 +50,8 @@ export class SerialMonitor implements vscode.Disposable { private _outputChannel: vscode.OutputChannel; + private _bufferedOutputChannel: BufferedOutputChannel; + public initialize() { let defaultBaudRate; if (ArduinoContext.arduinoApp && ArduinoContext.arduinoApp.settings && ArduinoContext.arduinoApp.settings.defaultBaudRate) { @@ -57,6 +60,7 @@ export class SerialMonitor implements vscode.Disposable { defaultBaudRate = SerialMonitor.DEFAULT_BAUD_RATE; } this._outputChannel = vscode.window.createOutputChannel(SerialMonitor.SERIAL_MONITOR); + this._bufferedOutputChannel = new BufferedOutputChannel(this._outputChannel.append, 300); this._currentBaudRate = defaultBaudRate; this._portsStatusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, constants.statusBarPriority.PORT); this._portsStatusBar.command = "arduino.selectSerialPort"; @@ -88,6 +92,8 @@ export class SerialMonitor implements vscode.Disposable { if (this._serialPortCtrl && this._serialPortCtrl.isActive) { return this._serialPortCtrl.stop(); } + this._outputChannel.dispose(); + this._bufferedOutputChannel.dispose(); } public async selectSerialPort(vid: string, pid: string) { @@ -145,7 +151,11 @@ export class SerialMonitor implements vscode.Disposable { return; } } else { - this._serialPortCtrl = new SerialPortCtrl(this._currentPort, this._currentBaudRate, this._outputChannel); + this._serialPortCtrl = new SerialPortCtrl( + this._currentPort, + this._currentBaudRate, + this._bufferedOutputChannel, + this._outputChannel.show); } if (!this._serialPortCtrl.currentPort) { diff --git a/src/serialmonitor/serialportctrl.ts b/src/serialmonitor/serialportctrl.ts index 70e03631..e59cc6c2 100644 --- a/src/serialmonitor/serialportctrl.ts +++ b/src/serialmonitor/serialportctrl.ts @@ -4,8 +4,8 @@ import { ChildProcess, execFileSync, spawn } from "child_process"; import * as os from "os"; import * as path from "path"; -import { OutputChannel } from "vscode"; import { DeviceContext } from "../deviceContext"; +import { BufferedOutputChannel } from "./outputBuffer"; interface ISerialPortDetail { port: string; @@ -63,7 +63,11 @@ export class SerialPortCtrl { private _currentBaudRate: number; private _currentSerialPort = null; - public constructor(port: string, baudRate: number, private _outputChannel: OutputChannel) { + public constructor( + port: string, + baudRate: number, + private _bufferedOutputChannel: BufferedOutputChannel, + private showOutputChannel: (preserveFocus?: boolean) => void) { this._currentBaudRate = baudRate; this._currentPort = port; } @@ -80,8 +84,8 @@ export class SerialPortCtrl { } public open(): Promise { - this._outputChannel.appendLine(`[Starting] Opening the serial port - ${this._currentPort}`); - this._outputChannel.show(); + this._bufferedOutputChannel.appendLine(`[Starting] Opening the serial port - ${this._currentPort}`); + this.showOutputChannel(); if (this._child) { this.stop(); @@ -96,8 +100,10 @@ export class SerialPortCtrl { }); this._child.stdout.on("data", (data) => { - const jsonObj = JSON.parse(data.toString()) - this._outputChannel.append(jsonObj["payload"] + "\n"); + if (this.isActive) { + const jsonObj = JSON.parse(data.toString()) + this._bufferedOutputChannel.append(jsonObj["payload"] + "\n"); + } }); // TODO: add message check to ensure _child spawned without errors resolve(); @@ -155,8 +161,8 @@ export class SerialPortCtrl { } try { this._child.stdin.write('{"cmd": "close"}\n'); - if (this._outputChannel) { - this._outputChannel.appendLine(`[Done] Closed the serial port ${os.EOL}`); + if (this._bufferedOutputChannel) { + this._bufferedOutputChannel.appendLine(`[Done] Closed the serial port ${os.EOL}`); } this._child = null; resolve(true);