Skip to content

Commit c7242ca

Browse files
author
Akos Kitta
committed
slightly better reconnecting experience.
Signed-off-by: Akos Kitta <kittaakos@typefox.io>
1 parent a4e5e65 commit c7242ca

File tree

6 files changed

+65
-40
lines changed

6 files changed

+65
-40
lines changed

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

+14-8
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,20 @@ export class BoardsServiceClientImpl implements BoardsServiceClient {
107107
return this._boardsConfig;
108108
}
109109

110+
/**
111+
* `true` if the `config.selectedBoard` is defined; hence can compile against the board. Otherwise, `false`.
112+
*/
113+
canVerify(config: BoardsConfig.Config | undefined = this.boardsConfig): config is BoardsConfig.Config & { selectedBoard: Board } {
114+
return !!config && !!config.selectedBoard;
115+
}
116+
117+
/**
118+
* `true` if the `canVerify` and the `config.selectedPort` is also set with FQBN, hence can upload to board. Otherwise, `false`.
119+
*/
120+
canUploadTo(config: BoardsConfig.Config | undefined = this.boardsConfig): config is RecursiveRequired<BoardsConfig.Config> {
121+
return this.canVerify(config) && !!config.selectedPort && !!config.selectedBoard.fqbn;
122+
}
123+
110124
protected saveState(): Promise<void> {
111125
return this.storageService.setData('latest-valid-boards-config', this.latestValidBoardsConfig);
112126
}
@@ -118,12 +132,4 @@ export class BoardsServiceClientImpl implements BoardsServiceClient {
118132
}
119133
}
120134

121-
protected canVerify(config: BoardsConfig.Config | undefined): config is BoardsConfig.Config & { selectedBoard: Board } {
122-
return !!config && !!config.selectedBoard;
123-
}
124-
125-
protected canUploadTo(config: BoardsConfig.Config | undefined): config is RecursiveRequired<BoardsConfig.Config> {
126-
return this.canVerify(config) && !!config.selectedPort && !!config.selectedBoard.fqbn;
127-
}
128-
129135
}

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

+10-7
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ export class MonitorConnection {
4747
break;
4848
}
4949
case MonitorError.ErrorCodes.DEVICE_NOT_CONFIGURED: {
50-
const { port } = config;
51-
this.messageService.info(`Disconnected from ${Port.toString(port)}.`);
50+
const { port, board } = config;
51+
this.messageService.info(`Disconnected ${Board.toString(board, { useFqbn: false })} from ${Port.toString(port)}.`);
5252
break;
5353
}
5454
case undefined: {
@@ -77,19 +77,22 @@ export class MonitorConnection {
7777

7878
async connect(config: MonitorConfig): Promise<Status> {
7979
if (this.state) {
80-
throw new Error(`Already connected to ${MonitorConnection.State.toString(this.state)}.`);
80+
const disconnectStatus = await this.disconnect();
81+
if (!Status.isOK(disconnectStatus)) {
82+
return disconnectStatus;
83+
}
8184
}
82-
const status = await this.monitorService.connect(config);
83-
if (Status.isOK(status)) {
85+
const connectStatus = await this.monitorService.connect(config);
86+
if (Status.isOK(connectStatus)) {
8487
this.state = { config };
8588
this.onConnectionChangedEmitter.fire(true);
8689
}
87-
return Status.isOK(status);
90+
return Status.isOK(connectStatus);
8891
}
8992

9093
async disconnect(): Promise<Status> {
9194
if (!this.state) {
92-
throw new Error('Not connected. Nothing to disconnect.');
95+
return Status.OK;
9396
}
9497
console.log('>>> Disposing existing monitor connection before establishing a new one...');
9598
const status = await this.monitorService.disconnect();

arduino-ide-extension/src/browser/monitor/monitor-widget.tsx

+14-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { MessageService } from '@theia/core/lib/common/message-service';
99
import { ReactWidget, Message, Widget } from '@theia/core/lib/browser';
1010
import { MonitorServiceClientImpl } from './monitor-service-client-impl';
1111
import { MonitorConfig, MonitorService } from '../../common/protocol/monitor-service';
12-
import { AttachedSerialBoard, BoardsService } from '../../common/protocol/boards-service';
12+
import { AttachedSerialBoard, BoardsService, AttachedBoardsChangeEvent } from '../../common/protocol/boards-service';
1313
import { BoardsConfig } from '../boards/boards-config';
1414
import { BoardsServiceClientImpl } from '../boards/boards-service-client-impl';
1515
import { MonitorModel } from './monitor-model';
@@ -171,16 +171,24 @@ export class MonitorWidget extends ReactWidget {
171171
}
172172
}),
173173
this.boardsServiceClient.onBoardsConfigChanged(config => {
174-
const { selectedBoard, selectedPort } = config;
175-
if (selectedBoard && selectedPort) {
174+
if (this.boardsServiceClient.canUploadTo(config)) {
176175
this.boardsService.getAttachedBoards().then(({ boards }) => {
177176
if (boards.filter(AttachedSerialBoard.is).some(board => BoardsConfig.Config.sameAs(config, board))) {
178-
this.clearConsole();
179177
this.connect();
180178
}
181179
});
182180
}
183-
})]);
181+
}),
182+
this.boardsServiceClient.onBoardsChanged(event => {
183+
const { attached } = AttachedBoardsChangeEvent.diff(event);
184+
if (!this.connection.connected && this.boardsServiceClient.canUploadTo()) {
185+
const { boardsConfig } = this.boardsServiceClient;
186+
if (attached.boards.filter(AttachedSerialBoard.is).some(board => BoardsConfig.Config.sameAs(boardsConfig, board))) {
187+
this.connect();
188+
}
189+
}
190+
})
191+
]);
184192
this.update();
185193
}
186194

