Skip to content

Commit 0a67591

Browse files
author
Akos Kitta
committed
fix: update monitor settings only if it's changed
Closes #375 Signed-off-by: Akos Kitta <[email protected]>
1 parent ba16dcf commit 0a67591

File tree

3 files changed

+127
-1
lines changed

3 files changed

+127
-1
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
"google-protobuf": "^3.20.1",
7474
"hash.js": "^1.1.7",
7575
"js-yaml": "^3.13.1",
76+
"just-diff": "^5.1.1",
7677
"jwt-decode": "^3.1.2",
7778
"keytar": "7.2.0",
7879
"lodash.debounce": "^4.0.8",

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

+121-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ClientDuplexStream } from '@grpc/grpc-js';
22
import { Disposable, Emitter, ILogger } from '@theia/core';
33
import { inject, named, postConstruct } from '@theia/core/shared/inversify';
4+
import { diff, Operation } from 'just-diff';
45
import { Board, Port, Status, Monitor } from '../common/protocol';
56
import {
67
EnumerateMonitorPortSettingsRequest,
@@ -80,6 +81,15 @@ export class MonitorService extends CoreClientAware implements Disposable {
8081
private readonly port: Port;
8182
private readonly monitorID: string;
8283

84+
/**
85+
* The lightweight representation of the port configuration currently in use for the running monitor.
86+
* IDE2 stores this object after starting the monitor. On every monitor settings change request, IDE2 compares
87+
* the current config with the new settings, and only sends the diff as the new config to overcome https://github.com/arduino/arduino-ide/issues/375.
88+
*/
89+
private currentPortConfigSnapshot:
90+
| MonitorPortConfiguration.AsObject
91+
| undefined;
92+
8393
constructor(
8494
@inject(MonitorServiceFactoryOptions) options: MonitorServiceFactoryOptions
8595
) {
@@ -211,6 +221,14 @@ export class MonitorService extends CoreClientAware implements Disposable {
211221
monitorRequest
212222
);
213223
if (wroteToStreamSuccessfully) {
224+
// Only store the config, if the monitor has successfully started.
225+
this.currentPortConfigSnapshot = MonitorPortConfiguration.toObject(
226+
false,
227+
config
228+
);
229+
this.logger.info(
230+
`Using port configuration for ${this.port.protocol}:${this.port.address}: ${this.currentPortConfigSnapshot}`
231+
);
214232
this.startMessagesHandlers();
215233
this.logger.info(
216234
`started monitor to ${this.port?.address} using ${this.port?.protocol}`
@@ -518,16 +536,118 @@ export class MonitorService extends CoreClientAware implements Disposable {
518536
if (!this.duplex) {
519537
return Status.NOT_CONNECTED;
520538
}
539+
540+
const diffConfig = this.maybeUpdatePortConfigSnapshot(config);
541+
if (!diffConfig) {
542+
this.logger.info(
543+
`No port configuration changes have been detected. No need to send configure commands to the running monitor ${this.port.protocol}:${this.port.address}.`
544+
);
545+
return Status.OK;
546+
}
547+
521548
const coreClient = await this.coreClient;
522549
const { instance } = coreClient;
523550

551+
this.logger.info(
552+
`Sending monitor request with new port configuration: ${JSON.stringify(
553+
MonitorPortConfiguration.toObject(false, diffConfig)
554+
)}`
555+
);
524556
const req = new MonitorRequest();
525557
req.setInstance(instance);
526-
req.setPortConfiguration(config);
558+
req.setPortConfiguration(diffConfig);
527559
this.duplex.write(req);
528560
return Status.OK;
529561
}
530562

563+
/**
564+
* Function to calculate a diff between the `otherPortConfig` argument and the `currentPortConfigSnapshot`.
565+
*
566+
* If the current config snapshot and the snapshot derived from `otherPortConfig` are the same, no snapshot update happens,
567+
* and the function returns with undefined. Otherwise, the current snapshot config value will be updated from the snapshot
568+
* derived from the `otherPortConfig` argument, and this function returns with a `MonitorPortConfiguration` instance
569+
* representing only the difference between the two snapshot configs to avoid sending unnecessary monitor to configure commands to the CLI.
570+
* See [#1703 (comment)](https://github.com/arduino/arduino-ide/pull/1703#issuecomment-1327913005) for more details.
571+
*/
572+
private maybeUpdatePortConfigSnapshot(
573+
otherPortConfig: MonitorPortConfiguration
574+
): MonitorPortConfiguration | undefined {
575+
const otherPortConfigSnapshot = MonitorPortConfiguration.toObject(
576+
false,
577+
otherPortConfig
578+
);
579+
if (!this.currentPortConfigSnapshot) {
580+
throw new Error(
581+
`The current port configuration object was undefined when tried to merge in ${JSON.stringify(
582+
otherPortConfigSnapshot
583+
)}.`
584+
);
585+
}
586+
587+
const snapshotDiff = diff(
588+
this.currentPortConfigSnapshot,
589+
otherPortConfigSnapshot
590+
);
591+
if (!snapshotDiff.length) {
592+
return undefined;
593+
}
594+
595+
const diffConfig = snapshotDiff.reduce((acc, curr) => {
596+
if (!this.isValidMonitorPortSettingChange(curr)) {
597+
throw new Error(
598+
`Expected only 'replace' operation: a 'value' change in the 'settingsList'. Calculated diff a ${JSON.stringify(
599+
snapshotDiff
600+
)} between ${JSON.stringify(
601+
this.currentPortConfigSnapshot
602+
)} and ${JSON.stringify(
603+
otherPortConfigSnapshot
604+
)} snapshots. Current JSON-patch entry was ${curr}.`
605+
);
606+
}
607+
const { path, value } = curr;
608+
const [, index] = path;
609+
if (!this.currentPortConfigSnapshot?.settingsList) {
610+
throw new Error(
611+
`'settingsList' is missing from current port config snapshot: ${this.currentPortConfigSnapshot}`
612+
);
613+
}
614+
const changedSetting = this.currentPortConfigSnapshot.settingsList[index];
615+
const setting = new MonitorPortSetting();
616+
setting.setValue(value);
617+
setting.setSettingId(changedSetting.settingId);
618+
acc.addSettings(setting);
619+
return acc;
620+
}, new MonitorPortConfiguration());
621+
622+
this.currentPortConfigSnapshot = otherPortConfigSnapshot;
623+
this.logger.info(
624+
`Updated the port configuration for ${this.port.protocol}:${
625+
this.port.address
626+
}: ${JSON.stringify(this.currentPortConfigSnapshot)}`
627+
);
628+
return diffConfig;
629+
}
630+
631+
private isValidMonitorPortSettingChange(entry: {
632+
op: Operation;
633+
path: (string | number)[];
634+
value: unknown;
635+
}): entry is {
636+
op: 'add';
637+
path: ['settingsList', number, string];
638+
value: string;
639+
} {
640+
const { op, path, value } = entry;
641+
return (
642+
op === 'replace' &&
643+
path.length === 3 &&
644+
path[0] === 'settingsList' &&
645+
typeof path[1] === 'number' &&
646+
path[2] === 'value' &&
647+
typeof value === 'string'
648+
);
649+
}
650+
531651
/**
532652
* Starts the necessary handlers to send and receive
533653
* messages to and from the frontend and the running monitor

Diff for: yarn.lock

+5
Original file line numberDiff line numberDiff line change
@@ -9391,6 +9391,11 @@ jsprim@^1.2.2:
93919391
array-includes "^3.1.5"
93929392
object.assign "^4.1.2"
93939393

9394+
just-diff@^5.1.1:
9395+
version "5.1.1"
9396+
resolved "https://registry.yarnpkg.com/just-diff/-/just-diff-5.1.1.tgz#8da6414342a5ed6d02ccd64f5586cbbed3146202"
9397+
integrity sha512-u8HXJ3HlNrTzY7zrYYKjNEfBlyjqhdBkoyTVdjtn7p02RJD5NvR8rIClzeGA7t+UYP1/7eAkWNLU0+P3QrEqKQ==
9398+
93949399
jwt-decode@^3.1.2:
93959400
version "3.1.2"
93969401
resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59"

0 commit comments

Comments
 (0)