diff --git a/media/VisqRunner/index.html b/media/VisqRunner/index.html new file mode 100644 index 00000000..93bffe98 --- /dev/null +++ b/media/VisqRunner/index.html @@ -0,0 +1,133 @@ + + + + + + + + + VISQ configuration runner + + + + + + +
+ Run visq on the following configuration + + + + Run configuration + + + +
+ +
+
+ Input +
+
+
+ + Float model + + + Path to the fp32 circle model. + + + + + + Quantized model + + + Path to the quantized circle model. + + + + + + h5 data file + + + Path to the data used for inference. Random data will be used if this option is not given. + + + + +
+
+ +
+
+ Output +
+
+ MPEIR + + + + Path to the output json file (qerror metric = MPEIR). + + + + + MSE + + + + Path to the output json file (qerror metric = MSE). + + + + + TAE + + + + Path to the output json file (qerror metric = TAE). + + + + + SRMSE + + + + Path to the output json file (qerror metric = SRMSE). + + + + +
+
+ +
+
+ Options +
+
+ Dump dot graph +
+
+
+ + diff --git a/media/VisqRunner/index.js b/media/VisqRunner/index.js new file mode 100644 index 00000000..74ba7c4b --- /dev/null +++ b/media/VisqRunner/index.js @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const vscode = acquireVsCodeApi(); + +// Just like a regular webpage we need to wait for the webview +// DOM to load before we can reference any of the HTML elements +// or toolkit components +window.addEventListener("load", main); + +function main() { + register(); + + window.addEventListener("message", (event) => { + const message = event.data; + switch (message.type) { + case "displayCFG": + displayCFGToEditor(message.content); + break; + default: + break; + } + }); + + vscode.postMessage({ type: "requestDisplayCFG" }); +} + +function register() { + registerMainControls(); +} + +function registerMainControls() { + { + document + .getElementById("float-file") + .addEventListener("click", function () { + vscode.postMessage({ + type: "loadFloatInputFile", + }); + }); + + document + .getElementById("quantized-file") + .addEventListener("click", function () { + vscode.postMessage({ + type: "loadQuantizedInputFile", + }); + }); + + document.getElementById("h5-file").addEventListener("click", function () { + vscode.postMessage({ + type: "loadH5DataFile", + }); + }); + } + + { + document.getElementById("MPEIR-on").addEventListener("click", function () { + vscode.postMessage({ + type: "toggleMPEIROutputFile", + on: document.getElementById("MPEIR-on").checked, + }); + }); + document + .getElementById("mpeir-file") + .addEventListener("click", function () { + vscode.postMessage({ + type: "loadMPEIROutputFile", + }); + }); + document + .getElementById("MPEIROutputPath") + .addEventListener("input", function () { + vscode.postMessage({ + type: "onMPEIRChanged", + path: document.getElementById("MPEIROutputPath").value, + }); + }); + } + + { + document.getElementById("MSE-on").addEventListener("click", function () { + vscode.postMessage({ + type: "toggleMSEOutputFile", + on: document.getElementById("MSE-on").checked, + }); + }); + + document.getElementById("mse-file").addEventListener("click", function () { + vscode.postMessage({ + type: "loadMSEInputFile", + }); + }); + + document + .getElementById("MSEOutputPath") + .addEventListener("input", function () { + vscode.postMessage({ + type: "onMSEChanged", + path: document.getElementById("MSEOutputPath").value, + }); + }); + } + + { + document.getElementById("TAE-on").addEventListener("click", function () { + vscode.postMessage({ + type: "toggleTAEOutputFile", + on: document.getElementById("TAE-on").checked, + }); + }); + document.getElementById("tae-file").addEventListener("click", function () { + vscode.postMessage({ + type: "loadTAEOutputFile", + }); + }); + document + .getElementById("TAEOutputPath") + .addEventListener("input", function () { + vscode.postMessage({ + type: "onTAEChanged", + path: document.getElementById("TAEOutputPath").value, + }); + }); + } + + { + document.getElementById("SRMSE-on").addEventListener("click", function () { + vscode.postMessage({ + type: "toggleSRMSEOutputFile", + on: document.getElementById("SRMSE-on").checked, + }); + }); + document + .getElementById("srmse-file") + .addEventListener("click", function () { + vscode.postMessage({ + type: "loadSRMSEOutputFile", + }); + }); + document + .getElementById("SRMSEOutputPath") + .addEventListener("input", function () { + vscode.postMessage({ + type: "onSRMSEChanged", + path: document.getElementById("SRMSEOutputPath").value, + }); + }); + } + + document.getElementById("DumpDot").addEventListener("click", function () { + vscode.postMessage({ + type: "dumpDot", + dump: document.getElementById("DumpDot").checked, + }); + }); + + document.getElementById("RunCfg").addEventListener("click", function () { + vscode.postMessage({ + type: "runCfg", + }); + }); +} + +function displayCFGToEditor(cfg) { + document.getElementById("FloatModelInputPath").value = cfg?.["fp32_circle"]; + document.getElementById("QuantizedModelInputPath").value = cfg?.["q_circle"]; + document.getElementById("DataInputPath").value = cfg?.["data"]; + document.getElementById("MPEIR-on").checked = cfg?.["mpeir_on"]; + document.getElementById("MPEIROutputPath").value = cfg?.["mpeir_output"]; + document.getElementById("MSE-on").checked = cfg?.["mse_on"]; + document.getElementById("MSEOutputPath").value = cfg?.["mse_output"]; + document.getElementById("TAE-on").checked = cfg?.["tae_on"]; + document.getElementById("TAEOutputPath").value = cfg?.["tae_output"]; + document.getElementById("SRMSE-on").checked = cfg?.["srmse_on"]; + document.getElementById("SRMSEOutputPath").value = cfg?.["srmse_output"]; + document.getElementById("DumpDot").checked = cfg?.["dump_dot_graph"]; +} diff --git a/media/VisqRunner/style.css b/media/VisqRunner/style.css new file mode 100644 index 00000000..38204c07 --- /dev/null +++ b/media/VisqRunner/style.css @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +label { + white-space: pre-wrap; +} + +body { + min-width: 500px; + width: auto !important; +} + +.maintitle { + width: 100%; + display: flex; +} + +.maintitle .text { + float: left; + display: block; + font-size: medium; + font-weight: 600; + padding: 2px 0 0 5px; +} + +.title { + font-size: medium; +} + +.codicon.codicon-question .help, +.codicon.codicon-play .help { + visibility: hidden; + width: auto; + background-color: var(--vscode-editorSuggestWidget-background); + color: var(--vscode-editorSuggestWidget-foreground); + border: 2px solid var(--vscode-editorSuggestWidget-border); + font-size: small; + text-align: left; + position: absolute; + z-index: 1; + margin-left: 10px; + padding: 6px 6px 10px 8px; + font-family: var(--vscode-font-family); +} + +.codicon.codicon-question:hover .help { + visibility: visible; +} + +.codicon.codicon-play:hover .help { + visibility: visible; +} + +.input { + display: block; + margin-bottom: 5px; +} + +.sections { + display: inline-block; +} + +.output_grid { + display: inline-grid; + grid-template-columns: auto auto; + row-gap: 5px; +} diff --git a/package.json b/package.json index ff229e69..4480ac63 100644 --- a/package.json +++ b/package.json @@ -128,6 +128,16 @@ } ], "priority": "default" + }, + { + "viewType": "one.editor.cfgvisq", + "displayName": "VISQ Runner", + "selector": [ + { + "filenamePattern": "*.cfg.visq.json" + } + ], + "priority": "default" } ], "commands": [ @@ -256,6 +266,11 @@ "command": "one.editor.cfg.setDefaultValues", "title": "ONE: Set Default Values", "category": "ONE" + }, + { + "command": "one.editor.visqcfg.showFromDefaultExplorer", + "title": "Create VISQ data", + "category": "ONE" } ], "keybindings": [ @@ -370,6 +385,10 @@ "command": "one.viewer.metadata.showFromDefaultExplorer", "when": "resourceExtname in one.metadata.supportedFiles && !explorerResourceIsFolder", "group": "7_metadata@1" + }, + { + "command": "one.editor.visqcfg.showFromDefaultExplorer", + "group": "7_metadata@2" } ], "commandPalette": [ @@ -452,6 +471,10 @@ { "command": "one.viewer.metadata.showFromOneExplorer", "when": "false" + }, + { + "command": "one.editor.visqcfg.showFromDefaultExplorer", + "when": "false" } ] }, diff --git a/src/Job/Job.ts b/src/Job/Job.ts index 67ea1e3e..bb41a40a 100644 --- a/src/Job/Job.ts +++ b/src/Job/Job.ts @@ -48,5 +48,6 @@ export const enum JobType { tPrerequisites, tInstall, tUninstall, + tVisq, // TODO add more } diff --git a/src/VisqRunner/VisqRunProvider.ts b/src/VisqRunner/VisqRunProvider.ts new file mode 100644 index 00000000..41c0291b --- /dev/null +++ b/src/VisqRunner/VisqRunProvider.ts @@ -0,0 +1,753 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as fs from "fs"; +import * as path from "path"; +import * as vscode from "vscode"; + +import { Logger } from "../Utils/Logger"; +import { getNonce } from "../Utils/external/Nonce"; +import { getUri } from "../Utils/external/Uri"; +import { JobRunner } from "../Job/JobRunner"; +import { Command } from "../Backend/Command"; +import { WorkJobs } from "../Job/WorkJobs"; +import { JobCommand } from "../Job/JobCommand"; +import { JobType } from "../Job/Job"; +import { Balloon } from "../Utils/Balloon"; + +class VisqCommand extends JobCommand { + constructor(cmd: Command) { + super(cmd); + this.jobType = JobType.tVisq; + this.name = "visq"; + this.notiTitle = "Running visq..."; + this.valid = true; + } +} + +class VISQCfgData { + private _content: any; + + static floatModelKey: string = "fp32_circle"; + static quantizedModelKey: string = "q_circle"; + static dataKey: string = "data"; + static mpeirOutputKey: string = "mpeir_output"; + static mseOutputKey: string = "mse_output"; + static taeOutputKey: string = "tae_output"; + static srmseOutputKey: string = "srmse_output"; + static dumpDotKey: string = "dump_dot_graph"; + static mpeirOnKey: string = "mpeir_on"; + static mseOnKey: string = "mse_on"; + static taeOnKey: string = "tae_on"; + static srmseOnKey: string = "srmse_on"; + + static keys: string[] = [ + VISQCfgData.floatModelKey, + VISQCfgData.quantizedModelKey, + VISQCfgData.dataKey, + ]; + + static metricKeys: { [metric: string]: any } = { + mpeir: [VISQCfgData.mpeirOnKey, VISQCfgData.mpeirOutputKey], + mse: [VISQCfgData.mseOnKey, VISQCfgData.mseOutputKey], + tae: [VISQCfgData.taeOnKey, VISQCfgData.taeOutputKey], + srmse: [VISQCfgData.srmseOnKey, VISQCfgData.srmseOutputKey], + }; + + constructor() { + this._content = {}; + } + + getAsString(): string { + return JSON.stringify(this._content, null, " "); + } + + setWithString(text: string) { + this._content = JSON.parse(text); + } + + getToolArgs(): string[] { + let args: string[] = []; + // set inputs + for (let key of VISQCfgData.keys) { + if (key in this._content) { + args.push("--" + key); + args.push(this._content[key]); + } + } + + { + for (let metric in VISQCfgData.metricKeys) { + const onKey = VISQCfgData.metricKeys[metric][0]; + const outputKey = VISQCfgData.metricKeys[metric][1]; + if ( + onKey in this._content && + this._content[onKey] && + outputKey in this._content + ) { + args.push("--" + outputKey); + args.push(this._content[outputKey]); + } + } + } + + // set options + if ( + VISQCfgData.dumpDotKey in this._content && + this._content[VISQCfgData.dumpDotKey] + ) { + args.push("--" + VISQCfgData.dumpDotKey); + } + + return args; + } + + getOutputFiles(): string[] { + let outputs: string[] = []; + for (let metric in VISQCfgData.metricKeys) { + const onKey = VISQCfgData.metricKeys[metric][0]; + const outputKey = VISQCfgData.metricKeys[metric][1]; + if ( + onKey in this._content && + this._content[onKey] && + outputKey in this._content + ) { + outputs.push(this._content[outputKey]); + } + } + + return outputs; + } + + setFloatModel(model: string): void { + this._content[VISQCfgData.floatModelKey] = model; + } + + setQuantizedModel(model: string): void { + this._content[VISQCfgData.quantizedModelKey] = model; + } + + setH5Data(data: string): void { + this._content[VISQCfgData.dataKey] = data; + } + + setMPEIROutputPath(path: string): void { + this._content[VISQCfgData.mpeirOutputKey] = path; + } + + setMSEOutputPath(path: string): void { + this._content[VISQCfgData.mseOutputKey] = path; + } + + setTAEOutputPath(path: string): void { + this._content[VISQCfgData.taeOutputKey] = path; + } + + setSRMSEOutputPath(path: string): void { + this._content[VISQCfgData.srmseOutputKey] = path; + } + + setDumpDot(dump: boolean): void { + this._content[VISQCfgData.dumpDotKey] = dump; + } + + setMPEIROn(on: boolean): void { + this._content[VISQCfgData.mpeirOnKey] = on; + } + + setMSEOn(on: boolean): void { + this._content[VISQCfgData.mseOnKey] = on; + } + + setTAEOn(on: boolean): void { + this._content[VISQCfgData.taeOnKey] = on; + } + + setSRMSEOn(on: boolean): void { + this._content[VISQCfgData.srmseOnKey] = on; + } +} + +export class VISQRunProvider implements vscode.CustomTextEditorProvider { + public static readonly viewType = "one.editor.cfgvisq"; + public static readonly fileExtension = ".cfg.visq.json"; + + private _disposables: vscode.Disposable[] = []; + private _cfgDataMap: any = {}; + + public static register(context: vscode.ExtensionContext): void { + const provider = new VISQRunProvider(context); + const registrations = [ + vscode.window.registerCustomEditorProvider( + VISQRunProvider.viewType, + provider, + { + webviewOptions: { retainContextWhenHidden: true }, + } + ), + // Add command registration here + vscode.commands.registerCommand( + "one.editor.visqcfg.showFromDefaultExplorer", + (uri, selected) => { + // parse selection to get float model, quantized model, data path + let circleModels: string[] = []; + let h5Data: string = ""; + for (let uri of selected) { + const extName = path.parse(uri.path).ext.slice(1); + if (extName === "circle") { + circleModels.push(uri.path); + } else if (extName === "h5") { + h5Data = uri.path; + } + } + if (circleModels.length < 1 || circleModels.length > 2) { + // TODO + return; + } + let floatModel = circleModels[0]; + let quantizedModel = + circleModels.length === 2 ? circleModels[1] : undefined; + if (quantizedModel !== undefined) { + if ( + floatModel.toLowerCase().includes(".q8") || + floatModel.toLowerCase().includes(".q16") + ) { + [floatModel, quantizedModel] = [quantizedModel, floatModel]; + } + } + const dirPath = path.parse(floatModel).dir; + const modelName = path.parse(floatModel).name; + const extName = path.parse(floatModel).ext.slice(1); + + let defData = new VISQCfgData(); + defData.setFloatModel(floatModel); + if (quantizedModel !== undefined) { + defData.setQuantizedModel(quantizedModel); + } + if (h5Data.length > 0) { + defData.setH5Data(h5Data); + } + let msePath: string = path.join( + dirPath, + modelName + ".mse.visq.json" + ); + defData.setMSEOutputPath(msePath); + defData.setMSEOn(true); + + let mpeirPath: string = path.join( + dirPath, + modelName + ".mpeir.visq.json" + ); + defData.setMPEIROutputPath(mpeirPath); + defData.setMPEIROn(true); + + let taePath: string = path.join( + dirPath, + modelName + ".tae.visq.json" + ); + defData.setTAEOutputPath(taePath); + defData.setTAEOn(true); + + let srmsePath: string = path.join( + dirPath, + modelName + ".srmse.visq.json" + ); + defData.setSRMSEOutputPath(srmsePath); + defData.setSRMSEOn(true); + + defData.setDumpDot(false); + + const content = defData.getAsString(); + + const findInputPath = (name: string): string => { + const maxIters = 5; + for (let i = 0; i < maxIters; i++) { + const mpqPath: string = path.join( + dirPath, + name + VISQRunProvider.fileExtension + ); + if (!fs.existsSync(mpqPath)) { + return name + VISQRunProvider.fileExtension; + } + name = name + "(1)"; + } + return ""; + }; + + let cfgName = findInputPath(modelName); + if (cfgName.length < 1) { + // failed to find valid name, just revert to initial version + cfgName = modelName + VISQRunProvider.fileExtension; + } + + const validateInputPath = (name: string): string | undefined => { + const cfgPath: string = path.join(dirPath, name); + if (!cfgPath.endsWith(VISQRunProvider.fileExtension)) { + return ( + "A file extension must be " + VISQRunProvider.fileExtension + ); + } + if (fs.existsSync(cfgPath)) { + return `A file or folder ${cfgPath} already exists at this location. Please choose a different name.`; + } + }; + + vscode.window + .showInputBox({ + title: `Create visq run configuration for '${modelName}.${extName}' :`, + placeHolder: `Enter a file name`, + value: cfgName, + valueSelection: [ + 0, + cfgName.length - `${VISQRunProvider.fileExtension}`.length, + ], + validateInput: validateInputPath, + }) + .then((value) => { + if (!value) { + Logger.debug("VISQRunProvider", "User hit the escape key!"); + return; + } + + // 'uri' path is not occupied, assured by validateInputPath + const uri = vscode.Uri.file(`${dirPath}/${value}`); + const edit = new vscode.WorkspaceEdit(); + edit.createFile(uri); + edit.insert(uri, new vscode.Position(0, 0), content); + vscode.workspace.applyEdit(edit).then((isSuccess) => { + if (isSuccess) { + vscode.workspace.openTextDocument(uri).then((document) => { + document.save(); + vscode.commands.executeCommand( + "vscode.openWith", + uri, + VISQRunProvider.viewType + ); + }); + } else { + Logger.error( + "VisqCfgRunner", + "CreateVisqCfg", + `Failed to create the file ${uri}` + ); + } + }); + }); + } + ), + ]; + + registrations.forEach((disposable) => + context.subscriptions.push(disposable) + ); + } + + constructor(private readonly context: vscode.ExtensionContext) {} + + public async resolveCustomTextEditor( + document: vscode.TextDocument, + webviewPanel: vscode.WebviewPanel, + _token: vscode.CancellationToken + ): Promise { + this._cfgDataMap[document.uri.toString()] = new VISQCfgData(); + await this.initWebview(document, webviewPanel); + this.initWebviewPanel(document, webviewPanel); + this.updateWebview(document, webviewPanel.webview); + } + + private async initWebview( + document: vscode.TextDocument, + webviewPanel: vscode.WebviewPanel + ): Promise { + let webview: vscode.Webview = webviewPanel.webview; + + webview.options = { + enableScripts: true, + }; + + const nonce = getNonce(); + const scriptUri = getUri(webview, this.context.extensionUri, [ + "media", + "VisqRunner", + "index.js", + ]); + const styleUri = getUri(webview, this.context.extensionUri, [ + "media", + "VisqRunner", + "style.css", + ]); + const codiconUri = getUri(webview, this.context.extensionUri, [ + "node_modules", + "@vscode", + "codicons", + "dist", + "codicon.css", + ]); + const toolkitUri = getUri(webview, this.context.extensionUri, [ + "node_modules", + "@vscode", + "webview-ui-toolkit", + "dist", + "toolkit.js", + ]); + const htmlUri = vscode.Uri.joinPath( + this.context.extensionUri, + "media/VisqRunner/index.html" + ); + let html = Buffer.from( + await vscode.workspace.fs.readFile(htmlUri) + ).toString(); + html = html.replace(/\${nonce}/g, `${nonce}`); + html = html.replace(/\${webview.cspSource}/g, `${webview.cspSource}`); + html = html.replace(/\${scriptUri}/g, `${scriptUri}`); + html = html.replace(/\${toolkitUri}/g, `${toolkitUri}`); + html = html.replace(/\${cssUri}/g, `${styleUri}`); + html = html.replace(/\${codiconUri}/g, `${codiconUri}`); + webview.html = html; + + // Receive message from the webview. + webview.onDidReceiveMessage((e) => { + switch (e.type) { + case "requestDisplayCFG": + this.updateWebview(document, webview); + break; + case "loadFloatInputFile": + this.handleLoadInputFile(true, document); + break; + case "loadQuantizedInputFile": + this.handleLoadInputFile(false, document); + break; + case "loadH5DataFile": + this.handleLoadH5DataFile(document); + break; + case "loadMPEIROutputFile": + this.handleLoadMPEIRInputFile(document); + break; + case "loadMSEOutputFile": + this.handleLoadMSEInputFile(document); + break; + case "loadTAEOutputFile": + this.handleLoadTAEInputFile(document); + break; + case "loadSRMSEOutputFile": + this.handleLoadSRMSEInputFile(document); + break; + case "dumpDot": + this.handleDumpDotToggled(e.dump, document); + break; + case "runCfg": + this.handleRunCfg(document); + break; + case "toggleMPEIROutputFile": + this.handleMPEIRToggle(e.on, document); + break; + case "toggleMSEOutputFile": + this.handleMSEToggle(e.on, document); + break; + case "toggleTAEOutputFile": + this.handleTAEToggle(e.on, document); + break; + case "toggleSRMSEOutputFile": + this.handleSRMSEToggle(e.on, document); + break; + case "onMPEIRChanged": + this.handleMPEIRChanged(e.path, document); + break; + case "onMSEChanged": + this.handleMSEChanged(e.path, document); + break; + case "onTAEChanged": + this.handleTAEChanged(e.path, document); + break; + case "onSRMSEChanged": + this.handleSRMSEhanged(e.path, document); + break; + default: + break; + } + }); + } + + private handleLoadInputFile(float: boolean, document: vscode.TextDocument) { + let filters = {}; + if (float) { + filters = { "target files": ["circle"] }; + } else { + filters = { "target files": ["q8.circle", "q16.circle", "circle"] }; + } + + const dialogOptions = { + canSelectMany: false, + canSelectFolders: false, + openLabel: "Open", + filters: filters, + }; + + vscode.window.showOpenDialog(dialogOptions).then((fileUri) => { + if (fileUri && fileUri[0]) { + const path = fileUri[0].fsPath.toString(); + + let docUri = document.uri.toString(); + if (float) { + this._cfgDataMap[docUri].setFloatModel(path); + } else { + this._cfgDataMap[docUri].setQuantizedModel(path); + } + this.updateDocument(document); + } + }); + } + + private handleLoadH5DataFile(document: vscode.TextDocument) { + const dialogOptions = { + canSelectMany: false, + canSelectFolders: false, + openLabel: "Open", + filters: { "target files": ["h5"] }, + }; + + vscode.window.showOpenDialog(dialogOptions).then((fileUri) => { + if (fileUri && fileUri[0]) { + const path = fileUri[0].fsPath.toString(); + this._cfgDataMap[document.uri.toString()].setH5Data(path); + this.updateDocument(document); + } + }); + } + + private handleLoadMPEIRInputFile(document: vscode.TextDocument) { + const dialogOptions = { + canSelectMany: false, + canSelectFolders: false, + openLabel: "Open", + filters: { "target files": ["mpeir.visq.json"] }, + }; + + vscode.window.showOpenDialog(dialogOptions).then((fileUri) => { + if (fileUri && fileUri[0]) { + const path = fileUri[0].fsPath.toString(); + this._cfgDataMap[document.uri.toString()].setMPEIROutputPath(path); + this.updateDocument(document); + } + }); + } + + private handleLoadMSEInputFile(document: vscode.TextDocument) { + const dialogOptions = { + canSelectMany: false, + canSelectFolders: false, + openLabel: "Open", + filters: { "target files": ["mse.visq.json"] }, + }; + + vscode.window.showOpenDialog(dialogOptions).then((fileUri) => { + if (fileUri && fileUri[0]) { + const path = fileUri[0].fsPath.toString(); + this._cfgDataMap[document.uri.toString()].setMSEOutputPath(path); + this.updateDocument(document); + } + }); + } + + private handleLoadTAEInputFile(document: vscode.TextDocument) { + const dialogOptions = { + canSelectMany: false, + canSelectFolders: false, + openLabel: "Open", + filters: { "target files": ["tae.visq.json"] }, + }; + + vscode.window.showOpenDialog(dialogOptions).then((fileUri) => { + if (fileUri && fileUri[0]) { + const path = fileUri[0].fsPath.toString(); + this._cfgDataMap[document.uri.toString()].setTAEOutputPath(path); + this.updateDocument(document); + } + }); + } + + private handleLoadSRMSEInputFile(document: vscode.TextDocument) { + const dialogOptions = { + canSelectMany: false, + canSelectFolders: false, + openLabel: "Open", + filters: { "target files": ["mse.visq.json"] }, + }; + + vscode.window.showOpenDialog(dialogOptions).then((fileUri) => { + if (fileUri && fileUri[0]) { + const path = fileUri[0].fsPath.toString(); + this._cfgDataMap[document.uri.toString()].setSRMSEOutputPath(path); + this.updateDocument(document); + } + }); + } + + private handleRunCfg(document: vscode.TextDocument) { + // as for now installed visq doesn't work because it lacks DotBuilder.py + // which is not included into installation + const tool = + "/home/stanislav/repos/ONE_f/ONE/build/release_build_4/bin/visq"; //"/usr/share/one/bin/visq"; + const cmd = new Command(tool); + const toolargs: string[] = + this._cfgDataMap[document.uri.toString()].getToolArgs(); + toolargs.forEach((option) => { + cmd.push(option); + }); + let visqJob = new VisqCommand(cmd); + visqJob.successCallback = () => { + Balloon.info("Running visq has succeeded."); + const outputs: string[] = + this._cfgDataMap[document.uri.toString()].getOutputFiles(); + for (let output of outputs) { + let uri: vscode.Uri = vscode.Uri.file(output); + vscode.commands.executeCommand("vscode.open", uri); + } + }; + + visqJob.failureCallback = () => { + Balloon.error("Running visq has failed."); + }; + + let jobs = new WorkJobs(visqJob); + let runner = new JobRunner(); + runner.start(jobs); + } + + private handleDumpDotToggled(dump: boolean, document: vscode.TextDocument) { + this._cfgDataMap[document.uri.toString()].setDumpDot(dump); + this.updateDocument(document); + } + + private handleMPEIRToggle(on: boolean, document: vscode.TextDocument) { + this._cfgDataMap[document.uri.toString()].setMPEIROn(on); + this.updateDocument(document); + } + + private handleMSEToggle(on: boolean, document: vscode.TextDocument) { + this._cfgDataMap[document.uri.toString()].setMSEOn(on); + this.updateDocument(document); + } + + private handleTAEToggle(on: boolean, document: vscode.TextDocument) { + this._cfgDataMap[document.uri.toString()].setTAEOn(on); + this.updateDocument(document); + } + + private handleSRMSEToggle(on: boolean, document: vscode.TextDocument) { + this._cfgDataMap[document.uri.toString()].setSRMSEOn(on); + this.updateDocument(document); + } + + private handleMPEIRChanged(path: string, document: vscode.TextDocument) { + this._cfgDataMap[document.uri.toString()].setMPEIROutputPath(path); + this.updateDocument(document); + } + + private handleMSEChanged(path: string, document: vscode.TextDocument) { + this._cfgDataMap[document.uri.toString()].setMSEOutputPath(path); + this.updateDocument(document); + } + + private handleTAEChanged(path: string, document: vscode.TextDocument) { + this._cfgDataMap[document.uri.toString()].setTAEOutputPath(path); + this.updateDocument(document); + } + + private handleSRMSEhanged(path: string, document: vscode.TextDocument) { + this._cfgDataMap[document.uri.toString()].setSRMSEOutputPath(path); + this.updateDocument(document); + } + + private updateDocument(document: vscode.TextDocument) { + if ( + this._cfgDataMap[document.uri.toString()].getAsString() !== + document.getText() + ) { + // TODO Optimize this to modify only changed lines + const edit = new vscode.WorkspaceEdit(); + edit.replace( + document.uri, + new vscode.Range(0, 0, document.lineCount, 0), + this._cfgDataMap[document.uri.toString()].getAsString() + ); + vscode.workspace.applyEdit(edit); + } + } + + private initWebviewPanel( + document: vscode.TextDocument, + webviewPanel: vscode.WebviewPanel + ): void { + vscode.commands.executeCommand( + "setContext", + VISQRunProvider.viewType, + true + ); + + const changeDocumentSubscription = vscode.workspace.onDidChangeTextDocument( + (e) => { + if ( + e.contentChanges.length > 0 && + e.document.uri.toString() === document.uri.toString() + ) { + this.updateWebview(document, webviewPanel.webview); + } + } + ); + + webviewPanel.onDidChangeViewState( + () => { + vscode.commands.executeCommand( + "setContext", + VISQRunProvider.viewType, + webviewPanel.visible + ); + }, + null, + this._disposables + ); + + webviewPanel.onDidDispose(() => { + changeDocumentSubscription.dispose(); + while (this._disposables.length) { + const x = this._disposables.pop(); + if (x) { + x.dispose(); + } + } + vscode.commands.executeCommand( + "setContext", + VISQRunProvider.viewType, + false + ); + }); + } + + private updateWebview( + document: vscode.TextDocument, + webview: vscode.Webview + ): void { + this._cfgDataMap[document.uri.toString()].setWithString(document.getText()); + const content = JSON.parse(document.getText()); + if (content !== undefined) { + webview.postMessage({ + type: "displayCFG", + content: content, + }); + } + } +} diff --git a/src/extension.ts b/src/extension.ts index ad6c476e..19a31c4a 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -30,6 +30,7 @@ import { PartGraphSelPanel } from "./PartEditor/PartGraphSelector"; import { ToolchainProvider } from "./Toolchain/ToolchainProvider"; import { Logger } from "./Utils/Logger"; import { VisqViewerProvider } from "./Visquv/VisqViewer"; +import { VISQRunProvider } from "./VisqRunner/VisqRunProvider"; /* istanbul ignore next */ export function activate(context: vscode.ExtensionContext) { @@ -77,6 +78,8 @@ export function activate(context: vscode.ExtensionContext) { MetadataViewerProvider.register(context); + VISQRunProvider.register(context); + // returning backend registration function that will be called by backend extensions return backendRegistrationApi(); }