Skip to content

Commit 417d95f

Browse files
author
Akos Kitta
committed
Buffer events from BoardListWatchResponse.
Signed-off-by: Akos Kitta <[email protected]>
1 parent 0d545be commit 417d95f

File tree

4 files changed

+99
-73
lines changed

4 files changed

+99
-73
lines changed

Diff for: arduino-ide-extension/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
"deep-equal": "^2.0.5",
6767
"deepmerge": "2.0.1",
6868
"electron-updater": "^4.6.5",
69+
"fast-json-stable-stringify": "^2.0.0",
6970
"fast-safe-stringify": "^2.1.1",
7071
"glob": "^7.1.6",
7172
"google-protobuf": "^3.20.1",

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

+9
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import { Installable } from './installable';
44
import { ArduinoComponent } from './arduino-component';
55
import { nls } from '@theia/core/lib/common/nls';
66
import { All, Contributed, Partner, Type, Updatable } from '../nls';
7+
import stableJsonStringify = require('fast-json-stable-stringify');
78

9+
/**
10+
* Keys come from `Port#keyOf`.
11+
*/
812
export type AvailablePorts = Record<string, [Port, Array<Board>]>;
913
export namespace AvailablePorts {
1014
export function groupByProtocol(
@@ -37,6 +41,11 @@ export namespace AvailablePorts {
3741
ports: availablePorts,
3842
};
3943
}
44+
export function sameAs(left: AvailablePorts, right: AvailablePorts): boolean {
45+
return (
46+
left === right || stableJsonStringify(left) === stableJsonStringify(right)
47+
);
48+
}
4049
}
4150

4251
export interface AttachedBoardsChangeEvent {

Diff for: arduino-ide-extension/src/node/board-discovery.ts

+88-61
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { Disposable } from '@theia/core/shared/vscode-languageserver-protocol';
1010
import { v4 } from 'uuid';
1111
import { Unknown } from '../common/nls';
1212
import {
13-
AttachedBoardsChangeEvent,
1413
AvailablePorts,
1514
Board,
1615
NotificationServiceServer,
@@ -78,14 +77,6 @@ export class BoardDiscovery
7877

7978
onStart(): void {
8079
this.start();
81-
this.onClientDidRefresh(() => this.restart());
82-
}
83-
84-
private async restart(): Promise<void> {
85-
this.logger.info('restarting before stop');
86-
await this.stop();
87-
this.logger.info('restarting after stop');
88-
return this.start();
8980
}
9081

9182
onStop(): void {
@@ -259,57 +250,14 @@ export class BoardDiscovery
259250
return;
260251
}
261252

262-
const detectedPort = resp.getPort();
263-
if (detectedPort) {
264-
const { port, boards } = this.fromRpc(detectedPort);
265-
if (!port) {
266-
if (!!boards.length) {
267-
console.warn(
268-
`Could not detect the port, but unexpectedly received discovered boards. This is most likely a bug! Response was: ${this.toJson(
269-
resp
270-
)}`
271-
);
272-
}
273-
return;
274-
}
275-
const oldState = deepClone(this._availablePorts);
276-
const newState = deepClone(this._availablePorts);
277-
const key = Port.keyOf(port);
278-
279-
if (eventType === EventType.Add) {
280-
if (newState[key]) {
281-
const [, knownBoards] = newState[key];
282-
this.logger.warn(
283-
`Port '${Port.toString(
284-
port
285-
)}' was already available. Known boards before override: ${JSON.stringify(
286-
knownBoards
287-
)}`
288-
);
289-
}
290-
newState[key] = [port, boards];
291-
} else if (eventType === EventType.Remove) {
292-
if (!newState[key]) {
293-
this.logger.warn(
294-
`Port '${Port.toString(port)}' was not available. Skipping`
295-
);
296-
return;
297-
}
298-
delete newState[key];
299-
}
300-
301-
const event: AttachedBoardsChangeEvent = {
302-
oldState: {
303-
...AvailablePorts.split(oldState),
304-
},
305-
newState: {
306-
...AvailablePorts.split(newState),
307-
},
308-
uploadInProgress: this.uploadInProgress,
309-
};
310-
311-
this._availablePorts = newState;
312-
this.notificationService.notifyAttachedBoardsDidChange(event);
253+
const rpcDetectedPort = resp.getPort();
254+
if (rpcDetectedPort) {
255+
const detectedPort = this.fromRpc(rpcDetectedPort);
256+
this.fireSoon({ detectedPort, eventType });
257+
} else if (resp.getError()) {
258+
this.logger.error(
259+
`Could not extract any detected 'port' from the board list watch response. An 'error' has occurred: ${resp.getError()}`
260+
);
313261
}
314262
}
315263

@@ -340,6 +288,75 @@ export class BoardDiscovery
340288
};
341289
return port;
342290
}
291+
292+
private fireSoonHandle?: NodeJS.Timeout;
293+
private bufferedEvents: DetectedPortChangeEvent[] = [];
294+
private fireSoon(event: DetectedPortChangeEvent): void {
295+
this.bufferedEvents.push(event);
296+
clearTimeout(this.fireSoonHandle);
297+
this.fireSoonHandle = setTimeout(() => {
298+
const prevState = deepClone(this.availablePorts);
299+
const newState = this.calculateNewState(this.bufferedEvents, prevState);
300+
if (!AvailablePorts.sameAs(prevState, newState)) {
301+
this._availablePorts = newState;
302+
this.notificationService.notifyAttachedBoardsDidChange({
303+
newState: AvailablePorts.split(newState),
304+
oldState: AvailablePorts.split(prevState),
305+
uploadInProgress: this.uploadInProgress,
306+
});
307+
}
308+
this.bufferedEvents.length = 0;
309+
}, 100);
310+
}
311+
312+
private calculateNewState(
313+
events: DetectedPortChangeEvent[],
314+
prevState: AvailablePorts
315+
): AvailablePorts {
316+
const newState = deepClone(prevState);
317+
for (const { detectedPort, eventType } of events) {
318+
if (!DetectedPort.hasPort(detectedPort)) {
319+
if (!!detectedPort.boards.length) {
320+
console.warn(
321+
`Could not detect the port, but unexpectedly received discovered boards. This is most likely a bug! Detected port was: ${JSON.stringify(
322+
detectedPort
323+
)}`
324+
);
325+
} else {
326+
console.warn(
327+
`Could not detect the port. Skipping: ${JSON.stringify(
328+
detectedPort
329+
)}`
330+
);
331+
}
332+
continue;
333+
}
334+
const { port, boards } = detectedPort;
335+
const key = Port.keyOf(port);
336+
if (eventType === EventType.Add) {
337+
const alreadyDetectedPort = newState[key];
338+
if (alreadyDetectedPort) {
339+
console.warn(
340+
`Detected a new port that has been already discovered. The old value will be overridden. Old value: ${JSON.stringify(
341+
alreadyDetectedPort
342+
)}, new value: ${JSON.stringify(detectedPort)}`
343+
);
344+
}
345+
newState[key] = [port, boards];
346+
} else if (eventType === EventType.Remove) {
347+
const alreadyDetectedPort = newState[key];
348+
if (!alreadyDetectedPort) {
349+
console.warn(
350+
`Detected a port removal but it has not been discovered. This is most likely a bug! Detected port was: ${JSON.stringify(
351+
detectedPort
352+
)}`
353+
);
354+
}
355+
delete newState[key];
356+
}
357+
}
358+
return newState;
359+
}
343360
}
344361

