Skip to content

Commit 959e257

Browse files
author
Alberto Iannaccone
committed
fix reset loop
1 parent 16e2423 commit 959e257

File tree

7 files changed

+98
-86
lines changed

7 files changed

+98
-86
lines changed

Diff for: arduino-ide-extension/src/browser/monitor-manager-proxy-client-impl.ts

+55-23
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { CommandRegistry, Emitter, MessageService } from '@theia/core';
1+
import {
2+
CommandRegistry,
3+
Disposable,
4+
Emitter,
5+
MessageService,
6+
} from '@theia/core';
27
import { inject, injectable } from '@theia/core/shared/inversify';
38
import { Board, Port } from '../common/protocol';
49
import {
@@ -11,8 +16,7 @@ import {
1116
MonitorSettings,
1217
} from '../node/monitor-settings/monitor-settings-provider';
1318
import { BoardsConfig } from './boards/boards-config';
14-
import { MonitorViewContribution } from './serial/monitor/monitor-view-contribution';
15-
import { SerialPlotterContribution } from './serial/plotter/plotter-frontend-contribution';
19+
import { BoardsServiceProvider } from './boards/boards-service-provider';
1620

1721
@injectable()
1822
export class MonitorManagerProxyClientImpl
@@ -32,11 +36,15 @@ export class MonitorManagerProxyClientImpl
3236
readonly onMonitorSettingsDidChange =
3337
this.onMonitorSettingsDidChangeEmitter.event;
3438

39+
protected readonly onMonitorShouldResetEmitter = new Emitter();
40+
readonly onMonitorShouldReset = this.onMonitorShouldResetEmitter.event;
41+
3542
// WebSocket used to handle pluggable monitor communication between
3643
// frontend and backend.
3744
private webSocket?: WebSocket;
3845
private wsPort?: number;
3946
private lastConnectedBoard: BoardsConfig.Config;
47+
private onBoardsConfigChanged: Disposable | undefined;
4048

4149
getWebSocketPort(): number | undefined {
4250
return this.wsPort;
@@ -51,7 +59,10 @@ export class MonitorManagerProxyClientImpl
5159
protected server: MonitorManagerProxyFactory,
5260

5361
@inject(CommandRegistry)
54-
protected readonly commandRegistry: CommandRegistry
62+
protected readonly commandRegistry: CommandRegistry,
63+
64+
@inject(BoardsServiceProvider)
65+
protected readonly boardsServiceProvider: BoardsServiceProvider
5566
) {}
5667

5768
/**
@@ -89,6 +100,8 @@ export class MonitorManagerProxyClientImpl
89100
*/
90101
disconnect(): void {
91102
if (!this.webSocket) return;
103+
this.onBoardsConfigChanged?.dispose();
104+
this.onBoardsConfigChanged = undefined;
92105
try {
93106
this.webSocket?.close();
94107
this.webSocket = undefined;
@@ -101,27 +114,46 @@ export class MonitorManagerProxyClientImpl
101114
return !!this.webSocket;
102115
}
103116

104-
async startMonitor(
105-
board: Board,
106-
port: Port,
107-
settings?: PluggableMonitorSettings
108-
): Promise<void> {
109-
await this.server().startMonitor(board, port, settings);
110-
if (
111-
board.fqbn !== this.lastConnectedBoard?.selectedBoard?.fqbn ||
112-
port.id !== this.lastConnectedBoard?.selectedPort?.id
113-
) {
114-
await this.commandRegistry.executeCommand(
115-
MonitorViewContribution.RESET_SERIAL_MONITOR
116-
);
117-
await this.commandRegistry.executeCommand(
118-
SerialPlotterContribution.Commands.RESET.id
119-
);
120-
}
117+
async startMonitor(settings?: PluggableMonitorSettings): Promise<void> {
121118
this.lastConnectedBoard = {
122-
selectedBoard: board,
123-
selectedPort: port,
119+
selectedBoard: this.boardsServiceProvider.boardsConfig.selectedBoard,
120+
selectedPort: this.boardsServiceProvider.boardsConfig.selectedPort,
124121
};
122+
123+
if (!this.onBoardsConfigChanged) {
124+
this.onBoardsConfigChanged =
125+
this.boardsServiceProvider.onBoardsConfigChanged(
126+
async ({ selectedBoard, selectedPort }) => {
127+
if (
128+
typeof selectedBoard === 'undefined' ||
129+
typeof selectedPort === 'undefined'
130+
)
131+
return;
132+
133+
// a board is plugged and it's different from the old connected board
134+
if (
135+
selectedBoard?.fqbn !==
136+
this.lastConnectedBoard?.selectedBoard?.fqbn ||
137+
selectedPort?.id !== this.lastConnectedBoard?.selectedPort?.id
138+
) {
139+
this.onMonitorShouldResetEmitter.fire(null);
140+
this.lastConnectedBoard = {
141+
selectedBoard: selectedBoard,
142+
selectedPort: selectedPort,
143+
};
144+
} else {
145+
// a board is plugged and it's the same as prev, rerun "this.startMonitor" to
146+
// recreate the listener callback
147+
this.startMonitor();
148+
}
149+
}
150+
);
151+
}
152+
153+
const { selectedBoard, selectedPort } =
154+
this.boardsServiceProvider.boardsConfig;
155+
if (!selectedBoard || !selectedBoard.fqbn || !selectedPort) return;
156+
await this.server().startMonitor(selectedBoard, selectedPort, settings);
125157
}
126158

127159
getCurrentSettings(board: Board, port: Port): Promise<MonitorSettings> {

Diff for: arduino-ide-extension/src/browser/serial/monitor/monitor-view-contribution.tsx

+8-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { ArduinoToolbar } from '../../toolbar/arduino-toolbar';
1111
import { ArduinoMenus } from '../../menu/arduino-menus';
1212
import { nls } from '@theia/core/lib/common';
1313
import { MonitorModel } from '../../monitor-model';
14+
import { MonitorManagerProxyClient } from '../../../common/protocol';
1415

1516
export namespace SerialMonitor {
1617
export namespace Commands {
@@ -49,10 +50,13 @@ export class MonitorViewContribution
4950
MonitorWidget.ID + ':toggle-toolbar';
5051
static readonly RESET_SERIAL_MONITOR = MonitorWidget.ID + ':reset';
5152

52-
@inject(MonitorModel)
53-
protected readonly model: MonitorModel;
53+
constructor(
54+
@inject(MonitorModel)
55+
protected readonly model: MonitorModel,
5456

55-
constructor() {
57+
@inject(MonitorManagerProxyClient)
58+
protected readonly monitorManagerProxy: MonitorManagerProxyClient
59+
) {
5660
super({
5761
widgetId: MonitorWidget.ID,
5862
widgetName: MonitorWidget.LABEL,
@@ -62,6 +66,7 @@ export class MonitorViewContribution
6266
toggleCommandId: MonitorViewContribution.TOGGLE_SERIAL_MONITOR,
6367
toggleKeybinding: 'CtrlCmd+Shift+M',
6468
});
69+
this.monitorManagerProxy.onMonitorShouldReset(() => this.reset());
6570
}
6671

6772
registerMenus(menus: MenuModelRegistry): void {

Diff for: arduino-ide-extension/src/browser/serial/monitor/monitor-widget.tsx

+2-23
Original file line numberDiff line numberDiff line change
@@ -61,28 +61,6 @@ export class MonitorWidget extends ReactWidget {
6161
this.toDispose.push(
6262
Disposable.create(() => this.monitorManagerProxy.disconnect())
6363
);
64-
65-
// Start monitor right away if there is already a board/port combination selected
66-
const { selectedBoard, selectedPort } =
67-
this.boardsServiceProvider.boardsConfig;
68-
if (selectedBoard && selectedBoard.fqbn && selectedPort) {
69-
this.monitorManagerProxy.startMonitor(selectedBoard, selectedPort);
70-
}
71-
72-
this.toDispose.push(
73-
this.boardsServiceProvider.onBoardsConfigChanged(
74-
async ({ selectedBoard, selectedPort }) => {
75-
if (selectedBoard && selectedBoard.fqbn && selectedPort) {
76-
await this.monitorManagerProxy.startMonitor(
77-
selectedBoard,
78-
selectedPort
79-
);
80-
81-
this.update();
82-
}
83-
}
84-
)
85-
);
8664
}
8765

8866
protected onBeforeAttach(msg: Message): void {
@@ -92,6 +70,8 @@ export class MonitorWidget extends ReactWidget {
9270
this.monitorManagerProxy.onMonitorSettingsDidChange(
9371
this.onMonitorSettingsDidChange.bind(this)
9472
);
73+
74+
this.monitorManagerProxy.startMonitor();
9575
}
9676

9777
onMonitorSettingsDidChange(settings: MonitorSettings): void {
@@ -207,7 +187,6 @@ export class MonitorWidget extends ReactWidget {
207187
<div className="send">
208188
<SerialMonitorSendInput
209189
boardsServiceProvider={this.boardsServiceProvider}
210-
monitorManagerProxy={this.monitorManagerProxy}
211190
monitorModel={this.monitorModel}
212191
resolveFocus={this.onFocusResolved}
213192
onSend={this.onSend}

Diff for: arduino-ide-extension/src/browser/serial/monitor/serial-monitor-send-input.tsx

-2
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@ import { Key, KeyCode } from '@theia/core/lib/browser/keys';
33
import { Board } from '../../../common/protocol/boards-service';
44
import { isOSX } from '@theia/core/lib/common/os';
55
import { DisposableCollection, nls } from '@theia/core/lib/common';
6-
import { MonitorManagerProxyClient } from '../../../common/protocol';
76
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
87
import { MonitorModel } from '../../monitor-model';
98

109
export namespace SerialMonitorSendInput {
1110
export interface Props {
1211
readonly boardsServiceProvider: BoardsServiceProvider;
13-
readonly monitorManagerProxy: MonitorManagerProxyClient;
1412
readonly monitorModel: MonitorModel;
1513
readonly onSend: (text: string) => void;
1614
readonly resolveFocus: (element: HTMLElement | undefined) => void;

Diff for: arduino-ide-extension/src/browser/serial/plotter/plotter-frontend-contribution.ts

+4-13
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { MonitorManagerProxyClient } from '../../../common/protocol';
1414
import { SerialPlotter } from './protocol';
1515
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
1616
import { MonitorModel } from '../../monitor-model';
17+
1718
const queryString = require('query-string');
1819

1920
export namespace SerialPlotterContribution {
@@ -57,6 +58,8 @@ export class PlotterFrontendContribution extends Contribution {
5758
this.window = null;
5859
}
5960
});
61+
this.monitorManagerProxy.onMonitorShouldReset(() => this.reset());
62+
6063
return super.onStart(app);
6164
}
6265

@@ -78,19 +81,7 @@ export class PlotterFrontendContribution extends Contribution {
7881
}
7982

8083
async startPlotter(): Promise<void> {
81-
if (
82-
!this.boardsServiceProvider.boardsConfig.selectedBoard ||
83-
!this.boardsServiceProvider.boardsConfig.selectedPort
84-
) {
85-
this.messageService.error(
86-
`You need to select a connected board to start the serial plotter`
87-
);
88-
return;
89-
}
90-
await this.monitorManagerProxy.startMonitor(
91-
this.boardsServiceProvider.boardsConfig.selectedBoard,
92-
this.boardsServiceProvider.boardsConfig.selectedPort
93-
);
84+
await this.monitorManagerProxy.startMonitor();
9485
if (!!this.window) {
9586
this.window.focus();
9687
return;

Diff for: arduino-ide-extension/src/common/protocol/monitor-service.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,12 @@ export const MonitorManagerProxyClient = Symbol('MonitorManagerProxyClient');
3030
export interface MonitorManagerProxyClient {
3131
onMessagesReceived: Event<{ messages: string[] }>;
3232
onMonitorSettingsDidChange: Event<MonitorSettings>;
33+
onMonitorShouldReset: Event<void>;
3334
connect(addressPort: number): void;
3435
disconnect(): void;
3536
getWebSocketPort(): number | undefined;
3637
isWSConnected(): Promise<boolean>;
37-
startMonitor(
38-
board: Board,
39-
port: Port,
40-
settings?: PluggableMonitorSettings
41-
): Promise<void>;
38+
startMonitor(settings?: PluggableMonitorSettings): Promise<void>;
4239
getCurrentSettings(board: Board, port: Port): Promise<MonitorSettings>;
4340
send(message: string): void;
4441
changeSettings(settings: MonitorSettings): void;

Diff for: arduino-ide-extension/src/node/monitor-service.ts

+27-17
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export class MonitorService extends CoreClientAware implements Disposable {
6262

6363
protected uploadInProgress = false;
6464
protected _initialized = new Deferred<void>();
65+
protected creating: Deferred<Status>;
6566

6667
constructor(
6768
@inject(ILogger)
@@ -121,6 +122,7 @@ export class MonitorService extends CoreClientAware implements Disposable {
121122
dispose(): void {
122123
this.stop();
123124
this.onDisposeEmitter.fire();
125+
this.onWSClientsNumberChanged?.dispose();
124126
}
125127

126128
/**
@@ -148,24 +150,30 @@ export class MonitorService extends CoreClientAware implements Disposable {
148150
* @returns a status to verify connection has been established.
149151
*/
150152
async start(): Promise<Status> {
153+
if (this.creating?.state === 'unresolved') return this.creating.promise;
154+
this.creating = new Deferred();
151155
if (this.duplex) {
152156
this.updateClientsSettings({
153157
monitorUISettings: { connected: true, serialPort: this.port.address },
154158
});
155-
return Status.ALREADY_CONNECTED;
159+
this.creating.resolve(Status.ALREADY_CONNECTED);
160+
return this.creating.promise;
156161
}
157162

158163
if (!this.board?.fqbn || !this.port?.address || !this.port?.protocol) {
159164
this.updateClientsSettings({ monitorUISettings: { connected: false } });
160165

161-
return Status.CONFIG_MISSING;
166+
this.creating.resolve(Status.CONFIG_MISSING);
167+
return this.creating.promise;
162168
}
163169

164170
if (this.uploadInProgress) {
165171
this.updateClientsSettings({
166172
monitorUISettings: { connected: false, serialPort: this.port.address },
167173
});
168-
return Status.UPLOAD_IN_PROGRESS;
174+
175+
this.creating.resolve(Status.UPLOAD_IN_PROGRESS);
176+
return this.creating.promise;
169177
}
170178

171179
this.logger.info('starting monitor');
@@ -213,7 +221,7 @@ export class MonitorService extends CoreClientAware implements Disposable {
213221

214222
// Promise executor
215223
const writeToStream = (resolve: (value: boolean) => void) => {
216-
this.duplex = this.duplex || coreClient.client.monitor();
224+
this.duplex = coreClient.client.monitor();
217225

218226
const duplexHandlers: DuplexHandler[] = [
219227
{
@@ -257,17 +265,16 @@ export class MonitorService extends CoreClientAware implements Disposable {
257265
this.logger.error(res.getError());
258266
return;
259267
}
268+
if (res.getSuccess()) {
269+
resolve(true);
270+
return;
271+
}
260272
const data = res.getRxData();
261273
const message =
262274
typeof data === 'string'
263275
? data
264276
: new TextDecoder('utf8').decode(data);
265277
this.messages.push(...splitLines(message));
266-
267-
// if (res.getSuccess()) {
268-
// resolve(true);
269-
// return;
270-
// }
271278
},
272279
},
273280
];
@@ -277,27 +284,30 @@ export class MonitorService extends CoreClientAware implements Disposable {
277284
};
278285

279286
let attemptsRemaining = 10;
280-
let wroteToStreamWithoutError = false;
281-
do {
282-
await new Promise((r) => setTimeout(r, 10000));
283-
wroteToStreamWithoutError = await new Promise(writeToStream);
287+
let wroteToStreamSuccessfully = false;
288+
while (attemptsRemaining > 0) {
289+
wroteToStreamSuccessfully = await new Promise(writeToStream);
290+
if (wroteToStreamSuccessfully) break;
284291
attemptsRemaining -= 1;
285-
} while (!wroteToStreamWithoutError && attemptsRemaining > 0);
292+
await new Promise((r) => setTimeout(r, 2000));
293+
}
286294

287-
if (wroteToStreamWithoutError) {
295+
if (wroteToStreamSuccessfully) {
288296
this.startMessagesHandlers();
289297
this.logger.info(
290298
`started monitor to ${this.port?.address} using ${this.port?.protocol}`
291299
);
292300
this.updateClientsSettings({
293301
monitorUISettings: { connected: true, serialPort: this.port.address },
294302
});
295-
return Status.OK;
303+
this.creating.resolve(Status.OK);
304+
return this.creating.promise;
296305
} else {
297306
this.logger.warn(
298307
`failed starting monitor to ${this.port?.address} using ${this.port?.protocol}`
299308
);
300-
return Status.NOT_CONNECTED;
309+
this.creating.resolve(Status.NOT_CONNECTED);
310+
return this.creating.promise;
301311
}
302312
}
303313

0 commit comments

Comments
 (0)