ATL-546: Added UI for settings.

Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
Akos Kitta
2021-01-23 14:57:21 +01:00
committed by Akos Kitta
parent 1742c53015
commit 1f544b2656
24 changed files with 1374 additions and 159 deletions

View File

@@ -13,6 +13,7 @@ interface ISettingsService extends grpc.ServiceDefinition<grpc.UntypedServiceImp
merge: ISettingsService_IMerge;
getValue: ISettingsService_IGetValue;
setValue: ISettingsService_ISetValue;
write: ISettingsService_IWrite;
}
interface ISettingsService_IGetAll extends grpc.MethodDefinition<settings_settings_pb.GetAllRequest, settings_settings_pb.RawData> {
@@ -51,6 +52,15 @@ interface ISettingsService_ISetValue extends grpc.MethodDefinition<settings_sett
responseSerialize: grpc.serialize<settings_settings_pb.SetValueResponse>;
responseDeserialize: grpc.deserialize<settings_settings_pb.SetValueResponse>;
}
interface ISettingsService_IWrite extends grpc.MethodDefinition<settings_settings_pb.WriteRequest, settings_settings_pb.WriteResponse> {
path: "/cc.arduino.cli.settings.Settings/Write";
requestStream: false;
responseStream: false;
requestSerialize: grpc.serialize<settings_settings_pb.WriteRequest>;
requestDeserialize: grpc.deserialize<settings_settings_pb.WriteRequest>;
responseSerialize: grpc.serialize<settings_settings_pb.WriteResponse>;
responseDeserialize: grpc.deserialize<settings_settings_pb.WriteResponse>;
}
export const SettingsService: ISettingsService;
@@ -59,6 +69,7 @@ export interface ISettingsServer {
merge: grpc.handleUnaryCall<settings_settings_pb.RawData, settings_settings_pb.MergeResponse>;
getValue: grpc.handleUnaryCall<settings_settings_pb.GetValueRequest, settings_settings_pb.Value>;
setValue: grpc.handleUnaryCall<settings_settings_pb.Value, settings_settings_pb.SetValueResponse>;
write: grpc.handleUnaryCall<settings_settings_pb.WriteRequest, settings_settings_pb.WriteResponse>;
}
export interface ISettingsClient {
@@ -74,6 +85,9 @@ export interface ISettingsClient {
setValue(request: settings_settings_pb.Value, callback: (error: grpc.ServiceError | null, response: settings_settings_pb.SetValueResponse) => void): grpc.ClientUnaryCall;
setValue(request: settings_settings_pb.Value, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: settings_settings_pb.SetValueResponse) => void): grpc.ClientUnaryCall;
setValue(request: settings_settings_pb.Value, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: settings_settings_pb.SetValueResponse) => void): grpc.ClientUnaryCall;
write(request: settings_settings_pb.WriteRequest, callback: (error: grpc.ServiceError | null, response: settings_settings_pb.WriteResponse) => void): grpc.ClientUnaryCall;
write(request: settings_settings_pb.WriteRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: settings_settings_pb.WriteResponse) => void): grpc.ClientUnaryCall;
write(request: settings_settings_pb.WriteRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: settings_settings_pb.WriteResponse) => void): grpc.ClientUnaryCall;
}
export class SettingsClient extends grpc.Client implements ISettingsClient {
@@ -90,4 +104,7 @@ export class SettingsClient extends grpc.Client implements ISettingsClient {
public setValue(request: settings_settings_pb.Value, callback: (error: grpc.ServiceError | null, response: settings_settings_pb.SetValueResponse) => void): grpc.ClientUnaryCall;
public setValue(request: settings_settings_pb.Value, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: settings_settings_pb.SetValueResponse) => void): grpc.ClientUnaryCall;
public setValue(request: settings_settings_pb.Value, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: settings_settings_pb.SetValueResponse) => void): grpc.ClientUnaryCall;
public write(request: settings_settings_pb.WriteRequest, callback: (error: grpc.ServiceError | null, response: settings_settings_pb.WriteResponse) => void): grpc.ClientUnaryCall;
public write(request: settings_settings_pb.WriteRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: settings_settings_pb.WriteResponse) => void): grpc.ClientUnaryCall;
public write(request: settings_settings_pb.WriteRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: settings_settings_pb.WriteResponse) => void): grpc.ClientUnaryCall;
}

View File

@@ -85,6 +85,28 @@ function deserialize_cc_arduino_cli_settings_Value(buffer_arg) {
return settings_settings_pb.Value.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_cc_arduino_cli_settings_WriteRequest(arg) {
if (!(arg instanceof settings_settings_pb.WriteRequest)) {
throw new Error('Expected argument of type cc.arduino.cli.settings.WriteRequest');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_cc_arduino_cli_settings_WriteRequest(buffer_arg) {
return settings_settings_pb.WriteRequest.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_cc_arduino_cli_settings_WriteResponse(arg) {
if (!(arg instanceof settings_settings_pb.WriteResponse)) {
throw new Error('Expected argument of type cc.arduino.cli.settings.WriteResponse');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_cc_arduino_cli_settings_WriteResponse(buffer_arg) {
return settings_settings_pb.WriteResponse.deserializeBinary(new Uint8Array(buffer_arg));
}
// The Settings service provides an interface to Arduino CLI's configuration
// options
@@ -137,5 +159,17 @@ setValue: {
responseSerialize: serialize_cc_arduino_cli_settings_SetValueResponse,
responseDeserialize: deserialize_cc_arduino_cli_settings_SetValueResponse,
},
// Writes to file settings currently stored in memory
write: {
path: '/cc.arduino.cli.settings.Settings/Write',
requestStream: false,
responseStream: false,
requestType: settings_settings_pb.WriteRequest,
responseType: settings_settings_pb.WriteResponse,
requestSerialize: serialize_cc_arduino_cli_settings_WriteRequest,
requestDeserialize: deserialize_cc_arduino_cli_settings_WriteRequest,
responseSerialize: serialize_cc_arduino_cli_settings_WriteResponse,
responseDeserialize: deserialize_cc_arduino_cli_settings_WriteResponse,
},
};

View File

@@ -123,3 +123,41 @@ export namespace SetValueResponse {
export type AsObject = {
}
}
export class WriteRequest extends jspb.Message {
getFilepath(): string;
setFilepath(value: string): WriteRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): WriteRequest.AsObject;
static toObject(includeInstance: boolean, msg: WriteRequest): WriteRequest.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: WriteRequest, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): WriteRequest;
static deserializeBinaryFromReader(message: WriteRequest, reader: jspb.BinaryReader): WriteRequest;
}
export namespace WriteRequest {
export type AsObject = {
filepath: string,
}
}
export class WriteResponse extends jspb.Message {
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): WriteResponse.AsObject;
static toObject(includeInstance: boolean, msg: WriteResponse): WriteResponse.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: WriteResponse, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): WriteResponse;
static deserializeBinaryFromReader(message: WriteResponse, reader: jspb.BinaryReader): WriteResponse;
}
export namespace WriteResponse {
export type AsObject = {
}
}

View File

@@ -20,6 +20,8 @@ goog.exportSymbol('proto.cc.arduino.cli.settings.MergeResponse', null, global);
goog.exportSymbol('proto.cc.arduino.cli.settings.RawData', null, global);
goog.exportSymbol('proto.cc.arduino.cli.settings.SetValueResponse', null, global);
goog.exportSymbol('proto.cc.arduino.cli.settings.Value', null, global);
goog.exportSymbol('proto.cc.arduino.cli.settings.WriteRequest', null, global);
goog.exportSymbol('proto.cc.arduino.cli.settings.WriteResponse', null, global);
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
@@ -146,6 +148,48 @@ if (goog.DEBUG && !COMPILED) {
*/
proto.cc.arduino.cli.settings.SetValueResponse.displayName = 'proto.cc.arduino.cli.settings.SetValueResponse';
}
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
* server response, or constructed directly in Javascript. The array is used
* in place and becomes part of the constructed object. It is not cloned.
* If no data is provided, the constructed object will be empty, but still
* valid.
* @extends {jspb.Message}
* @constructor
*/
proto.cc.arduino.cli.settings.WriteRequest = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.cc.arduino.cli.settings.WriteRequest, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.cc.arduino.cli.settings.WriteRequest.displayName = 'proto.cc.arduino.cli.settings.WriteRequest';
}
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
* server response, or constructed directly in Javascript. The array is used
* in place and becomes part of the constructed object. It is not cloned.
* If no data is provided, the constructed object will be empty, but still
* valid.
* @extends {jspb.Message}
* @constructor
*/
proto.cc.arduino.cli.settings.WriteResponse = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.cc.arduino.cli.settings.WriteResponse, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.cc.arduino.cli.settings.WriteResponse.displayName = 'proto.cc.arduino.cli.settings.WriteResponse';
}
@@ -869,4 +913,235 @@ proto.cc.arduino.cli.settings.SetValueResponse.serializeBinaryToWriter = functio
};
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto.
* Field names that are reserved in JavaScript and will be renamed to pb_name.
* Optional fields that are not set will be set to undefined.
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
* For the list of reserved names please see:
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
* JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @return {!Object}
*/
proto.cc.arduino.cli.settings.WriteRequest.prototype.toObject = function(opt_includeInstance) {
return proto.cc.arduino.cli.settings.WriteRequest.toObject(opt_includeInstance, this);
};
/**
* Static version of the {@see toObject} method.
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
* the JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @param {!proto.cc.arduino.cli.settings.WriteRequest} msg The msg instance to transform.
* @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.settings.WriteRequest.toObject = function(includeInstance, msg) {
var f, obj = {
filepath: jspb.Message.getFieldWithDefault(msg, 1, "")
};
if (includeInstance) {
obj.$jspbMessageInstance = msg;
}
return obj;
};
}
/**
* Deserializes binary data (in protobuf wire format).
* @param {jspb.ByteSource} bytes The bytes to deserialize.
* @return {!proto.cc.arduino.cli.settings.WriteRequest}
*/
proto.cc.arduino.cli.settings.WriteRequest.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.cc.arduino.cli.settings.WriteRequest;
return proto.cc.arduino.cli.settings.WriteRequest.deserializeBinaryFromReader(msg, reader);
};
/**
* Deserializes binary data (in protobuf wire format) from the
* given reader into the given message object.
* @param {!proto.cc.arduino.cli.settings.WriteRequest} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.cc.arduino.cli.settings.WriteRequest}
*/
proto.cc.arduino.cli.settings.WriteRequest.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) {
if (reader.isEndGroup()) {
break;
}
var field = reader.getFieldNumber();
switch (field) {
case 1:
var value = /** @type {string} */ (reader.readString());
msg.setFilepath(value);
break;
default:
reader.skipField();
break;
}
}
return msg;
};
/**
* Serializes the message to binary data (in protobuf wire format).
* @return {!Uint8Array}
*/
proto.cc.arduino.cli.settings.WriteRequest.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.cc.arduino.cli.settings.WriteRequest.serializeBinaryToWriter(this, writer);
return writer.getResultBuffer();
};
/**
* Serializes the given message to binary data (in protobuf wire
* format), writing to the given BinaryWriter.
* @param {!proto.cc.arduino.cli.settings.WriteRequest} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.settings.WriteRequest.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getFilepath();
if (f.length > 0) {
writer.writeString(
1,
f
);
}
};
/**
* optional string filePath = 1;
* @return {string}
*/
proto.cc.arduino.cli.settings.WriteRequest.prototype.getFilepath = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
};
/**
* @param {string} value
* @return {!proto.cc.arduino.cli.settings.WriteRequest} returns this
*/
proto.cc.arduino.cli.settings.WriteRequest.prototype.setFilepath = function(value) {
return jspb.Message.setProto3StringField(this, 1, value);
};
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto.
* Field names that are reserved in JavaScript and will be renamed to pb_name.
* Optional fields that are not set will be set to undefined.
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
* For the list of reserved names please see:
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
* JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @return {!Object}
*/
proto.cc.arduino.cli.settings.WriteResponse.prototype.toObject = function(opt_includeInstance) {
return proto.cc.arduino.cli.settings.WriteResponse.toObject(opt_includeInstance, this);
};
/**
* Static version of the {@see toObject} method.
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
* the JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @param {!proto.cc.arduino.cli.settings.WriteResponse} msg The msg instance to transform.
* @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.settings.WriteResponse.toObject = function(includeInstance, msg) {
var f, obj = {
};
if (includeInstance) {
obj.$jspbMessageInstance = msg;
}
return obj;
};
}
/**
* Deserializes binary data (in protobuf wire format).
* @param {jspb.ByteSource} bytes The bytes to deserialize.
* @return {!proto.cc.arduino.cli.settings.WriteResponse}
*/
proto.cc.arduino.cli.settings.WriteResponse.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.cc.arduino.cli.settings.WriteResponse;
return proto.cc.arduino.cli.settings.WriteResponse.deserializeBinaryFromReader(msg, reader);
};
/**
* Deserializes binary data (in protobuf wire format) from the
* given reader into the given message object.
* @param {!proto.cc.arduino.cli.settings.WriteResponse} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.cc.arduino.cli.settings.WriteResponse}
*/
proto.cc.arduino.cli.settings.WriteResponse.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) {
if (reader.isEndGroup()) {
break;
}
var field = reader.getFieldNumber();
switch (field) {
default:
reader.skipField();
break;
}
}
return msg;
};
/**
* Serializes the message to binary data (in protobuf wire format).
* @return {!Uint8Array}
*/
proto.cc.arduino.cli.settings.WriteResponse.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.cc.arduino.cli.settings.WriteResponse.serializeBinaryToWriter(this, writer);
return writer.getResultBuffer();
};
/**
* Serializes the given message to binary data (in protobuf wire
* format), writing to the given BinaryWriter.
* @param {!proto.cc.arduino.cli.settings.WriteResponse} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.settings.WriteResponse.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
};
goog.object.extend(exports, proto.cc.arduino.cli.settings);

View File

@@ -12,7 +12,7 @@ import { BackendApplicationContribution } from '@theia/core/lib/node/backend-app
import { ConfigService, Config, NotificationServiceServer } from '../common/protocol';
import * as fs from './fs-extra';
import { spawnCommand } from './exec-util';
import { RawData } from './cli-protocol/settings/settings_pb';
import { RawData, WriteRequest } from './cli-protocol/settings/settings_pb';
import { SettingsClient } from './cli-protocol/settings/settings_grpc_pb';
import * as serviceGrpcPb from './cli-protocol/settings/settings_grpc_pb';
import { ConfigFileValidator } from './config-file-validator';
@@ -20,8 +20,8 @@ import { ArduinoDaemonImpl } from './arduino-daemon-impl';
import { DefaultCliConfig, CLI_CONFIG_SCHEMA_PATH, CLI_CONFIG } from './cli-config';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
import { deepClone } from '@theia/core';
const debounce = require('lodash.debounce');
const track = temp.track();
@injectable()
@@ -43,7 +43,6 @@ export class ConfigServiceImpl implements BackendApplicationContribution, Config
@inject(NotificationServiceServer)
protected readonly notificationService: NotificationServiceServer;
protected updating = false;
protected config: Config;
protected cliConfig: DefaultCliConfig | undefined;
protected ready = new Deferred<void>();
@@ -51,17 +50,16 @@ export class ConfigServiceImpl implements BackendApplicationContribution, Config
async onStart(): Promise<void> {
await this.ensureCliConfigExists();
await this.watchCliConfig();
this.cliConfig = await this.loadCliConfig();
if (this.cliConfig) {
const config = await this.mapCliConfigToAppConfig(this.cliConfig);
if (config) {
this.config = config;
this.ready.resolve();
return;
}
} else {
this.fireInvalidConfig();
}
this.fireInvalidConfig();
}
async getCliConfigFileUri(): Promise<string> {
@@ -78,6 +76,35 @@ export class ConfigServiceImpl implements BackendApplicationContribution, Config
return this.config;
}
async setConfiguration(config: Config): Promise<void> {
await this.ready.promise;
if (Config.sameAs(this.config, config)) {
return;
}
let copyDefaultCliConfig: DefaultCliConfig | undefined = deepClone(this.cliConfig);
if (!copyDefaultCliConfig) {
copyDefaultCliConfig = await this.getFallbackCliConfig();
}
const { additionalUrls, dataDirUri, downloadsDirUri, sketchDirUri } = config;
copyDefaultCliConfig.directories = {
data: FileUri.fsPath(dataDirUri),
downloads: FileUri.fsPath(downloadsDirUri),
user: FileUri.fsPath(sketchDirUri)
};
copyDefaultCliConfig.board_manager = {
additional_urls: [
...additionalUrls
]
};
const { port } = copyDefaultCliConfig.daemon;
await this.updateDaemon(port, copyDefaultCliConfig);
await this.writeDaemonState(port);
this.config = deepClone(config);
this.cliConfig = copyDefaultCliConfig;
this.fireConfigChanged(this.config);
}
get cliConfiguration(): DefaultCliConfig | undefined {
return this.cliConfig;
}
@@ -124,7 +151,7 @@ export class ConfigServiceImpl implements BackendApplicationContribution, Config
resolve(dirPath);
});
});
await spawnCommand(`"${cliPath}"`, ['config', 'init', '--dest-dir', throwawayDirPath]);
await spawnCommand(`"${cliPath}"`, ['config', 'init', '--dest-dir', `"${throwawayDirPath}"`]);
const rawYaml = await fs.readFile(path.join(throwawayDirPath, CLI_CONFIG), { encoding: 'utf-8' });
const model = yaml.safeLoad(rawYaml.trim());
return model as DefaultCliConfig;
@@ -163,63 +190,8 @@ export class ConfigServiceImpl implements BackendApplicationContribution, Config
};
}
protected async watchCliConfig(): Promise<void> {
const configDirUri = await this.getCliConfigFileUri();
const cliConfigPath = FileUri.fsPath(configDirUri);
const listener = debounce(async () => {
if (this.updating) {
return;
} else {
this.updating = true;
}
const cliConfig = await this.loadCliConfig();
// Could not parse the YAML content.
if (!cliConfig) {
this.updating = false;
this.fireInvalidConfig();
return;
}
const valid = await this.validator.validate(cliConfig);
if (!valid) {
this.updating = false;
this.fireInvalidConfig();
return;
}
const shouldUpdate = !this.cliConfig || !DefaultCliConfig.sameAs(this.cliConfig, cliConfig);
if (!shouldUpdate) {
this.fireConfigChanged(this.config);
this.updating = false;
return;
}
// We use the gRPC `Settings` API iff the `daemon.port` has not changed.
// Otherwise, we restart the daemon.
const canUpdateSettings = this.cliConfig && this.cliConfig.daemon.port === cliConfig.daemon.port;
try {
const config = await this.mapCliConfigToAppConfig(cliConfig);
const update = new Promise<void>(resolve => {
if (canUpdateSettings) {
return this.updateDaemon(cliConfig.daemon.port, cliConfig).then(resolve);
}
return this.daemon.stopDaemon()
.then(() => this.daemon.startDaemon())
.then(resolve);
})
update.then(() => {
this.cliConfig = cliConfig;
this.config = config;
this.configChangeEmitter.fire(this.config);
this.notificationService.notifyConfigChanged({ config: this.config });
}).finally(() => this.updating = false);
} catch (err) {
this.logger.error('Failed to update the daemon with the current CLI configuration.', err);
}
}, 200);
fs.watchFile(cliConfigPath, listener);
this.logger.info(`Started watching the Arduino CLI configuration: '${cliConfigPath}'.`);
}
protected fireConfigChanged(config: Config): void {
this.configChangeEmitter.fire(config);
this.notificationService.notifyConfigChanged({ config });
}
@@ -227,30 +199,51 @@ export class ConfigServiceImpl implements BackendApplicationContribution, Config
this.notificationService.notifyConfigChanged({ config: undefined });
}
protected async unwatchCliConfig(): Promise<void> {
const cliConfigFileUri = await this.getCliConfigFileUri();
const cliConfigPath = FileUri.fsPath(cliConfigFileUri);
fs.unwatchFile(cliConfigPath);
this.logger.info(`Stopped watching the Arduino CLI configuration: '${cliConfigPath}'.`);
}
protected async updateDaemon(port: string | number, config: DefaultCliConfig): Promise<void> {
// https://github.com/agreatfool/grpc_tools_node_protoc_ts/blob/master/doc/grpcjs_support.md#usage
// @ts-ignore
const SettingsClient = grpc.makeClientConstructor(serviceGrpcPb['cc.arduino.cli.settings.Settings'], 'SettingsService') as any;
const client = new SettingsClient(`localhost:${port}`, grpc.credentials.createInsecure()) as SettingsClient;
const client = this.createClient(port);
const data = new RawData();
data.setJsondata(JSON.stringify(config, null, 2));
return new Promise<void>((resolve, reject) => {
client.merge(data, error => {
if (error) {
reject(error);
return;
try {
if (error) {
reject(error);
return;
}
resolve();
} finally {
client.close();
}
client.close();
resolve();
})
});
});
}
protected async writeDaemonState(port: string | number): Promise<void> {
const client = this.createClient(port);
const req = new WriteRequest();
const cliConfigUri = await this.getCliConfigFileUri();
const cliConfigPath = FileUri.fsPath(cliConfigUri);
req.setFilepath(cliConfigPath);
return new Promise<void>((resolve, reject) => {
client.write(req, error => {
try {
if (error) {
reject(error);
return;
}
resolve();
} finally {
client.close();
}
});
});
}
private createClient(port: string | number): SettingsClient {
// https://github.com/agreatfool/grpc_tools_node_protoc_ts/blob/master/doc/grpcjs_support.md#usage
// @ts-ignore
const SettingsClient = grpc.makeClientConstructor(serviceGrpcPb['cc.arduino.cli.settings.Settings'], 'SettingsService') as any;
return new SettingsClient(`localhost:${port}`, grpc.credentials.createInsecure()) as SettingsClient;
}
}

View File

@@ -1,12 +1,12 @@
import { FileUri } from '@theia/core/lib/node/file-uri';
import { inject, injectable } from 'inversify';
import { inject, injectable, postConstruct } from 'inversify';
import { dirname } from 'path';
import { CoreService } from '../common/protocol/core-service';
import { CompileReq, CompileResp } from './cli-protocol/commands/compile_pb';
import { CoreClientProvider } from './core-client-provider';
import { UploadReq, UploadResp, BurnBootloaderReq, BurnBootloaderResp, UploadUsingProgrammerReq, UploadUsingProgrammerResp } from './cli-protocol/commands/upload_pb';
import { OutputService } from '../common/protocol/output-service';
import { NotificationServiceServer } from '../common/protocol';
import { NotificationServiceServer, ConfigService } from '../common/protocol';
import { ClientReadableStream } from '@grpc/grpc-js';
import { ArduinoCoreClient } from './cli-protocol/commands/commands_grpc_pb';
import { firstToUpperCase, firstToLowerCase } from '../common/utils';
@@ -23,16 +23,23 @@ export class CoreServiceImpl implements CoreService {
@inject(NotificationServiceServer)
protected readonly notificationService: NotificationServiceServer;
@inject(ConfigService)
protected readonly configService: ConfigService;
@postConstruct()
protected init(): void {
this.coreClient().then(({ client, instance }) => {
});
}
async compile(options: CoreService.Compile.Options): Promise<void> {
this.outputService.append({ name: 'compile', chunk: 'Compile...\n' + JSON.stringify(options, null, 2) + '\n--------------------------\n' });
const { sketchUri, fqbn } = options;
const sketchFilePath = FileUri.fsPath(sketchUri);
const sketchpath = dirname(sketchFilePath);
const coreClient = await this.coreClientProvider.client();
if (!coreClient) {
return;
}
const coreClient = await this.coreClient();
const { client, instance } = coreClient;
const compilerReq = new CompileReq();
@@ -43,7 +50,7 @@ export class CoreServiceImpl implements CoreService {
}
compilerReq.setOptimizefordebug(options.optimizeForDebug);
compilerReq.setPreprocess(false);
compilerReq.setVerbose(true);
compilerReq.setVerbose(options.verbose);
compilerReq.setQuiet(false);
const result = client.compile(compilerReq);
@@ -84,10 +91,7 @@ export class CoreServiceImpl implements CoreService {
const sketchFilePath = FileUri.fsPath(sketchUri);
const sketchpath = dirname(sketchFilePath);
const coreClient = await this.coreClientProvider.client();
if (!coreClient) {
return;
}
const coreClient = await this.coreClient();
const { client, instance } = coreClient;
const req = requestProvider();
@@ -102,6 +106,8 @@ export class CoreServiceImpl implements CoreService {
if (programmer) {
req.setProgrammer(programmer.id);
}
req.setVerbose(options.verbose);
req.setVerify(options.verify);
const result = responseHandler(client, req);
try {
@@ -121,12 +127,9 @@ export class CoreServiceImpl implements CoreService {
}
async burnBootloader(options: CoreService.Bootloader.Options): Promise<void> {
const coreClient = await this.coreClientProvider.client();
if (!coreClient) {
return;
}
const { fqbn, port, programmer } = options;
const coreClient = await this.coreClient();
const { client, instance } = coreClient;
const { fqbn, port, programmer } = options;
const burnReq = new BurnBootloaderReq();
burnReq.setInstance(instance);
if (fqbn) {
@@ -138,6 +141,8 @@ export class CoreServiceImpl implements CoreService {
if (programmer) {
burnReq.setProgrammer(programmer.id);
}
burnReq.setVerify(options.verify);
burnReq.setVerbose(options.verbose);
const result = client.burnBootloader(burnReq);
try {
await new Promise<void>((resolve, reject) => {
@@ -154,4 +159,23 @@ export class CoreServiceImpl implements CoreService {
}
}
private async coreClient(): Promise<CoreClientProvider.Client> {
const coreClient = await new Promise<CoreClientProvider.Client>(async resolve => {
const client = await this.coreClientProvider.client();
if (client) {
resolve(client);
return;
}
const toDispose = this.coreClientProvider.onClientReady(async () => {
const client = await this.coreClientProvider.client();
if (client) {
toDispose.dispose();
resolve(client);
return;
}
});
});
return coreClient;
}
}