Skip to content

Commit

Permalink
[Execute] Revisit executeQuickInput file (#971)
Browse files Browse the repository at this point in the history
* [Execute] Revisit executeQuickInput file

Let's revisit executeQuickInput file.

ONE-vscode-DCO-1.0-Signed-off-by: Yongseop Kim <[email protected]>

* Revise more

ONE-vscode-DCO-1.0-Signed-off-by: Yongseop Kim <[email protected]>

* Introduce InferenceQuickInput, InferenceRunner and InferenceShowBox

ONE-vscode-DCO-1.0-Signed-off-by: Yongseop Kim <<[email protected]>>

* Prepare tests

ONE-vscode-DCO-1.0-Signed-off-by: Yongseop Kim <<[email protected]>>

* fix

ONE-vscode-DCO-1.0-Signed-off-by: Yongseop Kim <<[email protected]>>

* Fill tests of InferenceQuickInput

ONE-vscode-DCO-1.0-Signed-off-by: Yongseop Kim <<[email protected]>>

* Fill tests of InferenceRunner

ONE-vscode-DCO-1.0-Signed-off-by: Yongseop Kim <<[email protected]>>

* Revert commandPalette option

ONE-vscode-DCO-1.0-Signed-off-by: Yongseop Kim <<[email protected]>>
  • Loading branch information
YongseopKim authored Jul 8, 2022
1 parent d9739d3 commit 0f0530d
Show file tree
Hide file tree
Showing 7 changed files with 604 additions and 147 deletions.
161 changes: 161 additions & 0 deletions src/Execute/InferenceQuickInput.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* Copyright (c) 2022 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 {Backend} from '../Backend/API';
import {globalBackendMap} from '../Backend/Backend';
import {Executor} from '../Backend/Executor';
import {Logger} from '../Utils/Logger';
import {MultiStepInput} from '../Utils/MultiStepInput';

const logTag = 'InferenceQuickInput';

interface State {
selectedItem: vscode.QuickPickItem;
}

class InferenceQuickInput {
backend: Backend|undefined = undefined;
modelPath: vscode.Uri|undefined = undefined;
inputSpec: string|undefined = undefined;
error: string|undefined = undefined;

constructor() {}

getBackend(): Backend {
if (this.error !== undefined || this.backend === undefined) {
throw new Error('wrong calling');
}
return this.backend as Backend;
}

getModelPath(): vscode.Uri {
if (this.error !== undefined || this.modelPath === undefined) {
throw new Error('wrong calling');
}
return this.modelPath as vscode.Uri;
}

getInputSpec(): string {
if (this.error !== undefined || this.inputSpec === undefined) {
throw new Error('wrong calling');
}
return this.inputSpec as string;
}

getError(): string|undefined {
return this.error;
}

async shouldResume(): Promise<boolean> {
// Could show a notification with the option to resume.
return new Promise<boolean>(
(resolve, reject) => {
// noop
});
}

getAllBackendNames(): string[] {
return Object.keys(globalBackendMap);
}

getQuickPickItems(items: string[]): vscode.QuickPickItem[] {
return items.map(label => ({label}));
}

getBackendFromGlobal(key: string): Backend {
return globalBackendMap[key];
}

async pickBackend(input: MultiStepInput, state: Partial<State>) {
const items = this.getQuickPickItems(this.getAllBackendNames());
state.selectedItem = await input.showQuickPick({
title: 'Choose Executor Toolchain',
step: 1,
totalSteps: 3,
placeholder: 'Select a Backend',
items: items,
shouldResume: this.shouldResume
});

this.backend = this.getBackendFromGlobal(state.selectedItem.label);

if (this.backend === undefined) {
this.error = 'Backend to infer is not chosen. Please check once again.';
return undefined;
}
if (this.backend.executor() === undefined) {
this.error = 'Backend executor is not set yet. Please check once again.';
return undefined;
}
}

getFilter(): {[name: string]: string[]} {
const backend: Backend = this.backend as Backend;
const executor = backend.executor() as Executor;
// List files which are filtered with executable extensions
return {backendName: executor.getExecutableExt()};
}

// TODO: Use quickPick window with fast grep child process
async selectInputModel(input: MultiStepInput, state: Partial<State>) {
const filter = this.getFilter();

const fileUri = await vscode.window.showOpenDialog({
title: 'Select Model to Infer',
canSelectMany: false,
openLabel: 'Select Model to Infer',
filters: filter
});

if (fileUri && fileUri[0]) {
this.modelPath = fileUri[0];
return;
}

Logger.warn(logTag, 'No model has been selected');
this.error = 'No model has been selected. Please check once again.';
return undefined;
}

getInputSpecKeys(): string[] {
return ['any', 'non-zero', 'positive'];
}

// TODO: enable the backend-driven option steps by backend extension
async selectInputSpec(input: MultiStepInput, state: Partial<State>): Promise<void> {
const items = this.getQuickPickItems(this.getInputSpecKeys());
state.selectedItem = await input.showQuickPick({
title: 'Enter Backend Specific Options',
step: 3,
totalSteps: 3,
placeholder: 'Select Random Input Spec',
items: items,
shouldResume: this.shouldResume
});
this.inputSpec = state.selectedItem.label;
}

async collectInputs(): Promise<void> {
const state = {selectedItem: undefined} as Partial<State>;
await MultiStepInput.run(input => this.pickBackend(input, state));
await MultiStepInput.run(input => this.selectInputModel(input, state));
await MultiStepInput.run(input => this.selectInputSpec(input, state));
}
}

export {State, InferenceQuickInput};
80 changes: 80 additions & 0 deletions src/Execute/InferenceRunner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (c) 2022 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 {exec} from 'child_process';
import {appendFileSync} from 'fs';
import * as vscode from 'vscode';

import {Backend} from '../Backend/API';
import {Command} from '../Backend/Command';
import {Executor} from '../Backend/Executor';
import {Logger} from '../Utils/Logger';

import {InfernceShowBox, Task} from './InferenceShowBox';

const logTag = 'InferenceRunner';

class InferenceRunner {
backend: Backend;
modelPath: vscode.Uri;
inputSpec: string;

constructor(backend: Backend, modelPath: vscode.Uri, inputSpec: string) {
this.backend = backend;
this.modelPath = modelPath;
this.inputSpec = inputSpec;
}

getInferenceCmd(): Command {
const executor = this.backend.executor() as Executor;
return executor.runInference(this.modelPath.path, ['--input-spec', this.inputSpec]);
}

getOutFileName(): string {
return `${this.modelPath.path}.infer.log`;
}

// TODO: It's possible divide to inferance and others
getInferenceTask(cmd: Command, outFileName: string): Task {
return (resolve, reject) => {
exec(cmd.str() + ' > ' + outFileName, (error, stdout, stderr) => {
if (error) {
appendFileSync(outFileName, error.message);
return reject(error.message);
}
// Some of warnings are treated as error.
// TODO: handle the stderr
// if (stderr) return reject(stderr);
else {
return resolve(stdout);
}
});
};
}

async runInference(): Promise<void> {
const cmd = this.getInferenceCmd();
const outFileName = this.getOutFileName();
const task = this.getInferenceTask(cmd, outFileName);

const showBox = new InfernceShowBox();
await showBox.showInference(
task, `Inference succeeded! You can find the log at ${outFileName}`,
`Exception Occurred! You can find the log at ${outFileName}`);
}
}

export {InferenceRunner};
52 changes: 52 additions & 0 deletions src/Execute/InferenceShowBox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2022 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 {Logger} from '../Utils/Logger';

type Task = (resolve: (value: unknown) => void, reject: (reason?: any) => void) => unknown;

const logTag = 'InfernceShowBox';

class InfernceShowBox {
constructor() {}

private async showProgressWith(task: Task): Promise<void> {
await vscode.window.withProgress(
{
location: vscode.ProgressLocation.Notification,
title: 'Inference Running!',
cancellable: true
},
(progress, token) => {
token.onCancellationRequested(() => {
Logger.info(logTag, 'User canceled the log running operation');
});
return new Promise((resolve, reject) => {
return task(resolve, reject);
});
});
}

async showInference(task: Task, successMsg: string, failedMsg: string) {
this.showProgressWith(task).then(
() => vscode.window.showInformationMessage(successMsg, {title: 'OK'})),
// TODO: Find if the message includes new line character.
() => vscode.window.showErrorMessage(failedMsg, {title: 'Close'});
}
}

export {Task, InfernceShowBox};
Loading

0 comments on commit 0f0530d

Please sign in to comment.