Skip to content

Fix upload and serial #661

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 7, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
handle serial connect in the BE
  • Loading branch information
fstasi authored and Alberto Iannaccone committed Dec 7, 2021
commit ba95e2796db7391a69db14f918ba5ac9f02797b6
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ export class BurnBootloader extends SketchContribution {
}

async burnBootloader(): Promise<void> {
await this.serialConnection.disconnect();
try {
const { boardsConfig } = this.boardsServiceClientImpl;
const port = boardsConfig.selectedPort;
Expand Down Expand Up @@ -87,9 +86,7 @@ export class BurnBootloader extends SketchContribution {
}
this.messageService.error(errorMessage);
} finally {
if (this.serialConnection.widgetsAttached()) {
await this.serialConnection.connect();
}
await this.serialConnection.reconnectAfterUpload();
}
}
}
Expand Down
24 changes: 2 additions & 22 deletions arduino-ide-extension/src/browser/contributions/upload-sketch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ export class UploadSketch extends SketchContribution {
if (!sketch) {
return;
}
await this.serialConnection.disconnect();

try {
const { boardsConfig } = this.boardsServiceClientImpl;
const [fqbn, { selectedProgrammer }, verify, verbose, sourceOverride] =
Expand Down Expand Up @@ -288,27 +288,7 @@ export class UploadSketch extends SketchContribution {
this.uploadInProgress = false;
this.onDidChangeEmitter.fire();

if (
this.serialConnection.widgetsAttached() &&
this.serialConnection.serialConfig
) {
const { board, port } = this.serialConnection.serialConfig;
try {
await this.boardsServiceClientImpl.waitUntilAvailable(
Object.assign(board, { port }),
10_000
);
await this.serialConnection.connect();
} catch (waitError) {
this.messageService.error(
nls.localize(
'arduino/sketch/couldNotConnectToSerial',
'Could not reconnect to serial port. {0}',
waitError.toString()
)
);
}
}
setTimeout(() => this.serialConnection.reconnectAfterUpload(), 5000);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import { SerialConfig } from '../../../common/protocol/serial-service';
import { ArduinoSelect } from '../../widgets/arduino-select';
import { SerialModel } from '../serial-model';
import { Serial, SerialConnectionManager } from '../serial-connection-manager';
import { SerialConnectionManager } from '../serial-connection-manager';
import { SerialMonitorSendInput } from './serial-monitor-send-input';
import { SerialMonitorOutput } from './serial-monitor-send-output';
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
Expand Down Expand Up @@ -57,9 +57,7 @@ export class MonitorWidget extends ReactWidget {
this.scrollOptions = undefined;
this.toDispose.push(this.clearOutputEmitter);
this.toDispose.push(
Disposable.create(() =>
this.serialConnection.closeSerial(Serial.Type.Monitor)
)
Disposable.create(() => this.serialConnection.closeSerial())
);
}

Expand All @@ -83,7 +81,7 @@ export class MonitorWidget extends ReactWidget {

protected onAfterAttach(msg: Message): void {
super.onAfterAttach(msg);
this.serialConnection.openSerial(Serial.Type.Monitor);
this.serialConnection.openSerial();
}

onCloseRequest(msg: Message): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { ArduinoMenus } from '../../menu/arduino-menus';
import { Contribution } from '../../contributions/contribution';
import { Endpoint, FrontendApplication } from '@theia/core/lib/browser';
import { ipcRenderer } from '@theia/core/shared/electron';
import { SerialConfig, Status } from '../../../common/protocol';
import { Serial, SerialConnectionManager } from '../serial-connection-manager';
import { SerialConfig } from '../../../common/protocol';
import { SerialConnectionManager } from '../serial-connection-manager';
import { SerialPlotter } from './protocol';
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
const queryString = require('query-string');
Expand Down Expand Up @@ -51,10 +51,8 @@ export class PlotterFrontendContribution extends Contribution {
ipcRenderer.on('CLOSE_CHILD_WINDOW', async () => {
if (!!this.window) {
this.window = null;
await this.serialConnection.closeSerial(Serial.Type.Plotter);
}
});

return super.onStart(app);
}

Expand All @@ -77,12 +75,10 @@ export class PlotterFrontendContribution extends Contribution {
this.window.focus();
return;
}
const status = await this.serialConnection.openSerial(Serial.Type.Plotter);
const wsPort = this.serialConnection.getWsPort();
if (Status.isOK(status) && wsPort) {
if (wsPort) {
this.open(wsPort);
} else {
this.serialConnection.closeSerial(Serial.Type.Plotter);
this.messageService.error(`Couldn't open serial plotter`);
}
}
Expand Down
190 changes: 69 additions & 121 deletions arduino-ide-extension/src/browser/serial/serial-connection-manager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { injectable, inject } from 'inversify';
import { deepClone } from '@theia/core/lib/common/objects';
import { Emitter, Event } from '@theia/core/lib/common/event';
import { MessageService } from '@theia/core/lib/common/message-service';
import {
Expand All @@ -23,7 +22,6 @@ import { nls } from '@theia/core/lib/common/nls';

@injectable()
export class SerialConnectionManager {
protected _state: Serial.State = [];
protected config: Partial<SerialConfig> = {
board: undefined,
port: undefined,
Expand Down Expand Up @@ -61,7 +59,9 @@ export class SerialConnectionManager {
protected readonly boardsServiceProvider: BoardsServiceProvider,
@inject(MessageService) protected messageService: MessageService,
@inject(ThemeService) protected readonly themeService: ThemeService,
@inject(CoreService) protected readonly core: CoreService
@inject(CoreService) protected readonly core: CoreService,
@inject(BoardsServiceProvider)
protected readonly boardsServiceClientImpl: BoardsServiceProvider
) {
this.serialServiceClient.onWebSocketChanged(
this.handleWebSocketChanged.bind(this)
Expand Down Expand Up @@ -129,17 +129,16 @@ export class SerialConnectionManager {
this.config = { ...this.config, [key]: newConfig[key] };
}
});
if (
configHasChanged &&
this.widgetsAttached() &&
!(await this.core.isUploading())
) {

if (configHasChanged) {
this.serialService.updateWsConfigParam({
currentBaudrate: this.config.baudRate,
serialPort: this.config.port?.address,
});
await this.disconnect();
await this.connect();

if (isSerialConfig(this.config)) {
this.serialService.setSerialConfig(this.config);
}
}
}

Expand All @@ -159,61 +158,6 @@ export class SerialConnectionManager {
this.wsPort = wsPort;
}

/**
* When the serial is open and the frontend is connected to the serial, we create the websocket here
*/
protected createWsConnection(): boolean {
if (this.wsPort) {
try {
this.webSocket = new WebSocket(`ws://localhost:${this.wsPort}`);
this.webSocket.onmessage = (res) => {
const messages = JSON.parse(res.data);
this.onReadEmitter.fire({ messages });
};
return true;
} catch {
return false;
}
}
return false;
}

/**
* Sets the types of connections needed by the client.
*
* @param newState The array containing the list of desired connections.
* If the previuos state was empty and 'newState' is not, it tries to reconnect to the serial service
* If the provios state was NOT empty and now it is, it disconnects to the serial service
* @returns The status of the operation
*/
protected async setState(newState: Serial.State): Promise<Status> {
const oldState = deepClone(this._state);
let status = Status.OK;

if (this.widgetsAttached(oldState) && !this.widgetsAttached(newState)) {
status = await this.disconnect();
} else if (
!this.widgetsAttached(oldState) &&
this.widgetsAttached(newState)
) {
if (await this.core.isUploading()) {
this.messageService.error(`Cannot open serial port when uploading`);
return Status.NOT_CONNECTED;
}
status = await this.connect();
}
this._state = newState;
return status;
}

protected get state(): Serial.State {
return this._state;
}

widgetsAttached(state?: Serial.State): boolean {
return (state ? state : this._state).length > 0;
}

get serialConfig(): SerialConfig | undefined {
return isSerialConfig(this.config)
? (this.config as SerialConfig)
Expand All @@ -224,52 +168,36 @@ export class SerialConnectionManager {
return await this.serialService.isSerialPortOpen();
}

/**
* Called when a client opens the serial from the GUI
*
* @param type could be either 'Monitor' or 'Plotter'. If it's 'Monitor' we also connect to the websocket and
* listen to the message events
* @returns the status of the operation
*/
async openSerial(type: Serial.Type): Promise<Status> {
openSerial(): void {
if (!isSerialConfig(this.config)) {
this.messageService.error(
`Please select a board and a port to open the serial connection.`
);
return Status.NOT_CONNECTED;
return;
}

if (!this.webSocket && this.wsPort) {
try {
this.webSocket = new WebSocket(`ws://localhost:${this.wsPort}`);
this.webSocket.onmessage = (res) => {
const messages = JSON.parse(res.data);
this.onReadEmitter.fire({ messages });
};
} catch {
this.messageService.error(`Unable to connect to websocket`);
}
}
if (this.state.includes(type)) return Status.OK;
const newState = deepClone(this.state);
newState.push(type);
const status = await this.setState(newState);
if (Status.isOK(status) && type === Serial.Type.Monitor)
this.createWsConnection();
return status;
}

/**
* Called when a client closes the serial from the GUI
*
* @param type could be either 'Monitor' or 'Plotter'. If it's 'Monitor' we close the websocket connection
* @returns the status of the operation
*/
async closeSerial(type: Serial.Type): Promise<Status> {
const index = this.state.indexOf(type);
let status = Status.OK;
if (index >= 0) {
const newState = deepClone(this.state);
newState.splice(index, 1);
status = await this.setState(newState);
if (
Status.isOK(status) &&
type === Serial.Type.Monitor &&
this.webSocket
) {
closeSerial(): void {
if (this.webSocket) {
try {
this.webSocket.close();
this.webSocket = undefined;
} catch {
this.messageService.error(`Unable to close websocket`);
}
}
return status;
}

/**
Expand Down Expand Up @@ -330,7 +258,7 @@ export class SerialConnectionManager {
}
}

if (this.widgetsAttached()) {
if ((await this.serialService.clientsAttached()) > 0) {
if (this.serialErrors.length >= 10) {
this.messageService.warn(
nls.localize(
Expand Down Expand Up @@ -362,36 +290,56 @@ export class SerialConnectionManager {
)
);
this.reconnectTimeout = window.setTimeout(
() => this.connect(),
() => this.reconnectAfterUpload(),
timeout
);
}
}
}

async connect(): Promise<Status> {
if (await this.serialService.isSerialPortOpen())
return Status.ALREADY_CONNECTED;
if (!isSerialConfig(this.config)) return Status.NOT_CONNECTED;

console.info(
`>>> Creating serial connection for ${Board.toString(
this.config.board
)} on port ${Port.toString(this.config.port)}...`
);
const connectStatus = await this.serialService.connect(this.config);
if (Status.isOK(connectStatus)) {
console.info(
`<<< Serial connection created for ${Board.toString(this.config.board, {
useFqbn: false,
})} on port ${Port.toString(this.config.port)}.`
// async connect(): Promise<Status> {
// if (await this.serialService.isSerialPortOpen())
// return Status.ALREADY_CONNECTED;
// if (!isSerialConfig(this.config)) return Status.NOT_CONNECTED;

// console.info(
// `>>> Creating serial connection for ${Board.toString(
// this.config.board
// )} on port ${Port.toString(this.config.port)}...`
// );
// const connectStatus = await this.serialService.connect(this.config);
// if (Status.isOK(connectStatus)) {
// console.info(
// `<<< Serial connection created for ${Board.toString(this.config.board, {
// useFqbn: false,
// })} on port ${Port.toString(this.config.port)}.`
// );
// }

// return Status.isOK(connectStatus);
// }

async reconnectAfterUpload(): Promise<void> {
try {
if (isSerialConfig(this.config)) {
await this.boardsServiceClientImpl.waitUntilAvailable(
Object.assign(this.config.board, { port: this.config.port }),
10_000
);
this.serialService.connectSerialIfRequired();
}
} catch (waitError) {
this.messageService.error(
nls.localize(
'arduino/sketch/couldNotConnectToSerial',
'Could not reconnect to serial port. {0}',
waitError.toString()
)
);
}

return Status.isOK(connectStatus);
}

async disconnect(): Promise<Status> {
async disconnectSerialPort(): Promise<Status> {
if (!(await this.serialService.isSerialPortOpen())) {
return Status.OK;
}
Expand Down
Loading