Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 54a67fc

Browse files
fstasiAlberto Iannaccone
and
Alberto Iannaccone
committed
Improve Serial Monitor Performances (#524)
Co-authored-by: Alberto Iannaccone <a.iannaccone@arduino.cc>
1 parent 7f8b227 commit 54a67fc

File tree

13 files changed

+868
-461
lines changed

13 files changed

+868
-461
lines changed

‎arduino-ide-extension/package.json‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"test:watch": "mocha --watch --watch-files lib \"./lib/test/**/*.test.js\""
1919
},
2020
"dependencies": {
21-
"@grpc/grpc-js": "^1.1.1",
21+
"@grpc/grpc-js": "^1.3.7",
2222
"@theia/application-package": "next",
2323
"@theia/core": "next",
2424
"@theia/editor": "next",
@@ -64,6 +64,7 @@
6464
"fuzzy": "^0.1.3",
6565
"glob": "^7.1.6",
6666
"google-protobuf": "^3.11.4",
67+
"grpc": "^1.24.11",
6768
"hash.js": "^1.1.7",
6869
"is-valid-path": "^0.1.1",
6970
"js-yaml": "^3.13.1",
@@ -78,6 +79,7 @@
7879
"react-disable": "^0.1.0",
7980
"react-select": "^3.0.4",
8081
"react-tabs": "^3.1.2",
82+
"react-window": "^1.8.6",
8183
"semver": "^7.3.2",
8284
"string-natural-compare": "^2.0.3",
8385
"temp": "^0.9.1",
@@ -89,6 +91,7 @@
8991
"@types/chai": "^4.2.7",
9092
"@types/chai-string": "^1.4.2",
9193
"@types/mocha": "^5.2.7",
94+
"@types/react-window": "^1.8.5",
9295
"chai": "^4.2.0",
9396
"chai-string": "^1.5.0",
9497
"decompress": "^4.2.0",
@@ -97,6 +100,7 @@
97100
"download": "^7.1.0",
98101
"grpc_tools_node_protoc_ts": "^4.1.0",
99102
"mocha": "^7.0.0",
103+
"mockdate": "^3.0.5",
100104
"moment": "^2.24.0",
101105
"protoc": "^1.0.4",
102106
"shelljs": "^0.8.3",

‎arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts‎

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -400,24 +400,14 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
400400
bind(MonitorService)
401401
.toDynamicValue((context) => {
402402
const connection = context.container.get(WebSocketConnectionProvider);
403-
const client = context.container.get(MonitorServiceClientImpl);
403+
const client =
404+
context.container.get<MonitorServiceClient>(MonitorServiceClient);
404405
return connection.createProxy(MonitorServicePath, client);
405406
})
406407
.inSingletonScope();
407408
bind(MonitorConnection).toSelf().inSingletonScope();
408409
// Serial monitor service client to receive and delegate notifications from the backend.
409-
bind(MonitorServiceClientImpl).toSelf().inSingletonScope();
410-
bind(MonitorServiceClient)
411-
.toDynamicValue((context) => {
412-
const client = context.container.get(MonitorServiceClientImpl);
413-
WebSocketConnectionProvider.createProxy(
414-
context.container,
415-
MonitorServicePath,
416-
client
417-
);
418-
return client;
419-
})
420-
.inSingletonScope();
410+
bind(MonitorServiceClient).to(MonitorServiceClientImpl).inSingletonScope();
421411

422412
bind(WorkspaceService).toSelf().inSingletonScope();
423413
rebind(TheiaWorkspaceService).toService(WorkspaceService);

‎arduino-ide-extension/src/browser/monitor/monitor-connection.ts‎

Lines changed: 122 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
MonitorConfig,
99
MonitorError,
1010
Status,
11+
MonitorServiceClient,
1112
} from '../../common/protocol/monitor-service';
1213
import { BoardsServiceProvider } from '../boards/boards-service-provider';
1314
import {
@@ -16,7 +17,6 @@ import {
1617
BoardsService,
1718
AttachedBoardsChangeEvent,
1819
} from '../../common/protocol/boards-service';
19-
import { MonitorServiceClientImpl } from './monitor-service-client-impl';
2020
import { BoardsConfig } from '../boards/boards-config';
2121
import { MonitorModel } from './monitor-model';
2222
import { NotificationCenter } from '../notification-center';
@@ -29,8 +29,8 @@ export class MonitorConnection {
2929
@inject(MonitorService)
3030
protected readonly monitorService: MonitorService;
3131

32-
@inject(MonitorServiceClientImpl)
33-
protected readonly monitorServiceClient: MonitorServiceClientImpl;
32+
@inject(MonitorServiceClient)
33+
protected readonly monitorServiceClient: MonitorServiceClient;
3434

3535
@inject(BoardsService)
3636
protected readonly boardsService: BoardsService;
@@ -59,7 +59,7 @@ export class MonitorConnection {
5959
/**
6060
* This emitter forwards all read events **iff** the connection is established.
6161
*/
62-
protected readonly onReadEmitter = new Emitter<{ message: string }>();
62+
protected readonly onReadEmitter = new Emitter<{ messages: string[] }>();
6363

6464
/**
6565
* Array for storing previous monitor errors received from the server, and based on the number of elements in this array,
@@ -71,112 +71,15 @@ export class MonitorConnection {
7171

7272
@postConstruct()
7373
protected init(): void {
74-
this.monitorServiceClient.onError(async (error) => {
75-
let shouldReconnect = false;
76-
if (this.state) {
77-
const { code, config } = error;
78-
const { board, port } = config;
79-
const options = { timeout: 3000 };
80-
switch (code) {
81-
case MonitorError.ErrorCodes.CLIENT_CANCEL: {
82-
console.debug(
83-
`Connection was canceled by client: ${MonitorConnection.State.toString(
84-
this.state
85-
)}.`
86-
);
87-
break;
88-
}
89-
case MonitorError.ErrorCodes.DEVICE_BUSY: {
90-
this.messageService.warn(
91-
`Connection failed. Serial port is busy: ${Port.toString(port)}.`,
92-
options
93-
);
94-
shouldReconnect = this.autoConnect;
95-
this.monitorErrors.push(error);
96-
break;
97-
}
98-
case MonitorError.ErrorCodes.DEVICE_NOT_CONFIGURED: {
99-
this.messageService.info(
100-
`Disconnected ${Board.toString(board, {
101-
useFqbn: false,
102-
})} from ${Port.toString(port)}.`,
103-
options
104-
);
105-
break;
106-
}
107-
case undefined: {
108-
this.messageService.error(
109-
`Unexpected error. Reconnecting ${Board.toString(
110-
board
111-
)} on port ${Port.toString(port)}.`,
112-
options
113-
);
114-
console.error(JSON.stringify(error));
115-
shouldReconnect = this.connected && this.autoConnect;
116-
break;
117-
}
118-
}
119-
const oldState = this.state;
120-
this.state = undefined;
121-
this.onConnectionChangedEmitter.fire(this.state);
122-
if (shouldReconnect) {
123-
if (this.monitorErrors.length >= 10) {
124-
this.messageService.warn(
125-
`Failed to reconnect ${Board.toString(board, {
126-
useFqbn: false,
127-
})} to the the serial-monitor after 10 consecutive attempts. The ${Port.toString(
128-
port
129-
)} serial port is busy. after 10 consecutive attempts.`
130-
);
131-
this.monitorErrors.length = 0;
132-
} else {
133-
const attempts = this.monitorErrors.length || 1;
134-
if (this.reconnectTimeout !== undefined) {
135-
// Clear the previous timer.
136-
window.clearTimeout(this.reconnectTimeout);
137-
}
138-
const timeout = attempts * 1000;
139-
this.messageService.warn(
140-
`Reconnecting ${Board.toString(board, {
141-
useFqbn: false,
142-
})} to ${Port.toString(port)} in ${attempts} seconds...`,
143-
{ timeout }
144-
);
145-
this.reconnectTimeout = window.setTimeout(
146-
() => this.connect(oldState.config),
147-
timeout
148-
);
149-
}
150-
}
151-
}
152-
});
74+
this.monitorServiceClient.onMessage(this.handleMessage.bind(this));
75+
this.monitorServiceClient.onError(this.handleError.bind(this));
15376
this.boardsServiceProvider.onBoardsConfigChanged(
15477
this.handleBoardConfigChange.bind(this)
15578
);
156-
this.notificationCenter.onAttachedBoardsChanged((event) => {
157-
if (this.autoConnect && this.connected) {
158-
const { boardsConfig } = this.boardsServiceProvider;
159-
if (
160-
this.boardsServiceProvider.canUploadTo(boardsConfig, {
161-
silent: false,
162-
})
163-
) {
164-
const { attached } = AttachedBoardsChangeEvent.diff(event);
165-
if (
166-
attached.boards.some(
167-
(board) =>
168-
!!board.port && BoardsConfig.Config.sameAs(boardsConfig, board)
169-
)
170-
) {
171-
const { selectedBoard: board, selectedPort: port } = boardsConfig;
172-
const { baudRate } = this.monitorModel;
173-
this.disconnect().then(() =>
174-
this.connect({ board, port, baudRate })
175-
);
176-
}
177-
}
178-
}
179-
});
79+
this.notificationCenter.onAttachedBoardsChanged(
80+
this.handleAttachedBoardsChanged.bind(this)
81+
);
82+
18083
// Handles the `baudRate` changes by reconnecting if required.
18184
this.monitorModel.onChange(({ property }) => {
18285
if (property === 'baudRate' && this.autoConnect && this.connected) {
@@ -186,6 +89,14 @@ export class MonitorConnection {
18689
});
18790
}
18891

92+
async handleMessage(port: string): Promise<void> {
93+
const w = new WebSocket(`ws://localhost:${port}`);
94+
w.onmessage = (res) => {
95+
const messages = JSON.parse(res.data);
96+
this.onReadEmitter.fire({ messages });
97+
};
98+
}
99+
189100
get connected(): boolean {
190101
return !!this.state;
191102
}
@@ -217,6 +128,109 @@ export class MonitorConnection {
217128
}
218129
}
219130

131+
handleError(error: MonitorError): void {
132+
let shouldReconnect = false;
133+
if (this.state) {
134+
const { code, config } = error;
135+
const { board, port } = config;
136+
const options = { timeout: 3000 };
137+
switch (code) {
138+
case MonitorError.ErrorCodes.CLIENT_CANCEL: {
139+
console.debug(
140+
`Connection was canceled by client: ${MonitorConnection.State.toString(
141+
this.state
142+
)}.`
143+
);
144+
break;
145+
}
146+
case MonitorError.ErrorCodes.DEVICE_BUSY: {
147+
this.messageService.warn(
148+
`Connection failed. Serial port is busy: ${Port.toString(port)}.`,
149+
options
150+
);
151+
shouldReconnect = this.autoConnect;
152+
this.monitorErrors.push(error);
153+
break;
154+
}
155+
case MonitorError.ErrorCodes.DEVICE_NOT_CONFIGURED: {
156+
this.messageService.info(
157+
`Disconnected ${Board.toString(board, {
158+
useFqbn: false,
159+
})} from ${Port.toString(port)}.`,
160+
options
161+
);
162+
break;
163+
}
164+
case undefined: {
165+
this.messageService.error(
166+
`Unexpected error. Reconnecting ${Board.toString(
167+
board
168+
)} on port ${Port.toString(port)}.`,
169+
options
170+
);
171+
console.error(JSON.stringify(error));
172+
shouldReconnect = this.connected && this.autoConnect;
173+
break;
174+
}
175+
}
176+
const oldState = this.state;
177+
this.state = undefined;
178+
this.onConnectionChangedEmitter.fire(this.state);
179+
if (shouldReconnect) {
180+
if (this.monitorErrors.length >= 10) {
181+
this.messageService.warn(
182+
`Failed to reconnect ${Board.toString(board, {
183+
useFqbn: false,
184+
})} to the the serial-monitor after 10 consecutive attempts. The ${Port.toString(
185+
port
186+
)} serial port is busy. after 10 consecutive attempts.`
187+
);
188+
this.monitorErrors.length = 0;
189+
} else {
190+
const attempts = this.monitorErrors.length || 1;
191+
if (this.reconnectTimeout !== undefined) {
192+
// Clear the previous timer.
193+
window.clearTimeout(this.reconnectTimeout);
194+
}
195+
const timeout = attempts * 1000;
196+
this.messageService.warn(
197+
`Reconnecting ${Board.toString(board, {
198+
useFqbn: false,
199+
})} to ${Port.toString(port)} in ${attempts} seconds...`,
200+
{ timeout }
201+
);
202+
this.reconnectTimeout = window.setTimeout(
203+
() => this.connect(oldState.config),
204+
timeout
205+
);
206+
}
207+
}
208+
}
209+
}
210+
211+
handleAttachedBoardsChanged(event: AttachedBoardsChangeEvent): void {
212+
if (this.autoConnect && this.connected) {
213+
const { boardsConfig } = this.boardsServiceProvider;
214+
if (
215+
this.boardsServiceProvider.canUploadTo(boardsConfig, {
216+
silent: false,
217+
})
218+
) {
219+
const { attached } = AttachedBoardsChangeEvent.diff(event);
220+
if (
221+
attached.boards.some(
222+
(board) =>
223+
!!board.port && BoardsConfig.Config.sameAs(boardsConfig, board)
224+
)
225+
) {
226+
const { selectedBoard: board, selectedPort: port } = boardsConfig;
227+
const { baudRate } = this.monitorModel;
228+
this.disconnect().then(() => this.connect({ board, port, baudRate }));
229+
}
230+
}
231+
}
232+
}
233+
220234
async connect(config: MonitorConfig): Promise<Status> {
221235
if (this.connected) {
222236
const disconnectStatus = await this.disconnect();
@@ -231,15 +245,6 @@ export class MonitorConnection {
231245
);
232246
const connectStatus = await this.monitorService.connect(config);
233247
if (Status.isOK(connectStatus)) {
234-
const requestMessage = () => {
235-
this.monitorService.request().then(({ message }) => {
236-
if (this.connected) {
237-
this.onReadEmitter.fire({ message });
238-
requestMessage();
239-
}
240-
});
241-
};
242-
requestMessage();
243248
this.state = { config };
244249
console.info(
245250
`<<< Serial monitor connection created for ${Board.toString(
@@ -300,7 +305,7 @@ export class MonitorConnection {
300305
return this.onConnectionChangedEmitter.event;
301306
}
302307

303-
get onRead(): Event<{ message: string }> {
308+
get onRead(): Event<{ messages: string[] }> {
304309
return this.onReadEmitter.event;
305310
}
306311

‎arduino-ide-extension/src/browser/monitor/monitor-service-client-impl.ts‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,14 @@ export class MonitorServiceClientImpl implements MonitorServiceClient {
1010
protected readonly onErrorEmitter = new Emitter<MonitorError>();
1111
readonly onError = this.onErrorEmitter.event;
1212

13+
protected readonly onMessageEmitter = new Emitter<string>();
14+
readonly onMessage = this.onMessageEmitter.event;
15+
1316
notifyError(error: MonitorError): void {
1417
this.onErrorEmitter.fire(error);
1518
}
19+
20+
notifyMessage(message: string): void {
21+
this.onMessageEmitter.fire(message);
22+
}
1623
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /