mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-27 05:06:42 +00:00
* 1032 failing upload flag for monitor mgr * move upload failure fix logic to frontend * misc corrections * avoid starting monitor when upload is in progress * avoid starting monitor when upload is in progress * prevent monitor side effects on upload (WIP) * send upload req after notifying mgr * dispose instead of pause on upld (code not final) * Revert "dispose instead of pause on upld (code not final)" This reverts commit 2d5dff2a2d85754467470b5973b7ecac8017aa58. * force wait before upload (test) * always start queued services after uplaod finishes * test cli with monitor close delay * clean up unnecessary await(s) * remove unused dependency * revert CLI to 0.23 * use master cli for testing, await in upload finish * remove upload port from pending monitor requests * fix startQueuedServices * refinements queued monitors * clean up monitor mgr state * fix typo from prev cleanup * avoid dupl queued monitor services * variable name changes * reference latest cli commit in package.json Co-authored-by: Alberto Iannaccone <a.iannaccone@arduino.cc>
This commit is contained in:
parent
84109e416a
commit
a54d7c8f45
@ -156,7 +156,11 @@
|
|||||||
],
|
],
|
||||||
"arduino": {
|
"arduino": {
|
||||||
"cli": {
|
"cli": {
|
||||||
"version": "0.23.0"
|
"version": {
|
||||||
|
"owner": "arduino",
|
||||||
|
"repo": "arduino-cli",
|
||||||
|
"commitish": "c1b10f562f1e1a112e215a69b84e2f2b69e3af2d"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"fwuploader": {
|
"fwuploader": {
|
||||||
"version": "2.2.0"
|
"version": "2.2.0"
|
||||||
|
@ -90,7 +90,7 @@ export class ArduinoFirmwareUploaderImpl implements ArduinoFirmwareUploader {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
this.monitorManager.notifyUploadFinished(board, port);
|
await this.monitorManager.notifyUploadFinished(board, port);
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
|
|||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
async upload(options: CoreService.Upload.Options): Promise<void> {
|
upload(options: CoreService.Upload.Options): Promise<void> {
|
||||||
return this.doUpload(
|
return this.doUpload(
|
||||||
options,
|
options,
|
||||||
() => new UploadRequest(),
|
() => new UploadRequest(),
|
||||||
|
@ -40,11 +40,14 @@ export class MonitorManagerProxyImpl implements MonitorManagerProxy {
|
|||||||
if (settings) {
|
if (settings) {
|
||||||
await this.changeMonitorSettings(board, port, settings);
|
await this.changeMonitorSettings(board, port, settings);
|
||||||
}
|
}
|
||||||
const status = await this.manager.startMonitor(board, port);
|
|
||||||
if (status === Status.ALREADY_CONNECTED || status === Status.OK) {
|
const connectToClient = (status: Status) => {
|
||||||
// Monitor started correctly, connect it with the frontend
|
if (status === Status.ALREADY_CONNECTED || status === Status.OK) {
|
||||||
this.client.connect(this.manager.getWebsocketAddressPort(board, port));
|
// Monitor started correctly, connect it with the frontend
|
||||||
}
|
this.client.connect(this.manager.getWebsocketAddressPort(board, port));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return this.manager.startMonitor(board, port, connectToClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ILogger } from '@theia/core';
|
import { ILogger } from '@theia/core';
|
||||||
import { inject, injectable, named } from '@theia/core/shared/inversify';
|
import { inject, injectable, named } from '@theia/core/shared/inversify';
|
||||||
import { Board, Port, Status } from '../common/protocol';
|
import { Board, BoardsService, Port, Status } from '../common/protocol';
|
||||||
import { CoreClientAware } from './core-client-provider';
|
import { CoreClientAware } from './core-client-provider';
|
||||||
import { MonitorService } from './monitor-service';
|
import { MonitorService } from './monitor-service';
|
||||||
import { MonitorServiceFactory } from './monitor-service-factory';
|
import { MonitorServiceFactory } from './monitor-service-factory';
|
||||||
@ -11,16 +11,34 @@ import {
|
|||||||
|
|
||||||
type MonitorID = string;
|
type MonitorID = string;
|
||||||
|
|
||||||
|
type UploadState = 'uploadInProgress' | 'pausedForUpload' | 'disposedForUpload';
|
||||||
|
type MonitorIDsByUploadState = Record<UploadState, MonitorID[]>;
|
||||||
|
|
||||||
export const MonitorManagerName = 'monitor-manager';
|
export const MonitorManagerName = 'monitor-manager';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class MonitorManager extends CoreClientAware {
|
export class MonitorManager extends CoreClientAware {
|
||||||
|
@inject(BoardsService)
|
||||||
|
protected boardsService: BoardsService;
|
||||||
|
|
||||||
// Map of monitor services that manage the running pluggable monitors.
|
// Map of monitor services that manage the running pluggable monitors.
|
||||||
// Each service handles the lifetime of one, and only one, monitor.
|
// Each service handles the lifetime of one, and only one, monitor.
|
||||||
// If either the board or port managed changes, a new service must
|
// If either the board or port managed changes, a new service must
|
||||||
// be started.
|
// be started.
|
||||||
private monitorServices = new Map<MonitorID, MonitorService>();
|
private monitorServices = new Map<MonitorID, MonitorService>();
|
||||||
|
|
||||||
|
private monitorIDsByUploadState: MonitorIDsByUploadState = {
|
||||||
|
uploadInProgress: [],
|
||||||
|
pausedForUpload: [],
|
||||||
|
disposedForUpload: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
private monitorServiceStartQueue: {
|
||||||
|
monitorID: string;
|
||||||
|
serviceStartParams: [Board, Port];
|
||||||
|
connectToClient: (status: Status) => void;
|
||||||
|
}[] = [];
|
||||||
|
|
||||||
@inject(MonitorServiceFactory)
|
@inject(MonitorServiceFactory)
|
||||||
private monitorServiceFactory: MonitorServiceFactory;
|
private monitorServiceFactory: MonitorServiceFactory;
|
||||||
|
|
||||||
@ -48,6 +66,33 @@ export class MonitorManager extends CoreClientAware {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private uploadIsInProgress(): boolean {
|
||||||
|
return this.monitorIDsByUploadState.uploadInProgress.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private addToMonitorIDsByUploadState(
|
||||||
|
state: UploadState,
|
||||||
|
monitorID: string
|
||||||
|
): void {
|
||||||
|
this.monitorIDsByUploadState[state].push(monitorID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private removeFromMonitorIDsByUploadState(
|
||||||
|
state: UploadState,
|
||||||
|
monitorID: string
|
||||||
|
): void {
|
||||||
|
this.monitorIDsByUploadState[state] = this.monitorIDsByUploadState[
|
||||||
|
state
|
||||||
|
].filter((id) => id !== monitorID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private monitorIDIsInUploadState(
|
||||||
|
state: UploadState,
|
||||||
|
monitorID: string
|
||||||
|
): boolean {
|
||||||
|
return this.monitorIDsByUploadState[state].includes(monitorID);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start a pluggable monitor that receives and sends messages
|
* Start a pluggable monitor that receives and sends messages
|
||||||
* to the specified board and port combination.
|
* to the specified board and port combination.
|
||||||
@ -56,13 +101,34 @@ export class MonitorManager extends CoreClientAware {
|
|||||||
* @returns a Status object to know if the process has been
|
* @returns a Status object to know if the process has been
|
||||||
* started or if there have been errors.
|
* started or if there have been errors.
|
||||||
*/
|
*/
|
||||||
async startMonitor(board: Board, port: Port): Promise<Status> {
|
async startMonitor(
|
||||||
|
board: Board,
|
||||||
|
port: Port,
|
||||||
|
connectToClient: (status: Status) => void
|
||||||
|
): Promise<void> {
|
||||||
const monitorID = this.monitorID(board, port);
|
const monitorID = this.monitorID(board, port);
|
||||||
|
|
||||||
let monitor = this.monitorServices.get(monitorID);
|
let monitor = this.monitorServices.get(monitorID);
|
||||||
if (!monitor) {
|
if (!monitor) {
|
||||||
monitor = this.createMonitor(board, port);
|
monitor = this.createMonitor(board, port);
|
||||||
}
|
}
|
||||||
return await monitor.start();
|
|
||||||
|
if (this.uploadIsInProgress()) {
|
||||||
|
this.monitorServiceStartQueue = this.monitorServiceStartQueue.filter(
|
||||||
|
(request) => request.monitorID !== monitorID
|
||||||
|
);
|
||||||
|
|
||||||
|
this.monitorServiceStartQueue.push({
|
||||||
|
monitorID,
|
||||||
|
serviceStartParams: [board, port],
|
||||||
|
connectToClient,
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await monitor.start();
|
||||||
|
connectToClient(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -111,14 +177,18 @@ export class MonitorManager extends CoreClientAware {
|
|||||||
// to retrieve if we don't have this information.
|
// to retrieve if we don't have this information.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const monitorID = this.monitorID(board, port);
|
const monitorID = this.monitorID(board, port);
|
||||||
|
this.addToMonitorIDsByUploadState('uploadInProgress', monitorID);
|
||||||
|
|
||||||
const monitor = this.monitorServices.get(monitorID);
|
const monitor = this.monitorServices.get(monitorID);
|
||||||
if (!monitor) {
|
if (!monitor) {
|
||||||
// There's no monitor running there, bail
|
// There's no monitor running there, bail
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
monitor.setUploadInProgress(true);
|
|
||||||
return await monitor.pause();
|
this.addToMonitorIDsByUploadState('pausedForUpload', monitorID);
|
||||||
|
return monitor.pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,19 +200,69 @@ export class MonitorManager extends CoreClientAware {
|
|||||||
* started or if there have been errors.
|
* started or if there have been errors.
|
||||||
*/
|
*/
|
||||||
async notifyUploadFinished(board?: Board, port?: Port): Promise<Status> {
|
async notifyUploadFinished(board?: Board, port?: Port): Promise<Status> {
|
||||||
if (!board || !port) {
|
let status: Status = Status.NOT_CONNECTED;
|
||||||
// We have no way of knowing which monitor
|
let portDidChangeOnUpload = false;
|
||||||
// to retrieve if we don't have this information.
|
|
||||||
return Status.NOT_CONNECTED;
|
// We have no way of knowing which monitor
|
||||||
|
// to retrieve if we don't have this information.
|
||||||
|
if (board && port) {
|
||||||
|
const monitorID = this.monitorID(board, port);
|
||||||
|
this.removeFromMonitorIDsByUploadState('uploadInProgress', monitorID);
|
||||||
|
|
||||||
|
const monitor = this.monitorServices.get(monitorID);
|
||||||
|
if (monitor) {
|
||||||
|
status = await monitor.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// this monitorID will only be present in "disposedForUpload"
|
||||||
|
// if the upload changed the board port
|
||||||
|
portDidChangeOnUpload = this.monitorIDIsInUploadState(
|
||||||
|
'disposedForUpload',
|
||||||
|
monitorID
|
||||||
|
);
|
||||||
|
if (portDidChangeOnUpload) {
|
||||||
|
this.removeFromMonitorIDsByUploadState('disposedForUpload', monitorID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// in case a service was paused but not disposed
|
||||||
|
this.removeFromMonitorIDsByUploadState('pausedForUpload', monitorID);
|
||||||
}
|
}
|
||||||
const monitorID = this.monitorID(board, port);
|
|
||||||
const monitor = this.monitorServices.get(monitorID);
|
await this.startQueuedServices(portDidChangeOnUpload);
|
||||||
if (!monitor) {
|
return status;
|
||||||
// There's no monitor running there, bail
|
}
|
||||||
return Status.NOT_CONNECTED;
|
|
||||||
|
async startQueuedServices(portDidChangeOnUpload: boolean): Promise<void> {
|
||||||
|
// if the port changed during upload with the monitor open, "startMonitorPendingRequests"
|
||||||
|
// will include a request for our "upload port', most likely at index 0.
|
||||||
|
// We remove it, as this port was to be used exclusively for the upload
|
||||||
|
const queued = portDidChangeOnUpload
|
||||||
|
? this.monitorServiceStartQueue.slice(1)
|
||||||
|
: this.monitorServiceStartQueue;
|
||||||
|
this.monitorServiceStartQueue = [];
|
||||||
|
|
||||||
|
for (const {
|
||||||
|
monitorID,
|
||||||
|
serviceStartParams: [_, port],
|
||||||
|
connectToClient,
|
||||||
|
} of queued) {
|
||||||
|
const boardsState = await this.boardsService.getState();
|
||||||
|
const boardIsStillOnPort = Object.keys(boardsState)
|
||||||
|
.map((connection: string) => {
|
||||||
|
const portAddress = connection.split('|')[0];
|
||||||
|
return portAddress;
|
||||||
|
})
|
||||||
|
.some((portAddress: string) => port.address === portAddress);
|
||||||
|
|
||||||
|
if (boardIsStillOnPort) {
|
||||||
|
const monitorService = this.monitorServices.get(monitorID);
|
||||||
|
|
||||||
|
if (monitorService) {
|
||||||
|
const result = await monitorService.start();
|
||||||
|
connectToClient(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
monitor.setUploadInProgress(false);
|
|
||||||
return await monitor.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -202,6 +322,18 @@ export class MonitorManager extends CoreClientAware {
|
|||||||
this.monitorServices.set(monitorID, monitor);
|
this.monitorServices.set(monitorID, monitor);
|
||||||
monitor.onDispose(
|
monitor.onDispose(
|
||||||
(() => {
|
(() => {
|
||||||
|
// if a service is disposed during upload and
|
||||||
|
// we paused it beforehand we know it was disposed
|
||||||
|
// of because the upload changed the board port
|
||||||
|
if (
|
||||||
|
this.uploadIsInProgress() &&
|
||||||
|
this.monitorIDIsInUploadState('pausedForUpload', monitorID)
|
||||||
|
) {
|
||||||
|
this.removeFromMonitorIDsByUploadState('pausedForUpload', monitorID);
|
||||||
|
|
||||||
|
this.addToMonitorIDsByUploadState('disposedForUpload', monitorID);
|
||||||
|
}
|
||||||
|
|
||||||
this.monitorServices.delete(monitorID);
|
this.monitorServices.delete(monitorID);
|
||||||
}).bind(this)
|
}).bind(this)
|
||||||
);
|
);
|
||||||
|
@ -60,7 +60,6 @@ export class MonitorService extends CoreClientAware implements Disposable {
|
|||||||
protected readonly onDisposeEmitter = new Emitter<void>();
|
protected readonly onDisposeEmitter = new Emitter<void>();
|
||||||
readonly onDispose = this.onDisposeEmitter.event;
|
readonly onDispose = this.onDisposeEmitter.event;
|
||||||
|
|
||||||
protected uploadInProgress = false;
|
|
||||||
protected _initialized = new Deferred<void>();
|
protected _initialized = new Deferred<void>();
|
||||||
protected creating: Deferred<Status>;
|
protected creating: Deferred<Status>;
|
||||||
|
|
||||||
@ -114,10 +113,6 @@ export class MonitorService extends CoreClientAware implements Disposable {
|
|||||||
return this._initialized.promise;
|
return this._initialized.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
setUploadInProgress(status: boolean): void {
|
|
||||||
this.uploadInProgress = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
getWebsocketAddressPort(): number {
|
getWebsocketAddressPort(): number {
|
||||||
return this.webSocketProvider.getAddress().port;
|
return this.webSocketProvider.getAddress().port;
|
||||||
}
|
}
|
||||||
@ -161,15 +156,6 @@ export class MonitorService extends CoreClientAware implements Disposable {
|
|||||||
return this.creating.promise;
|
return this.creating.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.uploadInProgress) {
|
|
||||||
this.updateClientsSettings({
|
|
||||||
monitorUISettings: { connected: false, serialPort: this.port.address },
|
|
||||||
});
|
|
||||||
|
|
||||||
this.creating.resolve(Status.UPLOAD_IN_PROGRESS);
|
|
||||||
return this.creating.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.info('starting monitor');
|
this.logger.info('starting monitor');
|
||||||
|
|
||||||
// get default monitor settings from the CLI
|
// get default monitor settings from the CLI
|
||||||
|
Loading…
x
Reference in New Issue
Block a user