Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 607b97d

Browse files
author
Akos Kitta
committedMar 29, 2023
feat: monitor connection status
Signed-off-by: Akos Kitta <[email protected]>
1 parent 796aac4 commit 607b97d

File tree

11 files changed

+300
-129
lines changed

11 files changed

+300
-129
lines changed
 

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
MessageService,
77
nls,
88
} from '@theia/core';
9+
import { Deferred } from '@theia/core/lib/common/promise-util';
910
import { inject, injectable } from '@theia/core/shared/inversify';
1011
import { Board, Port } from '../common/protocol';
1112
import {
@@ -88,6 +89,9 @@ export class MonitorManagerProxyClientImpl
8889
return;
8990
}
9091

92+
const opened = new Deferred<void>();
93+
this.webSocket.onopen = () => opened.resolve();
94+
this.webSocket.onerror = () => opened.reject();
9195
this.webSocket.onmessage = (message) => {
9296
const parsedMessage = JSON.parse(message.data);
9397
if (Array.isArray(parsedMessage))
@@ -100,6 +104,7 @@ export class MonitorManagerProxyClientImpl
100104
}
101105
};
102106
this.wsPort = addressPort;
107+
return opened.promise;
103108
}
104109

105110
/**

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

Lines changed: 36 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ import {
44
LocalStorageService,
55
} from '@theia/core/lib/browser';
66
import { inject, injectable } from '@theia/core/shared/inversify';
7-
import { MonitorManagerProxyClient } from '../common/protocol';
7+
import {
8+
MonitorConnectionStatus,
9+
monitorConnectionStatusEquals,
10+
MonitorEOL,
11+
MonitorManagerProxyClient,
12+
MonitorState,
13+
} from '../common/protocol';
814
import { isNullOrUndefined } from '../common/utils';
915
import { MonitorSettings } from '../node/monitor-settings/monitor-settings-provider';
1016

@@ -19,48 +25,48 @@ export class MonitorModel implements FrontendApplicationContribution {
1925
protected readonly monitorManagerProxy: MonitorManagerProxyClient;
2026

2127
protected readonly onChangeEmitter: Emitter<
22-
MonitorModel.State.Change<keyof MonitorModel.State>
28+
MonitorState.Change<keyof MonitorState>
2329
>;
2430

2531
protected _autoscroll: boolean;
2632
protected _timestamp: boolean;
27-
protected _lineEnding: MonitorModel.EOL;
33+
protected _lineEnding: MonitorEOL;
2834
protected _interpolate: boolean;
2935
protected _darkTheme: boolean;
3036
protected _wsPort: number;
3137
protected _serialPort: string;
32-
protected _connected: boolean;
38+
protected _connectionStatus: MonitorConnectionStatus;
3339

3440
constructor() {
3541
this._autoscroll = true;
3642
this._timestamp = false;
3743
this._interpolate = false;
38-
this._lineEnding = MonitorModel.EOL.DEFAULT;
44+
this._lineEnding = MonitorEOL.DEFAULT;
3945
this._darkTheme = false;
4046
this._wsPort = 0;
4147
this._serialPort = '';
42-
this._connected = false;
48+
this._connectionStatus = 'not-connected';
4349

4450
this.onChangeEmitter = new Emitter<
45-
MonitorModel.State.Change<keyof MonitorModel.State>
51+
MonitorState.Change<keyof MonitorState>
4652
>();
4753
}
4854

4955
onStart(): void {
5056
this.localStorageService
51-
.getData<MonitorModel.State>(MonitorModel.STORAGE_ID)
57+
.getData<MonitorState>(MonitorModel.STORAGE_ID)
5258
.then(this.restoreState.bind(this));
5359

5460
this.monitorManagerProxy.onMonitorSettingsDidChange(
5561
this.onMonitorSettingsDidChange.bind(this)
5662
);
5763
}
5864

59-
get onChange(): Event<MonitorModel.State.Change<keyof MonitorModel.State>> {
65+
get onChange(): Event<MonitorState.Change<keyof MonitorState>> {
6066
return this.onChangeEmitter.event;
6167
}
6268

63-
protected restoreState(state: MonitorModel.State): void {
69+
protected restoreState(state: MonitorState): void {
6470
if (!state) {
6571
return;
6672
}
@@ -125,11 +131,11 @@ export class MonitorModel implements FrontendApplicationContribution {
125131
this.timestamp = !this._timestamp;
126132
}
127133

128-
get lineEnding(): MonitorModel.EOL {
134+
get lineEnding(): MonitorEOL {
129135
return this._lineEnding;
130136
}
131137

132-
set lineEnding(lineEnding: MonitorModel.EOL) {
138+
set lineEnding(lineEnding: MonitorEOL) {
133139
if (lineEnding === this._lineEnding) return;
134140
this._lineEnding = lineEnding;
135141
this.monitorManagerProxy.changeSettings({
@@ -211,19 +217,26 @@ export class MonitorModel implements FrontendApplicationContribution {
211217
);
212218
}
213219

214-
get connected(): boolean {
215-
return this._connected;
220+
get connectionStatus(): MonitorConnectionStatus {
221+
return this._connectionStatus;
216222
}
217223

218-
set connected(connected: boolean) {
219-
if (connected === this._connected) return;
220-
this._connected = connected;
224+
set connectionStatus(connectionStatus: MonitorConnectionStatus) {
225+
if (
226+
monitorConnectionStatusEquals(connectionStatus, this.connectionStatus)
227+
) {
228+
return;
229+
}
230+
this._connectionStatus = connectionStatus;
221231
this.monitorManagerProxy.changeSettings({
222-
monitorUISettings: { connected },
232+
monitorUISettings: {
233+
connectionStatus,
234+
connected: connectionStatus === 'connected', // TODO check if needed
235+
},
223236
});
224237
this.onChangeEmitter.fire({
225-
property: 'connected',
226-
value: this._connected,
238+
property: 'connectionStatus',
239+
value: this._connectionStatus,
227240
});
228241
}
229242

@@ -238,7 +251,7 @@ export class MonitorModel implements FrontendApplicationContribution {
238251
darkTheme,
239252
wsPort,
240253
serialPort,
241-
connected,
254+
connectionStatus,
242255
} = monitorUISettings;
243256

244257
if (!isNullOrUndefined(autoscroll)) this.autoscroll = autoscroll;
@@ -248,31 +261,7 @@ export class MonitorModel implements FrontendApplicationContribution {
248261
if (!isNullOrUndefined(darkTheme)) this.darkTheme = darkTheme;
249262
if (!isNullOrUndefined(wsPort)) this.wsPort = wsPort;
250263
if (!isNullOrUndefined(serialPort)) this.serialPort = serialPort;
251-
if (!isNullOrUndefined(connected)) this.connected = connected;
264+
if (!isNullOrUndefined(connectionStatus))
265+
this.connectionStatus = connectionStatus;
252266
};
253267
}
254-
255-
// TODO: Move this to /common
256-
export namespace MonitorModel {
257-
export interface State {
258-
autoscroll: boolean;
259-
timestamp: boolean;
260-
lineEnding: EOL;
261-
interpolate: boolean;
262-
darkTheme: boolean;
263-
wsPort: number;
264-
serialPort: string;
265-
connected: boolean;
266-
}
267-
export namespace State {
268-
export interface Change<K extends keyof State> {
269-
readonly property: K;
270-
readonly value: State[K];
271-
}
272-
}
273-
274-
export type EOL = '' | '\n' | '\r' | '\r\n';
275-
export namespace EOL {
276-
export const DEFAULT: EOL = '\n';
277-
}
278-
}

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ import { SerialMonitorSendInput } from './serial-monitor-send-input';
1313
import { SerialMonitorOutput } from './serial-monitor-send-output';
1414
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
1515
import { nls } from '@theia/core/lib/common';
16-
import { MonitorManagerProxyClient } from '../../../common/protocol';
16+
import {
17+
MonitorEOL,
18+
MonitorManagerProxyClient,
19+
} from '../../../common/protocol';
1720
import { MonitorModel } from '../../monitor-model';
1821
import { MonitorSettings } from '../../../node/monitor-settings/monitor-settings-provider';
1922

@@ -132,7 +135,7 @@ export class MonitorWidget extends ReactWidget {
132135
);
133136
};
134137

135-
protected get lineEndings(): SerialMonitorOutput.SelectOption<MonitorModel.EOL>[] {
138+
protected get lineEndings(): SerialMonitorOutput.SelectOption<MonitorEOL>[] {
136139
return [
137140
{
138141
label: nls.localize('arduino/serial/noLineEndings', 'No Line Ending'),
@@ -166,6 +169,7 @@ export class MonitorWidget extends ReactWidget {
166169
}
167170

168171
protected render(): React.ReactNode {
172+
debugger;
169173
const baudrate = this.settings?.pluggableMonitorSettings
170174
? this.settings.pluggableMonitorSettings.baudrate
171175
: undefined;
@@ -234,7 +238,7 @@ export class MonitorWidget extends ReactWidget {
234238
}
235239

236240
protected readonly onChangeLineEnding = (
237-
option: SerialMonitorOutput.SelectOption<MonitorModel.EOL>
241+
option: SerialMonitorOutput.SelectOption<MonitorEOL>
238242
): void => {
239243
this.monitorModel.lineEnding = option.value;
240244
};

‎arduino-ide-extension/src/browser/serial/monitor/serial-monitor-send-input.tsx

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import { DisposableCollection, nls } from '@theia/core/lib/common';
55
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
66
import { MonitorModel } from '../../monitor-model';
77
import { Unknown } from '../../../common/nls';
8+
import {
9+
isMonitorConnectionError,
10+
MonitorConnectionStatus,
11+
} from '../../../common/protocol';
812

913
class HistoryList {
1014
private readonly items: string[] = [];
@@ -62,7 +66,7 @@ export namespace SerialMonitorSendInput {
6266
}
6367
export interface State {
6468
text: string;
65-
connected: boolean;
69+
connectionStatus: MonitorConnectionStatus;
6670
history: HistoryList;
6771
}
6872
}
@@ -75,18 +79,27 @@ export class SerialMonitorSendInput extends React.Component<
7579

7680
constructor(props: Readonly<SerialMonitorSendInput.Props>) {
7781
super(props);
78-
this.state = { text: '', connected: true, history: new HistoryList() };
82+
this.state = {
83+
text: '',
84+
connectionStatus: 'not-connected',
85+
history: new HistoryList(),
86+
};
7987
this.onChange = this.onChange.bind(this);
8088
this.onSend = this.onSend.bind(this);
8189
this.onKeyDown = this.onKeyDown.bind(this);
8290
}
8391

8492
override componentDidMount(): void {
85-
this.setState({ connected: this.props.monitorModel.connected });
93+
this.setState({
94+
connectionStatus: this.props.monitorModel.connectionStatus,
95+
});
8696
this.toDisposeBeforeUnmount.push(
8797
this.props.monitorModel.onChange(({ property }) => {
88-
if (property === 'connected')
89-
this.setState({ connected: this.props.monitorModel.connected });
98+
if (property === 'connected' || property === 'connectionStatus') {
99+
this.setState({
100+
connectionStatus: this.props.monitorModel.connectionStatus,
101+
});
102+
}
90103
})
91104
);
92105
}
@@ -97,44 +110,72 @@ export class SerialMonitorSendInput extends React.Component<
97110
}
98111

99112
override render(): React.ReactNode {
113+
const inputClassName = this.inputClassName;
114+
const placeholder = this.placeholder;
100115
return (
101116
<input
102117
ref={this.setRef}
103118
type="text"
104-
className={`theia-input ${this.shouldShowWarning() ? 'warning' : ''}`}
105-
placeholder={this.placeholder}
119+
className={`theia-input ${inputClassName}`}
120+
readOnly={Boolean(inputClassName)}
121+
placeholder={placeholder}
122+
title={placeholder}
106123
value={this.state.text}
107124
onChange={this.onChange}
108125
onKeyDown={this.onKeyDown}
109126
/>
110127
);
111128
}
112129

130+
private get inputClassName(): 'error' | 'warning' | '' {
131+
const status = this.state.connectionStatus;
132+
if (isMonitorConnectionError(status)) {
133+
return 'error';
134+
}
135+
if (status === 'connected') {
136+
return '';
137+
}
138+
return 'warning';
139+
}
140+
113141
protected shouldShowWarning(): boolean {
114142
const board = this.props.boardsServiceProvider.boardsConfig.selectedBoard;
115143
const port = this.props.boardsServiceProvider.boardsConfig.selectedPort;
116-
return !this.state.connected || !board || !port;
144+
return !this.state.connectionStatus || !board || !port;
117145
}
118146

119147
protected get placeholder(): string {
120-
if (this.shouldShowWarning()) {
148+
const status = this.state.connectionStatus;
149+
if (isMonitorConnectionError(status)) {
150+
return status.errorMessage;
151+
}
152+
if (status === 'not-connected') {
121153
return nls.localize(
122154
'arduino/serial/notConnected',
123155
'Not connected. Select a board and a port to connect automatically.'
124156
);
125157
}
126-
127158
const board = this.props.boardsServiceProvider.boardsConfig.selectedBoard;
128159
const port = this.props.boardsServiceProvider.boardsConfig.selectedPort;
160+
const boardLabel = board
161+
? Board.toString(board, {
162+
useFqbn: false,
163+
})
164+
: Unknown;
165+
const portLabel = port ? port.address : Unknown;
166+
if (status === 'connecting') {
167+
return nls.localize(
168+
'arduino/serial/connecting',
169+
"Connecting to '{0}' on '{1}'...",
170+
boardLabel,
171+
portLabel
172+
);
173+
}
129174
return nls.localize(
130175
'arduino/serial/message',
131176
"Message (Enter to send message to '{0}' on '{1}')",
132-
board
133-
? Board.toString(board, {
134-
useFqbn: false,
135-
})
136-
: Unknown,
137-
port ? port.address : Unknown
177+
boardLabel,
178+
portLabel
138179
);
139180
}
140181

‎arduino-ide-extension/src/browser/style/index.css

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,22 +48,26 @@ body.theia-dark {
4848
}
4949

5050
.theia-input.warning::placeholder {
51-
/* Chrome, Firefox, Opera, Safari 10.1+ */
5251
color: var(--theia-warningForeground);
5352
background-color: var(--theia-warningBackground);
54-
opacity: 1; /* Firefox */
5553
}
5654

