Skip to content

Commit ad2cfc8

Browse files
Akos Kittakittaakos
Akos Kitta
authored andcommitted
GH-10: Serial monitor should spare WS connection.
Closes #10. Signed-off-by: Akos Kitta <[email protected]>
1 parent f34f594 commit ad2cfc8

File tree

5 files changed

+54
-44
lines changed

5 files changed

+54
-44
lines changed

Diff for: arduino-ide-extension/src/browser/monitor/monitor-connection.ts

+12-9
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { deepClone } from '@theia/core/lib/common/objects';
33
import { Emitter, Event } from '@theia/core/lib/common/event';
44
import { MessageService } from '@theia/core/lib/common/message-service';
55
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
6-
import { MonitorService, MonitorConfig, MonitorError, Status, MonitorReadEvent } from '../../common/protocol/monitor-service';
6+
import { MonitorService, MonitorConfig, MonitorError, Status } from '../../common/protocol/monitor-service';
77
import { BoardsServiceProvider } from '../boards/boards-service-provider';
88
import { Port, Board, BoardsService, AttachedBoardsChangeEvent } from '../../common/protocol/boards-service';
99
import { MonitorServiceClientImpl } from './monitor-service-client-impl';
@@ -48,7 +48,7 @@ export class MonitorConnection {
4848
/**
4949
* This emitter forwards all read events **iff** the connection is established.
5050
*/
51-
protected readonly onReadEmitter = new Emitter<MonitorReadEvent>();
51+
protected readonly onReadEmitter = new Emitter<{ message: string }>();
5252

5353
/**
5454
* Array for storing previous monitor errors received from the server, and based on the number of elements in this array,
@@ -60,12 +60,6 @@ export class MonitorConnection {
6060

6161
@postConstruct()
6262
protected init(): void {
63-
// Forward the messages from the board **iff** connected.
64-
this.monitorServiceClient.onRead(event => {
65-
if (this.connected) {
66-
this.onReadEmitter.fire(event);
67-
}
68-
});
6963
this.monitorServiceClient.onError(async error => {
7064
let shouldReconnect = false;
7165
if (this.state) {
@@ -179,6 +173,15 @@ export class MonitorConnection {
179173
console.info(`>>> Creating serial monitor connection for ${Board.toString(config.board)} on port ${Port.toString(config.port)}...`);
180174
const connectStatus = await this.monitorService.connect(config);
181175
if (Status.isOK(connectStatus)) {
176+
const requestMessage = () => {
177+
this.monitorService.request().then(({ message }) => {
178+
if (this.connected) {
179+
this.onReadEmitter.fire({ message });
180+
requestMessage();
181+
}
182+
});
183+
}
184+
requestMessage();
182185
this.state = { config };
183186
console.info(`<<< Serial monitor connection created for ${Board.toString(config.board, { useFqbn: false })} on port ${Port.toString(config.port)}.`);
184187
}
@@ -225,7 +228,7 @@ export class MonitorConnection {
225228
return this.onConnectionChangedEmitter.event;
226229
}
227230

228-
get onRead(): Event<MonitorReadEvent> {
231+
get onRead(): Event<{ message: string }> {
229232
return this.onReadEmitter.event;
230233
}
231234

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

+1-9
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,13 @@
11
import { injectable } from 'inversify';
22
import { Emitter } from '@theia/core/lib/common/event';
3-
import { MonitorServiceClient, MonitorReadEvent, MonitorError } from '../../common/protocol/monitor-service';
3+
import { MonitorServiceClient, MonitorError } from '../../common/protocol/monitor-service';
44

55
@injectable()
66
export class MonitorServiceClientImpl implements MonitorServiceClient {
77

8-
protected readonly onReadEmitter = new Emitter<MonitorReadEvent>();
98
protected readonly onErrorEmitter = new Emitter<MonitorError>();
10-
readonly onRead = this.onReadEmitter.event;
119
readonly onError = this.onErrorEmitter.event;
1210

13-
notifyRead(event: MonitorReadEvent): void {
14-
this.onReadEmitter.fire(event);
15-
const { data } = event;
16-
console.debug(`Received data: ${data}`);
17-
}
18-
1911
notifyError(error: MonitorError): void {
2012
this.onErrorEmitter.fire(error);
2113
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,8 @@ export class SerialMonitorOutput extends React.Component<SerialMonitorOutput.Pro
294294
componentDidMount(): void {
295295
this.scrollToBottom();
296296
this.toDisposeBeforeUnmount.pushAll([
297-
this.props.monitorConnection.onRead(({ data }) => {
298-
const rawLines = data.split('\n');
297+
this.props.monitorConnection.onRead(({ message }) => {
298+
const rawLines = message.split('\n');
299299
const lines: string[] = []
300300
const timestamp = () => this.state.timestamp ? `${dateFormat(new Date(), 'H:M:ss.l')} -> ` : '';
301301
for (let i = 0; i < rawLines.length; i++) {

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

+2-6
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ export const MonitorService = Symbol('MonitorService');
2020
export interface MonitorService extends JsonRpcServer<MonitorServiceClient> {
2121
connect(config: MonitorConfig): Promise<Status>;
2222
disconnect(): Promise<Status>;
23-
send(data: string | Uint8Array): Promise<Status>;
23+
send(message: string): Promise<Status>;
24+
request(): Promise<{ message: string }>;
2425
}
2526

2627
export interface MonitorConfig {
@@ -51,14 +52,9 @@ export namespace MonitorConfig {
5152

5253
export const MonitorServiceClient = Symbol('MonitorServiceClient');
5354
export interface MonitorServiceClient {
54-
notifyRead(event: MonitorReadEvent): void;
5555
notifyError(event: MonitorError): void;
5656
}
5757

58-
export interface MonitorReadEvent {
59-
readonly data: string;
60-
}
61-
6258
export interface MonitorError {
6359
readonly message: string;
6460
/**

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

+37-18
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ClientDuplexStream } from '@grpc/grpc-js';
22
import { TextDecoder, TextEncoder } from 'util';
33
import { injectable, inject, named } from 'inversify';
44
import { Struct } from 'google-protobuf/google/protobuf/struct_pb';
5+
import { Emitter } from '@theia/core/lib/common/event';
56
import { ILogger } from '@theia/core/lib/common/logger';
67
import { MonitorService, MonitorServiceClient, MonitorConfig, MonitorError, Status } from '../../common/protocol/monitor-service';
78
import { StreamingOpenReq, StreamingOpenResp, MonitorConfig as GrpcMonitorConfig } from '../cli-protocol/monitor/monitor_pb';
@@ -46,6 +47,8 @@ export class MonitorServiceImpl implements MonitorService {
4647

4748
protected client?: MonitorServiceClient;
4849
protected connection?: { duplex: ClientDuplexStream<StreamingOpenReq, StreamingOpenResp>, config: MonitorConfig };
50+
protected messages: string[] = [];
51+
protected onMessageDidReadEmitter = new Emitter<void>();
4952

5053
setClient(client: MonitorServiceClient | undefined): void {
5154
this.client = client;
@@ -86,11 +89,10 @@ export class MonitorServiceImpl implements MonitorService {
8689
}).bind(this));
8790

8891
duplex.on('data', ((resp: StreamingOpenResp) => {
89-
if (this.client) {
90-
const raw = resp.getData();
91-
const data = typeof raw === 'string' ? raw : new TextDecoder('utf8').decode(raw);
92-
this.client.notifyRead({ data });
93-
}
92+
const raw = resp.getData();
93+
const message = typeof raw === 'string' ? raw : new TextDecoder('utf8').decode(raw);
94+
this.messages.push(message);
95+
this.onMessageDidReadEmitter.fire();
9496
}).bind(this));
9597

9698
const { type, port } = config;
@@ -116,27 +118,31 @@ export class MonitorServiceImpl implements MonitorService {
116118
}
117119

118120
async disconnect(reason?: MonitorError): Promise<Status> {
119-
if (!this.connection && reason && reason.code === MonitorError.ErrorCodes.CLIENT_CANCEL) {
121+
try {
122+
if (!this.connection && reason && reason.code === MonitorError.ErrorCodes.CLIENT_CANCEL) {
123+
return Status.OK;
124+
}
125+
this.logger.info(`>>> Disposing monitor connection...`);
126+
if (!this.connection) {
127+
this.logger.warn(`<<< Not connected. Nothing to dispose.`);
128+
return Status.NOT_CONNECTED;
129+
}
130+
const { duplex, config } = this.connection;
131+
duplex.cancel();
132+
this.logger.info(`<<< Disposed monitor connection for ${Board.toString(config.board, { useFqbn: false })} on port ${Port.toString(config.port)}.`);
133+
this.connection = undefined;
120134
return Status.OK;
135+
} finally {
136+
this.messages.length = 0;
121137
}
122-
this.logger.info(`>>> Disposing monitor connection...`);
123-
if (!this.connection) {
124-
this.logger.warn(`<<< Not connected. Nothing to dispose.`);
125-
return Status.NOT_CONNECTED;
126-
}
127-
const { duplex, config } = this.connection;
128-
duplex.cancel();
129-
this.logger.info(`<<< Disposed monitor connection for ${Board.toString(config.board, { useFqbn: false })} on port ${Port.toString(config.port)}.`);
130-
this.connection = undefined;
131-
return Status.OK;
132138
}
133139

134-
async send(data: string): Promise<Status> {
140+
async send(message: string): Promise<Status> {
135141
if (!this.connection) {
136142
return Status.NOT_CONNECTED;
137143
}
138144
const req = new StreamingOpenReq();
139-
req.setData(new TextEncoder().encode(data));
145+
req.setData(new TextEncoder().encode(message));
140146
return new Promise<Status>(resolve => {
141147
if (this.connection) {
142148
this.connection.duplex.write(req, () => {
@@ -148,6 +154,19 @@ export class MonitorServiceImpl implements MonitorService {
148154
});
149155
}
150156

157+
async request(): Promise<{ message: string }> {
158+
const message = this.messages.shift();
159+
if (message) {
160+
return { message };
161+
}
162+
return new Promise<{ message: string }>(resolve => {
163+
const toDispose = this.onMessageDidReadEmitter.event(() => {
164+
toDispose.dispose();
165+
resolve(this.request());
166+
});
167+
});
168+
}
169+
151170
protected mapType(type?: MonitorConfig.ConnectionType): GrpcMonitorConfig.TargetType {
152171
switch (type) {
153172
case MonitorConfig.ConnectionType.SERIAL: return GrpcMonitorConfig.TargetType.SERIAL;

0 commit comments

Comments
 (0)