345362
enum EventType {
@@ -364,8 +381,18 @@ namespace EventType {
364381
}
365382
}
366383
}
367-
368384
interface DetectedPort {
369385
port: Port | undefined;
370386
boards: Board[];
371387
}
388+
namespace DetectedPort {
389+
export function hasPort(
390+
detectedPort: DetectedPort
391+
): detectedPort is DetectedPort & { port: Port } {
392+
return !!detectedPort.port;
393+
}
394+
}
395+
interface DetectedPortChangeEvent {
396+
detectedPort: DetectedPort;
397+
eventType: EventType.Add | EventType.Remove;
398+
}

Diff for: arduino-ide-extension/src/node/core-client-provider.ts

+1-12
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
injectable,
66
postConstruct,
77
} from '@theia/core/shared/inversify';
8-
import { Emitter, Event } from '@theia/core/lib/common/event';
8+
import { Emitter } from '@theia/core/lib/common/event';
99
import { ArduinoCoreServiceClient } from './cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb';
1010
import { Instance } from './cli-protocol/cc/arduino/cli/commands/v1/common_pb';
1111
import {
@@ -53,8 +53,6 @@ export class CoreClientProvider {
5353
private readonly onClientReadyEmitter =
5454
new Emitter<CoreClientProvider.Client>();
5555
private readonly onClientReady = this.onClientReadyEmitter.event;
56-
private readonly onClientDidRefreshEmitter =
57-
new Emitter<CoreClientProvider.Client>();
5856

5957
@postConstruct()
6058
protected init(): void {
@@ -90,10 +88,6 @@ export class CoreClientProvider {
9088
return this.pending.promise;
9189
}
9290

93-
get onClientDidRefresh(): Event<CoreClientProvider.Client> {
94-
return this.onClientDidRefreshEmitter.event;
95-
}
96-
9791
/**
9892
* Encapsulates both the gRPC core client creation (`CreateRequest`) and initialization (`InitRequest`).
9993
*/
@@ -259,7 +253,6 @@ export class CoreClientProvider {
259253
await this.initInstance(client);
260254
// notify clients about the index update only after the client has been "re-initialized" and the new content is available.
261255
progressHandler.reportEnd();
262-
this.onClientDidRefreshEmitter.fire(client);
263256
} catch (err) {
264257
console.error('Failed to update indexes', err);
265258
progressHandler.reportError(
@@ -411,10 +404,6 @@ export abstract class CoreClientAware {
411404
protected get coreClient(): Promise<CoreClientProvider.Client> {
412405
return this.coreClientProvider.client;
413406
}
414-
415-
protected get onClientDidRefresh(): Event<CoreClientProvider.Client> {
416-
return this.coreClientProvider.onClientDidRefresh;
417-
}
418407
}
419408

420409
class IndexUpdateRequiredBeforeInitError extends Error {

0 commit comments

Comments
 (0)