monitor service provider singleton

This commit is contained in:
Francesco Stasi 2022-05-19 16:56:37 +02:00
parent 0427759fdb
commit a4ff05a82b
4 changed files with 157 additions and 41 deletions

View File

@ -85,8 +85,14 @@ import { ArduinoLocalizationContribution } from './arduino-localization-contribu
import { LocalizationContribution } from '@theia/core/lib/node/i18n/localization-contribution';
import { MonitorManagerProxyImpl } from './monitor-manager-proxy-impl';
import { MonitorManager, MonitorManagerName } from './monitor-manager';
import { MonitorManagerProxy, MonitorManagerProxyClient, MonitorManagerProxyPath } from '../common/protocol/monitor-service';
import {
MonitorManagerProxy,
MonitorManagerProxyClient,
MonitorManagerProxyPath,
} from '../common/protocol/monitor-service';
import { MonitorServiceName } from './monitor-service';
import { MonitorSettingsProvider } from './monitor-settings/monitor-settings-provider';
import { MonitorSettingsProviderImpl } from './monitor-settings/monitor-settings-provider-impl';
export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(BackendApplication).toSelf().inSingletonScope();
@ -198,6 +204,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
// a single MonitorManager is responsible for handling the actual connections to the pluggable monitors
bind(MonitorManager).toSelf().inSingletonScope();
bind(MonitorSettingsProviderImpl).toSelf().inSingletonScope();
bind(MonitorSettingsProvider).toService(MonitorSettingsProviderImpl);
// Serial client provider per connected frontend.
bind(ConnectionContainerModule).toConstantValue(
ConnectionContainerModule.create(({ bind, bindBackendService }) => {

View File

@ -122,10 +122,18 @@ export class MonitorService extends CoreClientAware implements Disposable {
}
this.logger.info('starting monitor');
this.settings = await this.monitorSettingsProvider.init(
monitorID,
this.coreClientProvider
// get default monitor settings from the CLI
const defaultSettings = await this.portMonitorSettings(
this.port.protocol,
this.board.fqbn
);
// get actual settings from the settings provider
this.settings = await this.monitorSettingsProvider.getSettings(
monitorID,
defaultSettings
);
await this.coreClientProvider.initialized;
const coreClient = await this.coreClient();
const { client, instance } = coreClient;

View File

@ -1,47 +1,145 @@
import { injectable } from 'inversify';
import { CoreClientProvider } from '../core-client-provider';
import * as fs from 'fs';
import { join } from 'path';
import { injectable, inject, postConstruct } from 'inversify';
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { promisify } from 'util';
import {
PluggableMonitorSettings,
MonitorSettingsProvider,
MonitorSettings,
} from './monitor-settings-provider';
import { Deferred } from '@theia/core/lib/common/promise-util';
const MONITOR_SETTINGS_FILE = 'pluggable-monitor-settings.json';
@injectable()
export class MonitorSettingsProviderImpl implements MonitorSettingsProvider {
// this is populated with all settings coming from the CLI. This should never get modified
// as it is used to double actual values set by the user
private monitorSettings: MonitorSettings;
@inject(EnvVariablesServer)
protected readonly envVariablesServer: EnvVariablesServer;
// this contains values for setting of the monitorSettings
// the key is MonitorSetting.id, the value should be one of the MonitorSetting.values
private monitorSettingsValues: Record<string, any>;
protected ready = new Deferred<void>();
init(
id: string,
coreClientProvider: CoreClientProvider
// this is populated with all settings coming from the CLI. This should never be modified
// // as it is used to double check the monitorSettings attribute
// private monitorDefaultSettings: PluggableMonitorSettings;
// this contains actual values coming from the stored file and edited by the user
// this is a map with MonitorId as key and PluggableMonitorSetting as value
private monitorSettings: Record<string, PluggableMonitorSettings>;
private pluggableMonitorSettingsPath: string;
@postConstruct()
protected async init(): Promise<void> {
// get the monitor settings file path
const configDirUri = await this.envVariablesServer.getConfigDirUri();
this.pluggableMonitorSettingsPath = join(
FileUri.fsPath(configDirUri),
MONITOR_SETTINGS_FILE
);
// read existing settings
this.readFile();
console.log(this.monitorSettings);
this.ready.resolve();
}
async getSettings(
monitorId: string,
defaultSettings: PluggableMonitorSettings
): Promise<PluggableMonitorSettings> {
throw new Error('Method not implemented.');
// wait for the service to complete the init
await this.ready.promise;
// 1. query the CLI (via coreClientProvider) and return all available settings for the pluggable monitor.
// store these in `monitorSettings` for later checkings
const { matchingSettings } = this.longestPrefixMatch(monitorId);
// 2. check for the settings file in the user's home directory
// a. if it doesn't exist, create it as an empty json file
// 3. search the file, looking for the longest prefix matching the id
// a. miss: populate `monitorSettingsValues` with all default settings from `monitorSettings`
// b. hit: populate `monitorSettingsValues` with the result for the search
// i. purge the `monitorSettingsValues` removing keys that are not defined in `monitorSettings`
// and adding those that are missing
// ii. save the `monitorSettingsValues` in the file, using the id as the key
return this.reconcileSettings(matchingSettings, defaultSettings);
}
get(): Promise<PluggableMonitorSettings> {
throw new Error('Method not implemented.');
}
set(settings: PluggableMonitorSettings): Promise<PluggableMonitorSettings> {
throw new Error('Method not implemented.');
async setSettings(
monitorId: string,
settings: PluggableMonitorSettings
): Promise<PluggableMonitorSettings> {
// wait for the service to complete the init
await this.ready.promise;
// 1. parse the settings parameter and remove any setting that is not defined in `monitorSettings`
// 2. update `monitorSettingsValues` accordingly
// 3. save it to the file
const newSettings = this.reconcileSettings(
settings,
this.monitorSettings[monitorId]
);
this.monitorSettings[monitorId] = newSettings;
await this.writeFile();
return newSettings;
}
private reconcileSettings(
newSettings: PluggableMonitorSettings,
defaultSettings: PluggableMonitorSettings
): PluggableMonitorSettings {
// TODO: implement
return newSettings;
}
private async readFile(): Promise<void> {
const rawJson = await promisify(fs.readFile)(
this.pluggableMonitorSettingsPath,
{
encoding: 'utf-8',
flag: 'a+', // a+ = append and read, creating the file if it doesn't exist
}
);
if (!rawJson) {
this.monitorSettings = {};
}
try {
this.monitorSettings = JSON.parse(rawJson);
} catch (error) {
console.error(
'Could not parse the pluggable monitor settings file. Using empty file.'
);
this.monitorSettings = {};
}
}
private async writeFile() {
await promisify(fs.writeFile)(
this.pluggableMonitorSettingsPath,
JSON.stringify(this.monitorSettings)
);
}
private longestPrefixMatch(id: string): {
matchingPrefix: string;
matchingSettings: PluggableMonitorSettings;
} {
const separator = '-';
const idTokens = id.split(separator);
let matchingPrefix = '';
let matchingSettings: PluggableMonitorSettings = {};
const monitorSettingsKeys = Object.keys(this.monitorSettings);
for (let i = 0; i < idTokens.length; i++) {
const prefix = idTokens.slice(0, i + 1).join(separator);
for (let k = 0; k < monitorSettingsKeys.length; k++) {
if (monitorSettingsKeys[k].startsWith(prefix)) {
matchingPrefix = prefix;
matchingSettings = this.monitorSettings[monitorSettingsKeys[k]];
break;
}
}
if (matchingPrefix.length) {
break;
}
}
return { matchingPrefix, matchingSettings };
}
}

View File

@ -1,6 +1,5 @@
import { MonitorModel } from '../../browser/monitor-model';
import { PluggableMonitorSetting } from '../../common/protocol';
import { CoreClientProvider } from '../core-client-provider';
export type PluggableMonitorSettings = Record<string, PluggableMonitorSetting>;
export interface MonitorSettings {
@ -10,10 +9,12 @@ export interface MonitorSettings {
export const MonitorSettingsProvider = Symbol('MonitorSettingsProvider');
export interface MonitorSettingsProvider {
init(
id: string,
coreClientProvider: CoreClientProvider
getSettings(
monitorId: string,
defaultSettings: PluggableMonitorSettings
): Promise<PluggableMonitorSettings>;
setSettings(
monitorId: string,
settings: PluggableMonitorSettings
): Promise<PluggableMonitorSettings>;
get(): Promise<PluggableMonitorSettings>;
set(settings: PluggableMonitorSettings): Promise<PluggableMonitorSettings>;
}