From 7c4aab2418f34c2c29065c53372916735ec87117 Mon Sep 17 00:00:00 2001 From: Seongwon Im Date: Mon, 25 Sep 2023 14:54:15 +0900 Subject: [PATCH 1/2] [EdgeTpu] Separate ConfigObject's logic dependent on Backend by introducing ConfigSetting (#1672) * [EdgeTpu] Separate ConfigObject's logic dependent on Backend by introducing ConfigSetting This commit separate ConfigObject's logic dependent on Backend by introducing ConfigSetting - Introduce abstract class ConfigSetting for separate logic - Introduce class OneConfigSetting extends ConfigSetting for ONE Backend logic - Apply OneConfigSetting to ConfigObject - Add Test code for ConfigSetting associated code ONE-vscode-DCO-1.0-Signed-off-by: Seongwon Im Co-authored-by: SeungHyunH --- src/OneExplorer/ConfigObject.ts | 297 +++--------------- src/OneExplorer/ConfigSetting.ts | 152 +++++++++ .../ConfigSettings/OneConfigSetting.ts | 226 +++++++++++++ src/Tests/OneExplorer/ConfigObject.test.ts | 26 ++ src/Tests/OneExplorer/ConfigSetting.test.ts | 44 +++ .../ConfigSettings/OneConfigSettings.test.ts | 59 ++++ 6 files changed, 549 insertions(+), 255 deletions(-) create mode 100644 src/OneExplorer/ConfigSetting.ts create mode 100644 src/OneExplorer/ConfigSettings/OneConfigSetting.ts create mode 100644 src/Tests/OneExplorer/ConfigSetting.test.ts create mode 100644 src/Tests/OneExplorer/ConfigSettings/OneConfigSettings.test.ts diff --git a/src/OneExplorer/ConfigObject.ts b/src/OneExplorer/ConfigObject.ts index a4c8a32d..e61ef491 100644 --- a/src/OneExplorer/ConfigObject.ts +++ b/src/OneExplorer/ConfigObject.ts @@ -23,19 +23,23 @@ import * as vscode from "vscode"; import { RealPath } from "../Utils/Helpers"; import { Logger } from "../Utils/Logger"; -import { Artifact, Locator, LocatorRunner } from "./ArtifactLocator"; +import { Artifact } from "./ArtifactLocator"; +import { OneCfg, OneConfigSetting } from "./ConfigSettings/OneConfigSetting"; +import { ConfigSetting } from "./ConfigSetting"; -type Cfg = { - "one-import-tflite": CfgOneImportTflite; - "one-import-onnx": CfgOneImportOnnx; - "one-import-tf": CfgOneImportTf; -}; +/** + * Type for rawObj. + * Expand for additional Backend's section value + */ +type Cfg = OneCfg; type CfgKeys = keyof Cfg; -// TODO Update -type CfgOneImportTflite = any; -type CfgOneImportOnnx = any; -type CfgOneImportTf = any; +/** + * Enum for what type of ConfigSetting that ConfigObject use + */ +enum CfgType { + one, +} /** * @brief A helper class to get parsed artifacts (baseModels, products) @@ -66,6 +70,11 @@ export class ConfigObj { */ obj: { baseModels: Artifact[]; products: Artifact[] }; + /** + * type of config setting + */ + configType: CfgType; + get getBaseModels() { return this.obj.baseModels; } @@ -112,12 +121,31 @@ export class ConfigObj { return found ? true : false; } + /** + * @brief getter for config setting + */ + public get configSetting(): ConfigSetting { + let configSetting: ConfigSetting; + switch (this.configType) { + case CfgType.one: + default: + configSetting = new OneConfigSetting(); + } + configSetting.init(); + + return configSetting; + } + private constructor(uri: vscode.Uri, rawObj: Cfg) { this.uri = uri; this.rawObj = rawObj; + this.configType = CfgType.one; + + // TODO: separate to init() + const configSetting = this.configSetting; this.obj = { - baseModels: ConfigObj.parseBaseModels(uri.fsPath, rawObj), - products: ConfigObj.parseProducts(uri.fsPath, rawObj), + baseModels: configSetting.parseBaseModels(uri.fsPath, rawObj), + products: configSetting.parseProducts(uri.fsPath, rawObj), }; } @@ -163,11 +191,7 @@ export class ConfigObj { ): Thenable { const getSection = (name: string) => { const ext = path.extname(name); - const sections = { - ".pb": "one-import-tf", - ".tflite": "one-import-tflite", - ".onnx": "one-import-onnx", - }; + const sections = this.configSetting.sections; return sections[ext as keyof typeof sections]; }; @@ -176,6 +200,7 @@ export class ConfigObj { const kSection: CfgKeys = section as keyof Cfg; if ( + this.rawObj[kSection] && this.rawObj[kSection].input_path && this.getFullPath(this.rawObj[kSection].input_path) === oldpath ) { @@ -230,242 +255,4 @@ export class ConfigObj { // TODO check if toString() is required return ini.parse(configRaw.toString()); } - - /** - * @brief Parse base models written in the ini object and return the absolute path. - * - * @param uri cfg uri is required to calculate absolute path - * - * ABOUT MULTIPLE BASE MODELS - * - * onecc doesn't support multiple base models. - * However, OneExplorer will show the config node below multiple base models - * to prevent a case that users cannot find their faulty config files on ONE explorer. - * - * TODO Move to backend - */ - private static parseBaseModels = ( - filePath: string, - iniObj: object - ): Artifact[] => { - const dir = path.dirname(filePath); - - let locatorRunner = new LocatorRunner(); - - locatorRunner.register({ - artifactAttr: { - ext: ".tflite", - icon: new vscode.ThemeIcon("symbol-variable"), - }, - locator: new Locator((value: string) => - LocatorRunner.searchWithExt(".tflite", value) - ), - }); - - locatorRunner.register({ - artifactAttr: { - ext: ".pb", - icon: new vscode.ThemeIcon("symbol-variable"), - }, - locator: new Locator((value: string) => - LocatorRunner.searchWithExt(".pb", value) - ), - }); - - locatorRunner.register({ - artifactAttr: { - ext: ".onnx", - icon: new vscode.ThemeIcon("symbol-variable"), - }, - locator: new Locator((value: string) => - LocatorRunner.searchWithExt(".onnx", value) - ), - }); - - let artifacts: Artifact[] = locatorRunner.run(iniObj, dir); - - if (artifacts.length > 1) { - // TODO Notify the error with a better UX - // EX. put question mark next to the config icon - Logger.debug( - "OneExplorer", - `There are multiple input models in the configuration(${filePath}).` - ); - } - if (artifacts.length === 0) { - // TODO Notify the error with a better UX - // EX. showing orphan nodes somewhere - Logger.debug( - "OneExplorer", - `There is no input model in the configuration(${filePath}).` - ); - } - - // Return as list of uri - return artifacts; - }; - - /** - * @brief Find derived models written in the ini object and return the absolute path. - * - * @param filePath cfg file path is required to calculate absolute path - * - * TODO Move to backend - */ - private static parseProducts = ( - filePath: string, - iniObj: object - ): Artifact[] => { - const dir = path.dirname(filePath); - - let locatorRunner = new LocatorRunner(); - - /** - * ABOUT ORDERING - * - * The registration order determines the order in the tree view - */ - - locatorRunner.register({ - artifactAttr: { - ext: ".circle", - icon: new vscode.ThemeIcon("symbol-variable"), - openViewType: "one.viewer.circle", - }, - locator: new Locator((value: string) => - LocatorRunner.searchWithExt(".circle", value) - ), - }); - - locatorRunner.register({ - artifactAttr: { - ext: ".tvn", - icon: new vscode.ThemeIcon("symbol-variable"), - openViewType: "default", - }, - locator: new Locator((value: string) => - LocatorRunner.searchWithExt(".tvn", value) - ), - }); - - locatorRunner.register({ - artifactAttr: { - ext: ".tracealloc.json", - icon: new vscode.ThemeIcon("graph"), - openViewType: "one.viewer.mondrian", - canHide: true, - }, - locator: new Locator((value: string) => { - return LocatorRunner.searchWithExt(".tvn", value).map((filepath) => - filepath.replace(".tvn", ".tracealloc.json") - ); - }), - }); - - // NOTE - // Shows .trace.json - // REQUIRES: .tvn be written in the config file. - // This rule is added to show a trace.json file generated by `one.toolchain.profileModel` command. - locatorRunner.register({ - artifactAttr: { - ext: ".trace.json", - icon: new vscode.ThemeIcon("graph"), - openViewType: "default", - canHide: true, - }, - locator: new Locator((value: string) => { - return LocatorRunner.searchWithExt(".tvn", value).map((filepath) => - filepath.replace(".tvn", ".trace.json") - ); - }), - }); - - locatorRunner.register({ - artifactAttr: { - ext: ".json", - icon: new vscode.ThemeIcon("graph"), - openViewType: "default", - canHide: true, - }, - locator: new Locator( - (value: string) => { - return LocatorRunner.searchWithCommandOption( - value, - "--save-chrome-trace", - ".json" - ); - }, - "one-profile", - "command" - ), - }); - - locatorRunner.register({ - artifactAttr: { - ext: ".tv2m", - icon: new vscode.ThemeIcon("symbol-method"), - openViewType: "default", - canHide: true, - }, - locator: new Locator((value: string) => { - return LocatorRunner.searchWithExt(".tvn", value).map((filepath) => - filepath.replace(".tvn", ".tv2m") - ); - }), - }); - - locatorRunner.register({ - artifactAttr: { - ext: ".tv2o", - icon: new vscode.ThemeIcon("symbol-method"), - openViewType: "default", - canHide: true, - }, - locator: new Locator((value: string) => { - return LocatorRunner.searchWithExt(".tvn", value).map((filepath) => - filepath.replace(".tvn", ".tv2o") - ); - }), - }); - - locatorRunner.register({ - artifactAttr: { - ext: ".tv2w", - icon: new vscode.ThemeIcon("symbol-method"), - openViewType: "default", - canHide: true, - }, - locator: new Locator((value: string) => { - return LocatorRunner.searchWithExt(".tvn", value).map((filepath) => - filepath.replace(".tvn", ".tv2w") - ); - }), - }); - - locatorRunner.register({ - // 'default' view type is 'text editor' (vscode.openWith) - artifactAttr: { - ext: ".circle.log", - openViewType: "default", - icon: vscode.ThemeIcon.File, - canHide: true, - }, - locator: new Locator((value: string) => { - return LocatorRunner.searchWithExt(".circle", value).map((filepath) => - filepath.replace(".circle", ".circle.log") - ); - }), - }); - - /** - * When you add a new product type, please append the ext type to - * OneTreeDataProvider.fileWatcher too, to prevent a bug. - * - * TODO Provide better structure to remove this extra work - */ - - let artifacts: Artifact[] = locatorRunner.run(iniObj, dir); - - return artifacts; - }; } diff --git a/src/OneExplorer/ConfigSetting.ts b/src/OneExplorer/ConfigSetting.ts new file mode 100644 index 00000000..cc92b66f --- /dev/null +++ b/src/OneExplorer/ConfigSetting.ts @@ -0,0 +1,152 @@ +/* + * 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 path from "path"; + +import { Logger } from "../Utils/Logger"; +import { Artifact, LocatorRunner } from "./ArtifactLocator"; + +/** + * @brief A helper class for loading ConfigObject's logic dependent on the Backend. + */ +export abstract class ConfigSetting { + static backendName = "ONE"; + static ext = ".cfg"; + + baseModelsLocatorRunner: LocatorRunner; + productsLocatorRunner: LocatorRunner; + sections: { [key: string]: string }; + + constructor() { + this.baseModelsLocatorRunner = new LocatorRunner(); + this.productsLocatorRunner = new LocatorRunner(); + this.sections = { + ".pb": "one-import-tf", + ".tflite": "one-import-tflite", + ".onnx": "one-import-onnx", + }; + } + + public init(): void { + this._initBaseModelsLocatorRunner(); + this._initProductsLocatorRunner(); + } + + /** + * @brief A function for cases where output_path changes based on input_path + * + * @param newpath new input_path + * @param rawObj raw config object + * @param kSection key section to be updated + * + * Using onecc, this function do nothing. + * Using EdgeTpu compiler, output_path is determined by input_path + * @example input_path=model.tflite + * output_path=model_edgetpu.tflite + */ + public abstract updateOutPath( + newpath?: string, + rawObj?: { [key: string]: any }, + kSection?: string + ): void; + + /** + * @brief Parse base models written in the ini object and return the absolute path. + * + * @param uri cfg uri is required to calculate absolute path + * + * ABOUT MULTIPLE BASE MODELS + * + * onecc doesn't support multiple base models. + * However, OneExplorer will show the config node below multiple base models + * to prevent a case that users cannot find their faulty config files on ONE explorer. + * + * TODO Move to backend + */ + public parseBaseModels = (filePath: string, iniObj: object): Artifact[] => { + const dir = path.dirname(filePath); + + let locatorRunner = this.baseModelsLocatorRunner; + + let artifacts: Artifact[] = locatorRunner.run(iniObj, dir); + + if (artifacts.length > 1) { + // TODO Notify the error with a better UX + // EX. put question mark next to the config icon + Logger.debug( + "OneExplorer", + `There are multiple input models in the configuration(${filePath}).` + ); + } + if (artifacts.length === 0) { + // TODO Notify the error with a better UX + // EX. showing orphan nodes somewhere + Logger.debug( + "OneExplorer", + `There is no input model in the configuration(${filePath}).` + ); + } + + // Return as list of uri + return artifacts; + }; + + /** + * @brief Find derived models written in the ini object and return the absolute path. + * + * @param filePath cfg file path is required to calculate absolute path + * + * TODO Move to backend + */ + public parseProducts = (filePath: string, iniObj: object): Artifact[] => { + const dir = path.dirname(filePath); + + let locatorRunner = this.productsLocatorRunner; + + let artifacts: Artifact[] = locatorRunner.run(iniObj, dir); + + return artifacts; + }; + + /** + * @brief init baseModelsLocatorRunner + * + * This method register list of ArtifactLocator parsed as baseModel + */ + protected abstract _initBaseModelsLocatorRunner(): void; + + /** + * @brief init baseModelsLocatorRunner + * + * This method register list of ArtifactLocator parsed as baseModel + */ + protected abstract _initProductsLocatorRunner(): void; +} + +/** + * @brief Example class extends ConfigSetting + */ +export class ConfigSettingBase extends ConfigSetting { + public updateOutPath(): void { + throw new Error("Method not implemented."); + } + protected _initBaseModelsLocatorRunner(): void { + throw new Error("Method not implemented."); + } + protected _initProductsLocatorRunner(): void { + throw new Error("Method not implemented."); + } +} diff --git a/src/OneExplorer/ConfigSettings/OneConfigSetting.ts b/src/OneExplorer/ConfigSettings/OneConfigSetting.ts new file mode 100644 index 00000000..1cb0b7c2 --- /dev/null +++ b/src/OneExplorer/ConfigSettings/OneConfigSetting.ts @@ -0,0 +1,226 @@ +/* + * 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 vscode from "vscode"; + +import { Locator, LocatorRunner } from "../ArtifactLocator"; +import { ConfigSetting } from "../ConfigSetting"; + +export type OneCfg = { + "one-import-tflite": CfgOneImportTflite; + "one-import-onnx": CfgOneImportOnnx; + "one-import-tf": CfgOneImportTf; +}; + +// TODO Update +type CfgOneImportTflite = any; +type CfgOneImportOnnx = any; +type CfgOneImportTf = any; + +/** + * @brief A class for loading ConfigObject logic of ONE Backend. + */ +export class OneConfigSetting extends ConfigSetting { + /** + * On ONE Backend, this method does nothing. + * @returns void + */ + public updateOutPath(): void { + return; + } + + protected _initBaseModelsLocatorRunner() { + let locatorRunner = new LocatorRunner(); + + locatorRunner.register({ + artifactAttr: { + ext: ".tflite", + icon: new vscode.ThemeIcon("symbol-variable"), + }, + locator: new Locator((value: string) => { + return LocatorRunner.searchWithExt(".tflite", value); + }), + }); + + locatorRunner.register({ + artifactAttr: { + ext: ".pb", + icon: new vscode.ThemeIcon("symbol-variable"), + }, + locator: new Locator((value: string) => + LocatorRunner.searchWithExt(".pb", value) + ), + }); + + locatorRunner.register({ + artifactAttr: { + ext: ".onnx", + icon: new vscode.ThemeIcon("symbol-variable"), + }, + locator: new Locator((value: string) => + LocatorRunner.searchWithExt(".onnx", value) + ), + }); + + this.baseModelsLocatorRunner = locatorRunner; + } + + protected _initProductsLocatorRunner() { + let locatorRunner = new LocatorRunner(); + + /** + * ABOUT ORDERING + * + * The registration order determines the order in the tree view + */ + + locatorRunner.register({ + artifactAttr: { + ext: ".circle", + icon: new vscode.ThemeIcon("symbol-variable"), + openViewType: "one.viewer.circle", + }, + locator: new Locator((value: string) => + LocatorRunner.searchWithExt(".circle", value) + ), + }); + + locatorRunner.register({ + artifactAttr: { + ext: ".tvn", + icon: new vscode.ThemeIcon("symbol-variable"), + }, + locator: new Locator((value: string) => + LocatorRunner.searchWithExt(".tvn", value) + ), + }); + + locatorRunner.register({ + artifactAttr: { + ext: ".tracealloc.json", + icon: new vscode.ThemeIcon("graph"), + openViewType: "one.viewer.mondrian", + canHide: true, + }, + locator: new Locator((value: string) => { + return LocatorRunner.searchWithExt(".tvn", value).map((filepath) => + filepath.replace(".tvn", ".tracealloc.json") + ); + }), + }); + + // NOTE + // Shows .trace.json + // REQUIRES: .tvn be written in the config file. + // This rule is added to show a trace.json file generated by `one.toolchain.profileModel` command. + locatorRunner.register({ + artifactAttr: { + ext: ".trace.json", + icon: new vscode.ThemeIcon("graph"), + openViewType: "one.editor.jsonTracer", + canHide: true, + }, + locator: new Locator((value: string) => { + return LocatorRunner.searchWithExt(".tvn", value).map((filepath) => + filepath.replace(".tvn", ".trace.json") + ); + }), + }); + + locatorRunner.register({ + artifactAttr: { + ext: ".json", + icon: new vscode.ThemeIcon("graph"), + openViewType: "one.editor.jsonTracer", + canHide: true, + }, + locator: new Locator( + (value: string) => { + return LocatorRunner.searchWithCommandOption( + value, + "--save-chrome-trace", + ".json" + ); + }, + "one-profile", + "command" + ), + }); + + locatorRunner.register({ + artifactAttr: { + ext: ".tv2m", + icon: new vscode.ThemeIcon("symbol-method"), + canHide: true, + }, + locator: new Locator((value: string) => { + return LocatorRunner.searchWithExt(".tvn", value).map((filepath) => + filepath.replace(".tvn", ".tv2m") + ); + }), + }); + + locatorRunner.register({ + artifactAttr: { + ext: ".tv2o", + icon: new vscode.ThemeIcon("symbol-method"), + canHide: true, + }, + locator: new Locator((value: string) => { + return LocatorRunner.searchWithExt(".tvn", value).map((filepath) => + filepath.replace(".tvn", ".tv2o") + ); + }), + }); + + locatorRunner.register({ + artifactAttr: { + ext: ".tv2w", + icon: new vscode.ThemeIcon("symbol-method"), + canHide: true, + }, + locator: new Locator((value: string) => { + return LocatorRunner.searchWithExt(".tvn", value).map((filepath) => + filepath.replace(".tvn", ".tv2w") + ); + }), + }); + + locatorRunner.register({ + // 'default' view type is 'text editor' (vscode.openWith) + artifactAttr: { + ext: ".circle.log", + openViewType: "default", + icon: vscode.ThemeIcon.File, + canHide: true, + }, + locator: new Locator((value: string) => { + return LocatorRunner.searchWithExt(".circle", value).map((filepath) => + filepath.replace(".circle", ".circle.log") + ); + }), + }); + + /** + * When you add a new product type, please append the ext type to + * OneTreeDataProvider.fileWatcher too, to prevent a bug. + * + * TODO Provide better structure to remove this extra work + */ + + this.productsLocatorRunner = locatorRunner; + } +} diff --git a/src/Tests/OneExplorer/ConfigObject.test.ts b/src/Tests/OneExplorer/ConfigObject.test.ts index 41f564c4..62c20ea0 100644 --- a/src/Tests/OneExplorer/ConfigObject.test.ts +++ b/src/Tests/OneExplorer/ConfigObject.test.ts @@ -19,6 +19,7 @@ import * as vscode from "vscode"; import { ConfigObj } from "../../OneExplorer/ConfigObject"; import { TestBuilder } from "../TestBuilder"; +import { OneConfigSetting } from "../../OneExplorer/ConfigSettings/OneConfigSetting"; suite("OneExplorer", function () { suite("ConfigObject", function () { @@ -77,6 +78,31 @@ suite("OneExplorer", function () { }); }); + suite("#configSetting", function () { + test("Get one config setting with .cfg file", function () { + const configName = "model.cfg"; + + const content = ` +`; + + // Write a file inside temp directory + testBuilder.writeFileSync(configName, content); + + // Get file paths inside the temp directory + const configPath = testBuilder.getPath(configName); + + const configObj = ConfigObj.createConfigObj( + vscode.Uri.file(configPath) + ); + + // Validate + { + assert.isDefined(configObj); + assert.isTrue(configObj!.configSetting instanceof OneConfigSetting); + } + }); + }); + suite("#one-import-onnx section", function () { test("Parse basic example with one-import-onnx", function () { // Write a file inside temp directory diff --git a/src/Tests/OneExplorer/ConfigSetting.test.ts b/src/Tests/OneExplorer/ConfigSetting.test.ts new file mode 100644 index 00000000..4b6a1cde --- /dev/null +++ b/src/Tests/OneExplorer/ConfigSetting.test.ts @@ -0,0 +1,44 @@ +/* + * 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 { assert } from "chai"; + +import { ConfigSettingBase } from "../../OneExplorer/ConfigSetting"; + +suite("OneExplorer", function () { + suite("ConfigSettingBase", function () { + suite("#constructor()", function () { + test("Create dummy config setting", function () { + const instance = new ConfigSettingBase(); + assert.isTrue(instance instanceof ConfigSettingBase); + }); + }); + + suite("#init()", function () { + test("NEG: throw in dummy config setting base by init", function () { + const configSetting = new ConfigSettingBase(); + assert.throw(() => configSetting.init()); + }); + }); + + suite("#updateOutPath()", function () { + test("NEG: throw in dummy config setting base by updateOutPath", function () { + const configSetting = new ConfigSettingBase(); + assert.throw(() => configSetting.updateOutPath()); + }); + }); + }); +}); diff --git a/src/Tests/OneExplorer/ConfigSettings/OneConfigSettings.test.ts b/src/Tests/OneExplorer/ConfigSettings/OneConfigSettings.test.ts new file mode 100644 index 00000000..25b2680d --- /dev/null +++ b/src/Tests/OneExplorer/ConfigSettings/OneConfigSettings.test.ts @@ -0,0 +1,59 @@ +/* + * 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 { assert } from "chai"; + +import { LocatorRunner } from "../../../OneExplorer/ArtifactLocator"; +import { OneConfigSetting } from "../../../OneExplorer/ConfigSettings/OneConfigSetting"; + +suite("OneExplorer", function () { + suite("ConfigSettings", function () { + suite("OneConfigSetting", function () { + suite("#constructor", function () { + test("Create one config setting", function () { + assert.doesNotThrow(() => new OneConfigSetting()); + + const configSetting = new OneConfigSetting(); + assert.isTrue(configSetting instanceof OneConfigSetting); + }); + }); + + suite("#init()", function () { + test("init one config setting", function () { + const configSetting = new OneConfigSetting(); + assert.isTrue(configSetting instanceof OneConfigSetting); + + assert.doesNotThrow(() => configSetting.init()); + assert.isTrue( + configSetting.baseModelsLocatorRunner instanceof LocatorRunner + ); + assert.isTrue( + configSetting.productsLocatorRunner instanceof LocatorRunner + ); + }); + }); + + suite("#updateOutPath()", function () { + test("one config setting does not change output_path", function () { + const configSetting = new OneConfigSetting(); + assert.isTrue(configSetting instanceof OneConfigSetting); + + assert.doesNotThrow(() => configSetting.updateOutPath()); + }); + }); + }); + }); +}); From dcd11c3c0cfeebde7ba583c5fa98d49008cf2dd5 Mon Sep 17 00:00:00 2001 From: Seongwon Im Date: Tue, 26 Sep 2023 13:45:36 +0900 Subject: [PATCH 2/2] [EdgeTpu] Introduce EdgeTpuConfigSetting (#1682) * [EdgeTpu] Introduce EdgeTpuConfigSetting This commit introduce EdgeTpuConfigSetting that has ConfigObject logic for EdgeTpu Backend - Introduce class EdgeTpuConfigSetting extends ConfigSetting for EdgeTpu Backend logic - Apply EdgeTpuConfigSetting to ConfigObject - Add Test code for EdgeTpuConfigSetting ONE-vscode-DCO-1.0-Signed-off-by: Seongwon Im Co-authored-by: : SeungHyunH --- src/OneExplorer/ConfigObject.ts | 26 +++- .../ConfigSettings/EdgeTpuConfigSetting.ts | 126 ++++++++++++++++++ src/Tests/OneExplorer/ConfigObject.test.ts | 26 ++++ .../EdgeTpuConfigSetting.test.ts | 74 ++++++++++ 4 files changed, 249 insertions(+), 3 deletions(-) create mode 100644 src/OneExplorer/ConfigSettings/EdgeTpuConfigSetting.ts create mode 100644 src/Tests/OneExplorer/ConfigSettings/EdgeTpuConfigSetting.test.ts diff --git a/src/OneExplorer/ConfigObject.ts b/src/OneExplorer/ConfigObject.ts index e61ef491..d0425ff6 100644 --- a/src/OneExplorer/ConfigObject.ts +++ b/src/OneExplorer/ConfigObject.ts @@ -24,14 +24,18 @@ import { RealPath } from "../Utils/Helpers"; import { Logger } from "../Utils/Logger"; import { Artifact } from "./ArtifactLocator"; -import { OneCfg, OneConfigSetting } from "./ConfigSettings/OneConfigSetting"; import { ConfigSetting } from "./ConfigSetting"; +import { OneCfg, OneConfigSetting } from "./ConfigSettings/OneConfigSetting"; +import { + EdgeTpuCfg, + EdgeTpuConfigSetting, +} from "./ConfigSettings/EdgeTpuConfigSetting"; /** * Type for rawObj. * Expand for additional Backend's section value */ -type Cfg = OneCfg; +type Cfg = OneCfg & EdgeTpuCfg; type CfgKeys = keyof Cfg; /** @@ -39,6 +43,7 @@ type CfgKeys = keyof Cfg; */ enum CfgType { one, + edgeTpu, } /** @@ -127,6 +132,9 @@ export class ConfigObj { public get configSetting(): ConfigSetting { let configSetting: ConfigSetting; switch (this.configType) { + case CfgType.edgeTpu: + configSetting = new EdgeTpuConfigSetting(); + break; case CfgType.one: default: configSetting = new OneConfigSetting(); @@ -139,7 +147,15 @@ export class ConfigObj { private constructor(uri: vscode.Uri, rawObj: Cfg) { this.uri = uri; this.rawObj = rawObj; - this.configType = CfgType.one; + const ext = path.extname(uri.fsPath); + switch (ext) { + case EdgeTpuConfigSetting.ext: + this.configType = CfgType.edgeTpu; + break; + case OneConfigSetting.ext: + default: + this.configType = CfgType.one; + } // TODO: separate to init() const configSetting = this.configSetting; @@ -212,6 +228,10 @@ export class ConfigObj { ); } + // EdgeTpu Compiler's output_path is fixed + // ex) `input_path`_edgetpu.tflite + this.configSetting.updateOutPath(newpath, this.rawObj, kSection); + return vscode.workspace.fs.writeFile( this.uri, new TextEncoder().encode(ini.stringify(this.rawObj)) diff --git a/src/OneExplorer/ConfigSettings/EdgeTpuConfigSetting.ts b/src/OneExplorer/ConfigSettings/EdgeTpuConfigSetting.ts new file mode 100644 index 00000000..9e1db287 --- /dev/null +++ b/src/OneExplorer/ConfigSettings/EdgeTpuConfigSetting.ts @@ -0,0 +1,126 @@ +/* + * 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 vscode from "vscode"; +import * as path from "path"; + +import { Locator, LocatorRunner } from "../ArtifactLocator"; +import { ConfigSetting } from "../ConfigSetting"; + +export type EdgeTpuCfg = { + "edgetpu-compile": any; +}; + +export class EdgeTpuConfigSetting extends ConfigSetting { + static backendName = "EdgeTPU"; + static ext = ".edgetpucfg"; + + constructor() { + super(); + this.sections = { + ".tflite": "edgetpu-compile", + }; + } + + /** + * Add postfix(_edgetpu) to file name of new input path + * @returns void + */ + public updateOutPath( + newpath: string, + rawObj: { [key: string]: any }, + kSection: string + ): void { + const ext = path.extname(newpath); + const name = path.basename(newpath, ext) + "_edgetpu" + ext; + const dir = path.dirname(newpath); + const outpath = path.join(dir, name); + if (rawObj[kSection]) { + rawObj[kSection].output_path = outpath; + } + } + + protected _initBaseModelsLocatorRunner() { + let locatorRunner = new LocatorRunner(); + + locatorRunner.register({ + artifactAttr: { + ext: ".tflite", + icon: new vscode.ThemeIcon("symbol-variable"), + }, + locator: new Locator((value: string) => { + value += ""; + const filterd = value + .split(" ") + .filter((val) => !val.endsWith("_edgetpu.tflite")); + value = filterd.join(" "); + return LocatorRunner.searchWithExt(".tflite", value); + }), + }); + + this.baseModelsLocatorRunner = locatorRunner; + } + + protected _initProductsLocatorRunner() { + let locatorRunner = new LocatorRunner(); + + /** + * ABOUT ORDERING + * + * The registration order determines the order in the tree view + */ + + // NOTE + // Shows _edgetpu.tflite + // _edgetpu.tflite generated by .tflite is product type + locatorRunner.register({ + artifactAttr: { + ext: ".tflite", + icon: new vscode.ThemeIcon("symbol-variable"), + }, + locator: new Locator((value: string) => { + value += ""; + const filterd = value + .split(" ") + .filter((val) => val.endsWith("_edgetpu.tflite")); + value = filterd.join(" "); + return LocatorRunner.searchWithExt(".tflite", value); + }), + }); + + locatorRunner.register({ + // 'default' view type is 'text editor' (vscode.openWith) + artifactAttr: { + ext: ".log", + openViewType: "default", + icon: vscode.ThemeIcon.File, + canHide: true, + }, + locator: new Locator((value: string) => { + value += ""; + const filterd = value + .split(" ") + .filter((val) => val.endsWith("_edgetpu.tflite")); + value = filterd.join(" "); + return LocatorRunner.searchWithExt(".tflite", value).map((filepath) => + filepath.replace(".tflite", ".log") + ); + }), + }); + + this.productsLocatorRunner = locatorRunner; + } +} diff --git a/src/Tests/OneExplorer/ConfigObject.test.ts b/src/Tests/OneExplorer/ConfigObject.test.ts index 62c20ea0..3d2a9f43 100644 --- a/src/Tests/OneExplorer/ConfigObject.test.ts +++ b/src/Tests/OneExplorer/ConfigObject.test.ts @@ -20,6 +20,7 @@ import { ConfigObj } from "../../OneExplorer/ConfigObject"; import { TestBuilder } from "../TestBuilder"; import { OneConfigSetting } from "../../OneExplorer/ConfigSettings/OneConfigSetting"; +import { EdgeTpuConfigSetting } from "../../OneExplorer/ConfigSettings/EdgeTpuConfigSetting"; suite("OneExplorer", function () { suite("ConfigObject", function () { @@ -101,6 +102,31 @@ suite("OneExplorer", function () { assert.isTrue(configObj!.configSetting instanceof OneConfigSetting); } }); + + test("Get edge tpu config setting with .edgetpucfg file", function () { + const configName = "model.edgetpucfg"; + + const content = ` +`; + + // Write a file inside temp directory + testBuilder.writeFileSync(configName, content); + + // Get file paths inside the temp directory + const configPath = testBuilder.getPath(configName); + + const configObj = ConfigObj.createConfigObj( + vscode.Uri.file(configPath) + ); + + // Validate + { + assert.isDefined(configObj); + assert.isTrue( + configObj!.configSetting instanceof EdgeTpuConfigSetting + ); + } + }); }); suite("#one-import-onnx section", function () { diff --git a/src/Tests/OneExplorer/ConfigSettings/EdgeTpuConfigSetting.test.ts b/src/Tests/OneExplorer/ConfigSettings/EdgeTpuConfigSetting.test.ts new file mode 100644 index 00000000..06ab51ad --- /dev/null +++ b/src/Tests/OneExplorer/ConfigSettings/EdgeTpuConfigSetting.test.ts @@ -0,0 +1,74 @@ +/* + * 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 { assert } from "chai"; +import * as ini from "ini"; + +import { LocatorRunner } from "../../../OneExplorer/ArtifactLocator"; +import { EdgeTpuConfigSetting } from "../../../OneExplorer/ConfigSettings/EdgeTpuConfigSetting"; + +suite("OneExplorer", function () { + suite("ConfigSettings", function () { + suite("EdgeTpuConfigSetting", function () { + suite("#constructor", function () { + test("Create one config setting", function () { + assert.doesNotThrow(() => new EdgeTpuConfigSetting()); + + const configSetting = new EdgeTpuConfigSetting(); + assert.isTrue(configSetting instanceof EdgeTpuConfigSetting); + }); + }); + + suite("#init()", function () { + test("init one config setting", function () { + const configSetting = new EdgeTpuConfigSetting(); + assert.isTrue(configSetting instanceof EdgeTpuConfigSetting); + + assert.doesNotThrow(() => configSetting.init()); + assert.isTrue( + configSetting.baseModelsLocatorRunner instanceof LocatorRunner + ); + assert.isTrue( + configSetting.productsLocatorRunner instanceof LocatorRunner + ); + }); + }); + + suite("#updateOutPath()", function () { + test("edgetpu config setting change output_path by adding postfix(_edgetpu) to new_path", function () { + const inputPath = "model.tflite"; + const outputPath = "model_edgetpu.tflite"; + const kSection = "edgetpu-compile"; + const edgetpucfg = ` +[edgetpu-compile] +input_path=${inputPath} + `; + const rawObj = ini.parse(edgetpucfg); + + const configSetting = new EdgeTpuConfigSetting(); + assert.isTrue(configSetting instanceof EdgeTpuConfigSetting); + + assert.doesNotThrow(() => + configSetting.updateOutPath(inputPath, rawObj, kSection) + ); + console.log(rawObj); + assert.isDefined(rawObj["edgetpu-compile"].output_path); + assert.equal(outputPath, rawObj["edgetpu-compile"].output_path); + }); + }); + }); + }); +});