@@ -214,6 +222,7 @@ export class MonitorWidget extends ReactWidget {
214222
}
215223

216224
protected async connect(): Promise<void> {
225+
this.clearConsole();
217226
const config = await this.getConnectionConfig();
218227
if (config) {
219228
this.connection.connect(config);

arduino-ide-extension/src/browser/tool-output/client-service-impl.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ export class ToolOutputServiceClientImpl implements ToolOutputServiceClient {
1313
protected readonly outputContribution: OutputContribution;
1414

1515
onNewOutput(tool: string, chunk: string): void {
16-
this.outputContribution.openView({ reveal: true }).then(() => {
16+
this.outputContribution.openView({ activate: true }).then(() => {
1717
const channel = this.outputChannelManager.getChannel(`Arduino: ${tool}`);
1818
channel.setVisibility(true);
1919
channel.append(chunk);
20-
})
20+
});
2121
}
2222

23-
}
23+
}

arduino-ide-extension/src/common/protocol/boards-service.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,8 @@ export namespace Board {
214214
return !!board.fqbn;
215215
}
216216

217-
export function toString(board: Board): string {
218-
const fqbn = board.fqbn ? ` [${board.fqbn}]` : '';
217+
export function toString(board: Board, options: { useFqbn: boolean } = { useFqbn: true }): string {
218+
const fqbn = options && options.useFqbn && board.fqbn ? ` [${board.fqbn}]` : '';
219219
return `${board.name}${fqbn}`;
220220
}
221221

arduino-ide-extension/src/node/monitor/monitor-service-impl.ts

+22-15
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export class MonitorServiceImpl implements MonitorService {
4545
protected readonly monitorClientProvider: MonitorClientProvider;
4646

4747
protected client?: MonitorServiceClient;
48-
protected connection?: ClientDuplexStream<StreamingOpenReq, StreamingOpenResp>;
48+
protected connection?: { duplex: ClientDuplexStream<StreamingOpenReq, StreamingOpenResp>, config: MonitorConfig };
4949

5050
setClient(client: MonitorServiceClient | undefined): void {
5151
this.client = client;
@@ -66,20 +66,23 @@ export class MonitorServiceImpl implements MonitorService {
6666
return Status.ALREADY_CONNECTED;
6767
}
6868
const client = await this.monitorClientProvider.client;
69-
this.connection = client.streamingOpen();
70-
this.connection.on('error', ((error: Error) => {
69+
const duplex = client.streamingOpen();
70+
this.connection = { duplex, config };
71+
72+
duplex.on('error', ((error: Error) => {
7173
const monitorError = ErrorWithCode.toMonitorError(error, config);
72-
if (monitorError.code === undefined) {
73-
this.logger.error(error);
74-
}
75-
((monitorError.code === undefined ? this.disconnect() : Promise.resolve()) as Promise<any>).then(() => {
74+
this.disconnect().then(() => {
7675
if (this.client) {
7776
this.client.notifyError(monitorError);
7877
}
79-
})
78+
if (monitorError.code === undefined) {
79+
// Log the original, unexpected error.
80+
this.logger.error(error);
81+
}
82+
});
8083
}).bind(this));
8184

82-
this.connection.on('data', ((resp: StreamingOpenResp) => {
85+
duplex.on('data', ((resp: StreamingOpenResp) => {
8386
if (this.client) {
8487
const raw = resp.getData();
8588
const data = typeof raw === 'string' ? raw : new TextDecoder('utf8').decode(raw);
@@ -99,21 +102,25 @@ export class MonitorServiceImpl implements MonitorService {
99102

100103
return new Promise<Status>(resolve => {
101104
if (this.connection) {
102-
this.connection.write(req, () => {
103-
this.logger.info(`<<< Serial monitor connection created for ${Board.toString(config.board)} on port ${Port.toString(config.port)}.`);
105+
this.connection.duplex.write(req, () => {
106+
this.logger.info(`<<< Serial monitor connection created for ${Board.toString(config.board, { useFqbn: false })} on port ${Port.toString(config.port)}.`);
104107
resolve(Status.OK);
105108
});
106109
return;
107110
}
108-
resolve(Status.NOT_CONNECTED);
111+
this.disconnect().then(() => resolve(Status.NOT_CONNECTED));
109112
});
110113
}
111114

112115
async disconnect(): Promise<Status> {
116+
this.logger.info(`>>> Disposing monitor connection...`);
113117
if (!this.connection) {
118+
this.logger.warn(`<<< Not connected. Nothing to dispose.`);
114119
return Status.NOT_CONNECTED;
115120
}
116-
this.connection.cancel();
121+
const { duplex, config } = this.connection;
122+
duplex.cancel();
123+
this.logger.info(`<<< Disposed monitor connection for ${Board.toString(config.board, { useFqbn: false })} on port ${Port.toString(config.port)}.`);
117124
this.connection = undefined;
118125
return Status.OK;
119126
}
@@ -126,12 +133,12 @@ export class MonitorServiceImpl implements MonitorService {
126133
req.setData(new TextEncoder().encode(data));
127134
return new Promise<Status>(resolve => {
128135
if (this.connection) {
129-
this.connection.write(req, () => {
136+
this.connection.duplex.write(req, () => {
130137
resolve(Status.OK);
131138
});
132139
return;
133140
}
134-
resolve(Status.NOT_CONNECTED);
141+
this.disconnect().then(() => resolve(Status.NOT_CONNECTED));
135142
});
136143
}
137144

0 commit comments

Comments
 (0)