mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-08 20:06:32 +00:00
fix: update monitor settings only if it's changed
Closes #375 Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
parent
c0488d1f64
commit
d6a4b0f910
@ -73,6 +73,7 @@
|
|||||||
"google-protobuf": "^3.20.1",
|
"google-protobuf": "^3.20.1",
|
||||||
"hash.js": "^1.1.7",
|
"hash.js": "^1.1.7",
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
|
"just-diff": "^5.1.1",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
"keytar": "7.2.0",
|
"keytar": "7.2.0",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { ClientDuplexStream } from '@grpc/grpc-js';
|
import { ClientDuplexStream } from '@grpc/grpc-js';
|
||||||
import { Disposable, Emitter, ILogger } from '@theia/core';
|
import { Disposable, Emitter, ILogger } from '@theia/core';
|
||||||
import { inject, named, postConstruct } from '@theia/core/shared/inversify';
|
import { inject, named, postConstruct } from '@theia/core/shared/inversify';
|
||||||
|
import { diff, Operation } from 'just-diff';
|
||||||
import { Board, Port, Status, Monitor } from '../common/protocol';
|
import { Board, Port, Status, Monitor } from '../common/protocol';
|
||||||
import {
|
import {
|
||||||
EnumerateMonitorPortSettingsRequest,
|
EnumerateMonitorPortSettingsRequest,
|
||||||
@ -80,6 +81,15 @@ export class MonitorService extends CoreClientAware implements Disposable {
|
|||||||
private readonly port: Port;
|
private readonly port: Port;
|
||||||
private readonly monitorID: string;
|
private readonly monitorID: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The lightweight representation of the port configuration currently in use for the running monitor.
|
||||||
|
* IDE2 stores this object after starting the monitor. On every monitor settings change request, IDE2 compares
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
private currentPortConfigSnapshot:
|
||||||
|
| MonitorPortConfiguration.AsObject
|
||||||
|
| undefined;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@inject(MonitorServiceFactoryOptions) options: MonitorServiceFactoryOptions
|
@inject(MonitorServiceFactoryOptions) options: MonitorServiceFactoryOptions
|
||||||
) {
|
) {
|
||||||
@ -211,6 +221,16 @@ export class MonitorService extends CoreClientAware implements Disposable {
|
|||||||
monitorRequest
|
monitorRequest
|
||||||
);
|
);
|
||||||
if (wroteToStreamSuccessfully) {
|
if (wroteToStreamSuccessfully) {
|
||||||
|
// Only store the config, if the monitor has successfully started.
|
||||||
|
this.currentPortConfigSnapshot = MonitorPortConfiguration.toObject(
|
||||||
|
false,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
this.logger.info(
|
||||||
|
`Using port configuration for ${this.port.protocol}:${
|
||||||
|
this.port.address
|
||||||
|
}: ${JSON.stringify(this.currentPortConfigSnapshot)}`
|
||||||
|
);
|
||||||
this.startMessagesHandlers();
|
this.startMessagesHandlers();
|
||||||
this.logger.info(
|
this.logger.info(
|
||||||
`started monitor to ${this.port?.address} using ${this.port?.protocol}`
|
`started monitor to ${this.port?.address} using ${this.port?.protocol}`
|
||||||
@ -518,16 +538,120 @@ export class MonitorService extends CoreClientAware implements Disposable {
|
|||||||
if (!this.duplex) {
|
if (!this.duplex) {
|
||||||
return Status.NOT_CONNECTED;
|
return Status.NOT_CONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const diffConfig = this.maybeUpdatePortConfigSnapshot(config);
|
||||||
|
if (!diffConfig) {
|
||||||
|
this.logger.info(
|
||||||
|
`No port configuration changes have been detected. No need to send configure commands to the running monitor ${this.port.protocol}:${this.port.address}.`
|
||||||
|
);
|
||||||
|
return Status.OK;
|
||||||
|
}
|
||||||
|
|
||||||
const coreClient = await this.coreClient;
|
const coreClient = await this.coreClient;
|
||||||
const { instance } = coreClient;
|
const { instance } = coreClient;
|
||||||
|
|
||||||
|
this.logger.info(
|
||||||
|
`Sending monitor request with new port configuration: ${JSON.stringify(
|
||||||
|
MonitorPortConfiguration.toObject(false, diffConfig)
|
||||||
|
)}`
|
||||||
|
);
|
||||||
const req = new MonitorRequest();
|
const req = new MonitorRequest();
|
||||||
req.setInstance(instance);
|
req.setInstance(instance);
|
||||||
req.setPortConfiguration(config);
|
req.setPortConfiguration(diffConfig);
|
||||||
this.duplex.write(req);
|
this.duplex.write(req);
|
||||||
return Status.OK;
|
return Status.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to calculate a diff between the `otherPortConfig` argument and the `currentPortConfigSnapshot`.
|
||||||
|
*
|
||||||
|
* If the current config snapshot and the snapshot derived from `otherPortConfig` are the same, no snapshot update happens,
|
||||||
|
* and the function returns with undefined. Otherwise, the current snapshot config value will be updated from the snapshot
|
||||||
|
* derived from the `otherPortConfig` argument, and this function returns with a `MonitorPortConfiguration` instance
|
||||||
|
* representing only the difference between the two snapshot configs to avoid sending unnecessary monitor to configure commands to the CLI.
|
||||||
|
* See [#1703 (comment)](https://github.com/arduino/arduino-ide/pull/1703#issuecomment-1327913005) for more details.
|
||||||
|
*/
|
||||||
|
private maybeUpdatePortConfigSnapshot(
|
||||||
|
otherPortConfig: MonitorPortConfiguration
|
||||||
|
): MonitorPortConfiguration | undefined {
|
||||||
|
const otherPortConfigSnapshot = MonitorPortConfiguration.toObject(
|
||||||
|
false,
|
||||||
|
otherPortConfig
|
||||||
|
);
|
||||||
|
if (!this.currentPortConfigSnapshot) {
|
||||||
|
throw new Error(
|
||||||
|
`The current port configuration object was undefined when tried to merge in ${JSON.stringify(
|
||||||
|
otherPortConfigSnapshot
|
||||||
|
)}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const snapshotDiff = diff(
|
||||||
|
this.currentPortConfigSnapshot,
|
||||||
|
otherPortConfigSnapshot
|
||||||
|
);
|
||||||
|
if (!snapshotDiff.length) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const diffConfig = snapshotDiff.reduce((acc, curr) => {
|
||||||
|
if (!this.isValidMonitorPortSettingChange(curr)) {
|
||||||
|
throw new Error(
|
||||||
|
`Expected only 'replace' operation: a 'value' change in the 'settingsList'. Calculated diff a ${JSON.stringify(
|
||||||
|
snapshotDiff
|
||||||
|
)} between ${JSON.stringify(
|
||||||
|
this.currentPortConfigSnapshot
|
||||||
|
)} and ${JSON.stringify(
|
||||||
|
otherPortConfigSnapshot
|
||||||
|
)} snapshots. Current JSON-patch entry was ${JSON.stringify(curr)}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const { path, value } = curr;
|
||||||
|
const [, index] = path;
|
||||||
|
if (!this.currentPortConfigSnapshot?.settingsList) {
|
||||||
|
throw new Error(
|
||||||
|
`'settingsList' is missing from current port config snapshot: ${JSON.stringify(
|
||||||
|
this.currentPortConfigSnapshot
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const changedSetting = this.currentPortConfigSnapshot.settingsList[index];
|
||||||
|
const setting = new MonitorPortSetting();
|
||||||
|
setting.setValue(value);
|
||||||
|
setting.setSettingId(changedSetting.settingId);
|
||||||
|
acc.addSettings(setting);
|
||||||
|
return acc;
|
||||||
|
}, new MonitorPortConfiguration());
|
||||||
|
|
||||||
|
this.currentPortConfigSnapshot = otherPortConfigSnapshot;
|
||||||
|
this.logger.info(
|
||||||
|
`Updated the port configuration for ${this.port.protocol}:${
|
||||||
|
this.port.address
|
||||||
|
}: ${JSON.stringify(this.currentPortConfigSnapshot)}`
|
||||||
|
);
|
||||||
|
return diffConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
private isValidMonitorPortSettingChange(entry: {
|
||||||
|
op: Operation;
|
||||||
|
path: (string | number)[];
|
||||||
|
value: unknown;
|
||||||
|
}): entry is {
|
||||||
|
op: 'replace';
|
||||||
|
path: ['settingsList', number, string];
|
||||||
|
value: string;
|
||||||
|
} {
|
||||||
|
const { op, path, value } = entry;
|
||||||
|
return (
|
||||||
|
op === 'replace' &&
|
||||||
|
path.length === 3 &&
|
||||||
|
path[0] === 'settingsList' &&
|
||||||
|
typeof path[1] === 'number' &&
|
||||||
|
path[2] === 'value' &&
|
||||||
|
typeof value === 'string'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the necessary handlers to send and receive
|
* Starts the necessary handlers to send and receive
|
||||||
* messages to and from the frontend and the running monitor
|
* messages to and from the frontend and the running monitor
|
||||||
|
@ -9391,6 +9391,11 @@ jsprim@^1.2.2:
|
|||||||
array-includes "^3.1.5"
|
array-includes "^3.1.5"
|
||||||
object.assign "^4.1.2"
|
object.assign "^4.1.2"
|
||||||
|
|
||||||
|
just-diff@^5.1.1:
|
||||||
|
version "5.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/just-diff/-/just-diff-5.1.1.tgz#8da6414342a5ed6d02ccd64f5586cbbed3146202"
|
||||||
|
integrity sha512-u8HXJ3HlNrTzY7zrYYKjNEfBlyjqhdBkoyTVdjtn7p02RJD5NvR8rIClzeGA7t+UYP1/7eAkWNLU0+P3QrEqKQ==
|
||||||
|
|
||||||
jwt-decode@^3.1.2:
|
jwt-decode@^3.1.2:
|
||||||
version "3.1.2"
|
version "3.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59"
|
resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user