Skip to content

Commit 0899336

Browse files
committed
experimenting with websockets
1 parent b36b447 commit 0899336

9 files changed

+293
-35
lines changed

arduino-ide-extension/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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",

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,9 +397,19 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
397397
createWidget: () => context.container.get(MonitorWidget),
398398
}));
399399
// Frontend binding for the serial monitor service
400+
// bind(MonitorServiceClient)
401+
// .toDynamicValue((context) => {
402+
// const client = context.container.get(MonitorServiceClientImpl);
403+
// WebSocketConnectionProvider.createProxy(
404+
// context.container,
405+
// MonitorServicePath,
406+
// client
407+
// );
408+
// return client;
409+
// })
410+
// .inSingletonScope();
400411
bind(MonitorService)
401412
.toDynamicValue((context) => {
402-
debugger;
403413
const connection = context.container.get(WebSocketConnectionProvider);
404414
const client =
405415
context.container.get<MonitorServiceClient>(MonitorServiceClient);

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

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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,6 +71,31 @@ export class MonitorConnection {
7171

7272
@postConstruct()
7373
protected init(): void {
74+
this.monitorServiceClient.onMessage(async (port) => {
75+
const w = new WebSocket(`ws://localhost:${port}`);
76+
let h = 0;
77+
w.onmessage = (res) => {
78+
const messages = JSON.parse(res.data);
79+
h += messages.length;
80+
81+
if (h > 1000) {
82+
h = 0;
83+
console.log('read 1000 messages');
84+
}
85+
// console.log(`received ${messages.length} messages`);
86+
// this.onReadEmitter.fire({ messages });
87+
};
88+
});
89+
90+
// let i = 0;
91+
// this.monitorServiceClient.onMessage(async (msg) => {
92+
// // msg received
93+
// i++;
94+
// if (i % 1000 === 0) {
95+
// // console.log(msg);
96+
// }
97+
// });
98+
7499
this.monitorServiceClient.onError(async (error) => {
75100
let shouldReconnect = false;
76101
if (this.state) {
@@ -231,15 +256,22 @@ export class MonitorConnection {
231256
);
232257
const connectStatus = await this.monitorService.connect(config);
233258
if (Status.isOK(connectStatus)) {
259+
let j = 0;
234260
const requestMessage = () => {
235-
this.monitorService.request().then(({ message }) => {
261+
this.monitorService.request().then(({ messages }) => {
236262
if (this.connected) {
237-
this.onReadEmitter.fire({ message });
263+
// this.onReadEmitter.fire({ messages });
264+
j += messages.length;
265+
if (j > 1000) {
266+
j = 0;
267+
// console.log(`read more than 1000 messages`);
268+
}
269+
238270
requestMessage();
239271
}
240272
});
241273
};
242-
requestMessage();
274+
// requestMessage();
243275
this.state = { config };
244276
console.info(
245277
`<<< Serial monitor connection created for ${Board.toString(
@@ -300,7 +332,7 @@ export class MonitorConnection {
300332
return this.onConnectionChangedEmitter.event;
301333
}
302334

303-
get onRead(): Event<{ message: string }> {
335+
get onRead(): Event<{ messages: string[] }> {
304336
return this.onReadEmitter.event;
305337
}
306338

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
}

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

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,9 @@ export class SerialMonitorOutput extends React.Component<
326326
return (
327327
<React.Fragment>
328328
<div style={{ whiteSpace: 'pre', fontFamily: 'monospace' }}>
329-
{this.state.lines.map((l) => `${l}\n`)}
329+
{this.state.lines.map((line, i) => (
330+
<MonitorTextLine text={line} key={i} />
331+
))}
330332
</div>
331333
<div
332334
style={{ float: 'left', clear: 'both' }}
@@ -341,24 +343,30 @@ export class SerialMonitorOutput extends React.Component<
341343
componentDidMount(): void {
342344
this.scrollToBottom();
343345
this.toDisposeBeforeUnmount.pushAll([
344-
this.props.monitorConnection.onRead(({ message }) => {
345-
const rawLines = message.split('\n');
346-
const lines: string[] = [];
347-
const timestamp = () =>
348-
this.state.timestamp
349-
? `${dateFormat(new Date(), 'H:M:ss.l')} -> `
350-
: '';
351-
for (let i = 0; i < rawLines.length; i++) {
352-
if (
353-
i === 0
354-
// && this.state.content.length !== 0
355-
) {
356-
lines.push(rawLines[i]);
357-
} else {
358-
lines.push(timestamp() + rawLines[i]);
346+
this.props.monitorConnection.onRead(({ messages }) => {
347+
messages.forEach((message) => {
348+
const rawLines = message.split('\n');
349+
const lines: string[] = [];
350+
const timestamp = () =>
351+
this.state.timestamp
352+
? `${dateFormat(new Date(), 'H:M:ss.l')} -> `
353+
: '';
354+
355+
for (let i = 0; i < rawLines.length; i++) {
356+
if (i === 0 && this.state.lines.length !== 0) {
357+
lines.push(rawLines[i]);
358+
} else {
359+
lines.push(timestamp() + rawLines[i]);
360+
}
359361
}
360-
}
361-
this.setState({ lines: this.state.lines.concat(lines) });
362+
363+
this.setState((prevState) => ({
364+
lines: [...prevState.lines, lines.join('\n')],
365+
}));
366+
367+
// const content = this.state.content + lines.join('\n');
368+
// this.setState({ content });
369+
});
362370
}),
363371
this.props.clearConsoleEvent(() => this.setState({ lines: [] })),
364372
this.props.monitorModel.onChange(({ property }) => {
@@ -386,6 +394,11 @@ export class SerialMonitorOutput extends React.Component<
386394
}
387395
}
388396

397+
const _MonitorTextLine = ({ text }: { text: string }): React.ReactElement => {
398+
return <div>{text}</div>;
399+
};
400+
export const MonitorTextLine = React.memo(_MonitorTextLine);
401+
389402
export interface SelectOption<T> {
390403
readonly label: string;
391404
readonly value: T;

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export interface MonitorService extends JsonRpcServer<MonitorServiceClient> {
2424
connect(config: MonitorConfig): Promise<Status>;
2525
disconnect(): Promise<Status>;
2626
send(message: string): Promise<Status>;
27-
request(): Promise<{ message: string }>;
27+
request(): Promise<{ messages: string[] }>;
2828
}
2929

3030
export interface MonitorConfig {
@@ -61,8 +61,10 @@ export namespace MonitorConfig {
6161

6262
export const MonitorServiceClient = Symbol('MonitorServiceClient');
6363
export interface MonitorServiceClient {
64-
notifyError(event: MonitorError): void;
6564
onError: Event<MonitorError>;
65+
onMessage: Event<string>;
66+
notifyError(event: MonitorError): void;
67+
notifyMessage(message: string): void;
6668
}
6769

6870
export interface MonitorError {

arduino-ide-extension/src/node/arduino-ide-backend-module.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
200200
bind(MonitorClientProvider).toSelf().inSingletonScope();
201201
bind(MonitorServiceImpl).toSelf().inSingletonScope();
202202
bind(MonitorService).toService(MonitorServiceImpl);
203+
203204
bindBackendService<MonitorService, MonitorServiceClient>(
204205
MonitorServicePath,
205206
MonitorService,

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

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ClientDuplexStream } from '@grpc/grpc-js';
2-
import { TextDecoder, TextEncoder } from 'util';
2+
import { TextEncoder } from 'util';
33
import { injectable, inject, named } from 'inversify';
44
import { Struct } from 'google-protobuf/google/protobuf/struct_pb';
55
import { Emitter } from '@theia/core/lib/common/event';
@@ -18,6 +18,7 @@ import {
1818
} from '../cli-protocol/cc/arduino/cli/monitor/v1/monitor_pb';
1919
import { MonitorClientProvider } from './monitor-client-provider';
2020
import { Board, Port } from '../../common/protocol/boards-service';
21+
import * as WebSocket from 'ws';
2122

2223
interface ErrorWithCode extends Error {
2324
readonly code: number;
@@ -74,6 +75,13 @@ export class MonitorServiceImpl implements MonitorService {
7475
protected messages: string[] = [];
7576
protected onMessageDidReadEmitter = new Emitter<void>();
7677

78+
private rand = 0;
79+
80+
constructor() {
81+
this.rand = Math.floor(1000 * Math.random());
82+
console.log(`Spawning ${this.rand}`);
83+
}
84+
7785
setClient(client: MonitorServiceClient | undefined): void {
7886
this.client = client;
7987
}
@@ -122,14 +130,36 @@ export class MonitorServiceImpl implements MonitorService {
122130
}).bind(this)
123131
);
124132

133+
const ws = new WebSocket.Server({ port: 0 });
134+
const address: any = ws.address();
135+
this.client?.notifyMessage(address.port);
136+
let wsConn: WebSocket | null = null;
137+
ws.on('connection', (ws) => {
138+
wsConn = ws;
139+
});
140+
141+
const emptyTheQueue = () => {
142+
if (this.messages.length) {
143+
wsConn?.send(JSON.stringify(this.messages));
144+
this.messages = [];
145+
}
146+
};
147+
148+
// empty the queue every 16ms (~60fps)
149+
setInterval(emptyTheQueue, 32);
150+
125151
duplex.on(
126152
'data',
127153
((resp: StreamingOpenResponse) => {
154+
// eslint-disable-next-line unused-imports/no-unused-vars
128155
const raw = resp.getData();
156+
129157
const message =
130158
typeof raw === 'string' ? raw : new TextDecoder('utf8').decode(raw);
159+
// this.client?.notifyMessage(message);
131160
this.messages.push(message);
132-
this.onMessageDidReadEmitter.fire();
161+
// this.onMessageDidReadEmitter.fire();
162+
// wsConn?.send(message);
133163
}).bind(this)
134164
);
135165

@@ -207,12 +237,14 @@ export class MonitorServiceImpl implements MonitorService {
207237
});
208238
}
209239

210-
async request(): Promise<{ message: string }> {
211-
const message = this.messages.shift();
212-
if (message) {
213-
return { message };
240+
async request(): Promise<{ messages: string[] }> {
241+
// const messages = this.messages.shift();
242+
const messages = this.messages;
243+
if (messages.length) {
244+
this.messages = [];
245+
return { messages };
214246
}
215-
return new Promise<{ message: string }>((resolve) => {
247+
return new Promise<{ messages: string[] }>((resolve) => {
216248
const toDispose = this.onMessageDidReadEmitter.event(() => {
217249
toDispose.dispose();
218250
resolve(this.request());

0 commit comments

Comments
 (0)