57-
.theia-input.warning:-ms-input-placeholder {
58-
/* Internet Explorer 10-11 */
59-
color: var(--theia-warningForeground);
60-
background-color: var(--theia-warningBackground);
55+
.theia-input.error:focus {
56+
outline-width: 1px;
57+
outline-style: solid;
58+
outline-offset: -1px;
59+
opacity: 1 !important;
60+
color: var(--theia-errorForeground);
61+
background-color: var(--theia-errorBackground);
6162
}
6263

63-
.theia-input.warning::-ms-input-placeholder {
64-
/* Microsoft Edge */
65-
color: var(--theia-warningForeground);
66-
background-color: var(--theia-warningBackground);
64+
.theia-input.error {
65+
background-color: var(--theia-errorBackground);
66+
}
67+
68+
.theia-input.error::placeholder {
69+
color: var(--theia-errorForeground);
70+
background-color: var(--theia-errorBackground);
6771
}
6872

6973
/* Makes the sidepanel a bit wider when opening the widget */

‎arduino-ide-extension/src/common/protocol/monitor-service.ts

Lines changed: 94 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export interface MonitorManagerProxyClient {
3131
onMessagesReceived: Event<{ messages: string[] }>;
3232
onMonitorSettingsDidChange: Event<MonitorSettings>;
3333
onMonitorShouldReset: Event<void>;
34-
connect(addressPort: number): void;
34+
connect(addressPort: number): Promise<void>;
3535
disconnect(): void;
3636
getWebSocketPort(): number | undefined;
3737
isWSConnected(): Promise<boolean>;
@@ -96,57 +96,65 @@ export function createConnectionFailedError(
9696
port: Port,
9797
details?: string
9898
): ApplicationError<number, PortDescriptor> {
99-
const { protocol, protocolLabel, address, addressLabel } = port;
100-
const formattedDetails = details ? `: ${details}.` : '.';
101-
return ConnectionFailedError(
102-
nls.localize(
99+
const { protocol, address } = port;
100+
let message;
101+
if (details) {
102+
const detailsWithPeriod = details.endsWith('.') ? details : `${details}.`;
103+
message = nls.localize(
104+
'arduino/monitor/connectionFailedErrorWithDetails',
105+
'{0} Could not connect to {1} {2} port.',
106+
detailsWithPeriod,
107+
address,
108+
protocol
109+
);
110+
} else {
111+
message = nls.localize(
103112
'arduino/monitor/connectionFailedError',
104-
'Could not connect to {0} {1} port{2}',
105-
addressLabel,
106-
protocolLabel,
107-
formattedDetails
108-
),
109-
{ protocol, address }
110-
);
113+
'Could not connect to {0} {1} port.',
114+
address,
115+
protocol
116+
);
117+
}
118+
return ConnectionFailedError(message, { protocol, address });
111119
}
112120
export function createNotConnectedError(
113121
port: Port
114122
): ApplicationError<number, PortDescriptor> {
115-
const { protocol, protocolLabel, address, addressLabel } = port;
123+
const { protocol, address } = port;
116124
return NotConnectedError(
117125
nls.localize(
118126
'arduino/monitor/notConnectedError',
119127
'Not connected to {0} {1} port.',
120-
addressLabel,
121-
protocolLabel
128+
address,
129+
protocol
122130
),
123131
{ protocol, address }
124132
);
125133
}
126134
export function createAlreadyConnectedError(
127135
port: Port
128136
): ApplicationError<number, PortDescriptor> {
129-
const { protocol, protocolLabel, address, addressLabel } = port;
137+
const { protocol, address } = port;
130138
return AlreadyConnectedError(
131139
nls.localize(
132140
'arduino/monitor/alreadyConnectedError',
133141
'Could not connect to {0} {1} port. Already connected.',
134-
addressLabel,
135-
protocolLabel
142+
address,
143+
protocol
136144
),
137145
{ protocol, address }
138146
);
139147
}
140148
export function createMissingConfigurationError(
141149
port: Port
142150
): ApplicationError<number, PortDescriptor> {
143-
const { protocol, protocolLabel, address, addressLabel } = port;
151+
const { protocol, address } = port;
144152
return MissingConfigurationError(
145153
nls.localize(
146154
'arduino/monitor/missingConfigurationError',
147155
'Could not connect to {0} {1} port. The monitor configuration is missing.',
148-
addressLabel,
149-
protocolLabel
156+
address,
157+
protocol
150158
),
151159
{ protocol, address }
152160
);
@@ -167,3 +175,68 @@ function createMonitorError(
167175
(message: string, data: PortDescriptor) => ({ data, message })
168176
);
169177
}
178+
179+
export interface MonitorConnectionError {
180+
readonly errorMessage: string;
181+
}
182+
183+
export type MonitorConnectionStatus =
184+
| 'connecting'
185+
| 'connected'
186+
| 'not-connected'
187+
| MonitorConnectionError;
188+
189+
export function monitorConnectionStatusEquals(
190+
left: MonitorConnectionStatus,
191+
right: MonitorConnectionStatus
192+
): boolean {
193+
if (typeof left === 'string') {
194+
return typeof right === 'string' ? left === right : false;
195+
}
196+
return typeof right === 'object'
197+
? right.errorMessage === left.errorMessage
198+
: false;
199+
}
200+
201+
/**
202+
* @deprecated see `MonitorState#connected`
203+
* TODO: check if needed for the plotter app
204+
*/
205+
export function isMonitorConnected(
206+
status: MonitorConnectionStatus
207+
): status is 'connected' {
208+
return status === 'connected';
209+
}
210+
211+
export function isMonitorConnectionError(
212+
status: MonitorConnectionStatus
213+
): status is MonitorConnectionError {
214+
return typeof status === 'object';
215+
}
216+
217+
export interface MonitorState {
218+
autoscroll: boolean;
219+
timestamp: boolean;
220+
lineEnding: MonitorEOL;
221+
interpolate: boolean;
222+
darkTheme: boolean;
223+
wsPort: number;
224+
serialPort: string;
225+
connectionStatus: MonitorConnectionStatus;
226+
/**
227+
* @deprecated This property is never get by IDE2 only set. This value is present to be backward compatible with the plotter app.
228+
* IDE2 uses `MonitorState#connectionStatus`.
229+
*/
230+
connected: boolean;
231+
}
232+
export namespace MonitorState {
233+
export interface Change<K extends keyof MonitorState> {
234+
readonly property: K;
235+
readonly value: MonitorState[K];
236+
}
237+
}
238+
239+
export type MonitorEOL = '' | '\n' | '\r' | '\r\n';
240+
export namespace MonitorEOL {
241+
export const DEFAULT: MonitorEOL = '\n';
242+
}

‎arduino-ide-extension/src/node/monitor-manager-proxy-impl.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { inject, injectable } from '@theia/core/shared/inversify';
2+
import { Disposable } from '@theia/core/lib/common/disposable';
23
import {
34
MonitorManagerProxy,
45
MonitorManagerProxyClient,
@@ -10,6 +11,10 @@ import {
1011
PluggableMonitorSettings,
1112
} from './monitor-settings/monitor-settings-provider';
1213

14+
export interface ConnectToClient {
15+
(): Promise<Disposable>;
16+
}
17+
1318
@injectable()
1419
export class MonitorManagerProxyImpl implements MonitorManagerProxy {
1520
protected client: MonitorManagerProxyClient;
@@ -40,8 +45,17 @@ export class MonitorManagerProxyImpl implements MonitorManagerProxy {
4045
await this.changeMonitorSettings(board, port, settings);
4146
}
4247

43-
const connectToClient = () => {
44-
this.client.connect(this.manager.getWebsocketAddressPort(board, port));
48+
const connectToClient = async () => {
49+
const address = this.manager.getWebsocketAddressPort(board, port);
50+
if (!this.client) {
51+
throw new Error(
52+
`No client was connected to this monitor manager. Board: ${
53+
board.fqbn ?? board.name
54+
}, port: ${port.address}, address: ${address}`
55+
);
56+
}
57+
await this.client.connect(address);
58+
return Disposable.create(() => this.client?.disconnect());
4559
};
4660
return this.manager.startMonitor(board, port, connectToClient);
4761
}

‎arduino-ide-extension/src/node/monitor-manager.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
Port,
88
} from '../common/protocol';
99
import { CoreClientAware } from './core-client-provider';
10+
import { ConnectToClient } from './monitor-manager-proxy-impl';
1011
import { MonitorService } from './monitor-service';
1112
import { MonitorServiceFactory } from './monitor-service-factory';
1213
import {
@@ -109,7 +110,7 @@ export class MonitorManager extends CoreClientAware {
109110
async startMonitor(
110111
board: Board,
111112
port: Port,
112-
connectToClient: () => void
113+
connectToClient: ConnectToClient
113114
): Promise<void> {
114115
const monitorID = this.monitorID(board.fqbn, port);
115116

@@ -132,14 +133,15 @@ export class MonitorManager extends CoreClientAware {
132133
return;
133134
}
134135

136+
const disposable = await connectToClient();
135137
try {
136138
await monitor.start();
137139
} catch (err) {
138140
if (!AlreadyConnectedError.is(err)) {
141+
disposable.dispose();
139142
throw err;
140143
}
141144
}
142-
await connectToClient();
143145
}
144146

145147
/**
@@ -255,7 +257,7 @@ export class MonitorManager extends CoreClientAware {
255257

256258
for (const {
257259
monitorID,
258-
serviceStartParams: [_, port],
260+
serviceStartParams: [, port],
259261
connectToClient,
260262
} of queued) {
261263
const boardsState = await this.boardsService.getState();
@@ -271,7 +273,7 @@ export class MonitorManager extends CoreClientAware {
271273

272274
if (monitorService) {
273275
await monitorService.start();
274-
await connectToClient();
276+
connectToClient();
275277
}
276278
}
277279
}

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

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
createMissingConfigurationError,
1717
createNotConnectedError,
1818
createConnectionFailedError,
19+
isMonitorConnected,
1920
} from '../common/protocol';
2021
import {
2122
EnumerateMonitorPortSettingsRequest,
@@ -181,20 +182,32 @@ export class MonitorService extends CoreClientAware implements Disposable {
181182
this.creating = new Deferred();
182183
if (this.duplex) {
183184
this.updateClientsSettings({
184-
monitorUISettings: { connected: true, serialPort: this.port.address },
185+
monitorUISettings: {
186+
connectionStatus: 'connected',
187+
connected: true,
188+
serialPort: this.port.address,
189+
},
185190
});
186191
this.creating.reject(createAlreadyConnectedError(this.port));
187192
return this.creating.promise;
188193
}
189194

190195
if (!this.board?.fqbn || !this.port?.address || !this.port?.protocol) {
191-
this.updateClientsSettings({ monitorUISettings: { connected: false } });
196+
this.updateClientsSettings({
197+
monitorUISettings: {
198+
connectionStatus: 'not-connected',
199+
connected: false,
200+
},
201+
});
192202

193203
this.creating.reject(createMissingConfigurationError(this.port));
194204
return this.creating.promise;
195205
}
196206

197207
this.logger.info('starting monitor');
208+
this.updateClientsSettings({
209+
monitorUISettings: { connectionStatus: 'connecting' },
210+
});
198211

199212
// get default monitor settings from the CLI
200213
const defaultSettings = await this.portMonitorSettings(
@@ -253,22 +266,30 @@ export class MonitorService extends CoreClientAware implements Disposable {
253266
`started monitor to ${this.port?.address} using ${this.port?.protocol}`
254267
);
255268
this.updateClientsSettings({
256-
monitorUISettings: { connected: true, serialPort: this.port.address },
269+
monitorUISettings: {
270+
connectionStatus: 'connected',
271+
connected: true,
272+
serialPort: this.port.address,
273+
},
257274
});
258275
this.creating.resolve();
259276
return this.creating.promise;
260277
} catch (err) {
261278
this.logger.warn(
262279
`failed starting monitor to ${this.port?.address} using ${this.port?.protocol}`
263280
);
264-
this.creating.reject(
265-
ApplicationError.is(err)
266-
? err
267-
: createConnectionFailedError(
268-
this.port,
269-
err instanceof Error ? err.message : String(err)
270-
)
271-
);
281+
const appError = ApplicationError.is(err)
282+
? err
283+
: createConnectionFailedError(
284+
this.port,
285+
err instanceof Error ? err.message : String(err)
286+
);
287+
this.creating.reject(appError);
288+
this.updateClientsSettings({
289+
monitorUISettings: {
290+
connectionStatus: { errorMessage: appError.message },
291+
},
292+
});
272293
return this.creating.promise;
273294
}
274295
}
@@ -287,19 +308,23 @@ export class MonitorService extends CoreClientAware implements Disposable {
287308
// default handlers
288309
duplex
289310
.on('close', () => {
290-
this.duplex = null;
291-
this.updateClientsSettings({
292-
monitorUISettings: { connected: false },
293-
});
311+
if (duplex === this.duplex) {
312+
this.duplex = null;
313+
this.updateClientsSettings({
314+
monitorUISettings: { connected: false },
315+
});
316+
}
294317
this.logger.info(
295318
`monitor to ${this.port?.address} using ${this.port?.protocol} closed by client`
296319
);
297320
})
298321
.on('end', () => {
299-
this.duplex = null;
300-
this.updateClientsSettings({
301-
monitorUISettings: { connected: false },
302-
});
322+
if (duplex === this.duplex) {
323+
this.duplex = null;
324+
this.updateClientsSettings({
325+
monitorUISettings: { connected: false },
326+
});
327+
}
303328
this.logger.info(
304329
`monitor to ${this.port?.address} using ${this.port?.protocol} closed by server`
305330
);
@@ -369,7 +394,7 @@ export class MonitorService extends CoreClientAware implements Disposable {
369394
WRITE_TO_STREAM_TIMEOUT_MS,
370395
nls.localize(
371396
'arduino/monitor/connectionTimeout',
372-
"Timeout. The IDE has not received the 'success' message from the monitor after successfully connecting to it."
397+
"Timeout. The IDE has not received the 'success' message from the monitor after successfully connecting to it"
373398
)
374399
),
375400
]) as Promise<unknown> as Promise<void>;
@@ -512,11 +537,15 @@ export class MonitorService extends CoreClientAware implements Disposable {
512537
}
513538
}
514539

540+
const connectionStatus = Boolean(this.duplex)
541+
? 'connected'
542+
: 'not-connected';
515543
this.updateClientsSettings({
516544
monitorUISettings: {
517545
...settings.monitorUISettings,
518-
connected: !!this.duplex,
546+
connectionStatus,
519547
serialPort: this.port.address,
548+
connected: isMonitorConnected(connectionStatus),
520549
},
521550
pluggableMonitorSettings: reconciledSettings,
522551
});
@@ -674,6 +703,15 @@ export class MonitorService extends CoreClientAware implements Disposable {
674703

675704
updateClientsSettings(settings: MonitorSettings): void {
676705
this.settings = { ...this.settings, ...settings };
706+
if (
707+
settings.monitorUISettings?.connectionStatus &&
708+
!('connected' in settings.monitorUISettings)
709+
) {
710+
// Make sure the deprecated `connected` prop is set.
711+
settings.monitorUISettings.connected = isMonitorConnected(
712+
settings.monitorUISettings.connectionStatus
713+
);
714+
}
677715
const command: Monitor.Message = {
678716
command: Monitor.MiddlewareCommand.ON_SETTINGS_DID_CHANGE,
679717
data: settings,

‎arduino-ide-extension/src/node/monitor-settings/monitor-settings-provider.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import { MonitorModel } from '../../browser/monitor-model';
2-
import { PluggableMonitorSetting } from '../../common/protocol';
1+
import { MonitorState, PluggableMonitorSetting } from '../../common/protocol';
32

43
export type PluggableMonitorSettings = Record<string, PluggableMonitorSetting>;
54
export interface MonitorSettings {
65
pluggableMonitorSettings?: PluggableMonitorSettings;
7-
monitorUISettings?: Partial<MonitorModel.State>;
6+
monitorUISettings?: Partial<MonitorState>;
87
}
98

109
export const MonitorSettingsProvider = Symbol('MonitorSettingsProvider');

‎i18n/en.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,8 +329,9 @@
329329
},
330330
"monitor": {
331331
"alreadyConnectedError": "Could not connect to {0} {1} port. Already connected.",
332-
"connectionFailedError": "Could not connect to {0} {1} port{2}",
333-
"connectionTimeout": "Timeout. The IDE has not received the 'success' message from the monitor after successfully connecting to it.",
332+
"connectionFailedError": "Could not connect to {0} {1} port.",
333+
"connectionFailedErrorWithDetails": "{0} Could not connect to {1} {2} port.",
334+
"connectionTimeout": "Timeout. The IDE has not received the 'success' message from the monitor after successfully connecting to it",
334335
"missingConfigurationError": "Could not connect to {0} {1} port. The monitor configuration is missing.",
335336
"notConnectedError": "Not connected to {0} {1} port.",
336337
"unableToCloseWebSocket": "Unable to close websocket",
@@ -413,6 +414,7 @@
413414
"serial": {
414415
"autoscroll": "Autoscroll",
415416
"carriageReturn": "Carriage Return",
417+
"connecting": "Connecting to '{0}' on '{1}'...",
416418
"message": "Message (Enter to send message to '{0}' on '{1}')",
417419
"newLine": "New Line",
418420
"newLineCarriageReturn": "Both NL & CR",

0 commit comments

Comments
 (0)
Please sign in to comment.