Skip to content

Commit

Permalink
Merge pull request #3 from jan-dolejsi/master
Browse files Browse the repository at this point in the history
- v2.11.8
  • Loading branch information
haz committed Jul 6, 2019
2 parents 349211a + 329149a commit 585b2f0
Show file tree
Hide file tree
Showing 19 changed files with 483 additions and 166 deletions.
17 changes: 16 additions & 1 deletion client/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# PDDL support - What's new?

## [2.11.8] - 2019-07-05

### Fixes

- Activating the extension upon the `pddl.downloadVal` command.
- Improved Valstep error reporting.
- Instantaneous actions visualized correctly in object swim-lanes.
- Valstep error repro export uses full valstep path rather than relying on valstep in the `%path%`. Thanks, Christian.

### New Features

- Added configuration for asynchronous planning services exposing a `/request` RESTful interface. Configuration may be retrieved from a `*.plannerConfiguration.json` or a `.json` file.
- Tooltip on plan visualization plan selection bars now explain that the size of the bar correspond to the given plan metric value.

## [2.11.7] - 2019-06-28

### Preview of VAL tools download
Expand Down Expand Up @@ -638,7 +652,8 @@ Note for open source contributors: all notable changes to the "pddl" extension w

Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.

[Unreleased]: https://github.com/jan-dolejsi/vscode-pddl/compare/v2.11.7...HEAD
[Unreleased]: https://github.com/jan-dolejsi/vscode-pddl/compare/v2.11.8...HEAD
[2.11.8]:https://github.com/jan-dolejsi/vscode-pddl/compare/v2.11.7...v2.11.8
[2.11.7]:https://github.com/jan-dolejsi/vscode-pddl/compare/v2.11.5...v2.11.7
[2.11.5]:https://github.com/jan-dolejsi/vscode-pddl/compare/v2.11.4...v2.11.5
[2.11.4]:https://github.com/jan-dolejsi/vscode-pddl/compare/v2.11.3...v2.11.4
Expand Down
3 changes: 2 additions & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Planning Domain Description Language support",
"author": "Jan Dolejsi",
"license": "MIT",
"version": "2.11.7",
"version": "2.11.8",
"publisher": "jan-dolejsi",
"engines": {
"vscode": "^1.31.0",
Expand Down Expand Up @@ -38,6 +38,7 @@
"onCommand:pddl.showOverview",
"onCommand:pddl.searchDebugger.start",
"onCommand:pddl.planning.domains.session.load",
"onCommand:pddl.downloadVal",
"workspaceContains:.planning.domains.session.json",
"onView:pddl.planning.domains",
"onView:pddl.tests.explorer",
Expand Down
4 changes: 2 additions & 2 deletions client/publish.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ call vsce package
:: https://code.visualstudio.com/docs/editor/extension-gallery#_install-from-a-vsix
echo Installing the extension locally...
::major minor patch
call code --install-extension pddl-2.11.7.vsix
call code --install-extension pddl-2.11.8.vsix
echo Test extension before you continue
pause
call vsce publish --packagePath pddl-2.11.7.vsix
call vsce publish --packagePath pddl-2.11.8.vsix
35 changes: 0 additions & 35 deletions client/src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,34 +264,6 @@ export class PddlConfiguration {
return newPlannerOptions;
}

NO_OPTIONS: OptionsQuickPickItem = { label: 'No options.', options: '', description: '' };
optionsHistory: OptionsQuickPickItem[] = [ this.NO_OPTIONS, { label: 'Specify options...', newValue: true, options: '', description: '' }];

async getPlannerOptions() {
let optionsSelected = await vscode.window.showQuickPick(this.optionsHistory,
{ placeHolder: 'Optionally specify planner switches or press ENTER to use default planner configuration.' });

if (!optionsSelected) { return null; } // operation canceled by the user by pressing Escape
else if (optionsSelected.newValue) {
let optionsEntered = await vscode.window.showInputBox({ placeHolder: 'Specify planner options.' });
if (!optionsEntered) { return null; }
optionsSelected = { label: optionsEntered, options: optionsEntered, description: '' };
}
else if (optionsSelected !== this.NO_OPTIONS) {
// a previous option was selected - lets allow the user to edit it before continuing
let optionsEntered = await vscode.window.showInputBox({value: optionsSelected.options, placeHolder: 'Specify planner options.', prompt: 'Adjust the options, if needed and press Enter to continue.'});
if (!optionsEntered) { return null; } // canceled by the user
optionsSelected = { label: optionsEntered, options: optionsEntered, description: '' };
}

let indexOf = this.optionsHistory.findIndex(option => option.options === optionsSelected.options);
if (indexOf > -1) {
this.optionsHistory.splice(indexOf, 1);
}
this.optionsHistory.unshift(optionsSelected); // insert to the first position
return optionsSelected.options;
}

getPlannerSyntax(): string {
return vscode.workspace.getConfiguration().get(PLANNER_EXECUTABLE_OPTIONS);
}
Expand Down Expand Up @@ -428,10 +400,3 @@ class ScopeQuickPickItem implements vscode.QuickPickItem {
target: vscode.ConfigurationTarget;
uri?: vscode.Uri;
}

class OptionsQuickPickItem implements vscode.QuickPickItem {
label: string;
description: string;
options: string;
newValue?: boolean;
}
28 changes: 17 additions & 11 deletions client/src/debugger/ValStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import * as process from 'child_process';
import { EventEmitter } from 'events';

import { Util } from '../../../common/src/util';
import * as atmp from '../../../common/src/asynctmp';
import * as afs from '../../../common/src/asyncfs';
import * as path from 'path';
import { TimedVariableValue, VariableValue, ProblemInfo, DomainInfo, Parser } from '../../../common/src/parser';
import { Happening } from '../../../common/src/HappeningsInfo';
import { HappeningsToValStep } from '../diagnostics/HappeningsToValStep';
import { Uri } from 'vscode';

/**
* Wraps the Valstep executable.
Expand Down Expand Up @@ -85,7 +85,7 @@ export class ValStep extends EventEmitter {

try {
if (!child.stdin.write(valSteps)) {
reject('Cannot post happenings to valstep');return;
reject('Failed to post happenings to valstep'); return;
}
if (this.verbose) {
console.log("ValStep >>>" + valSteps);
Expand All @@ -95,7 +95,7 @@ export class ValStep extends EventEmitter {
if (this.verbose) {
console.log("ValStep input causing error: " + valSteps);
}
reject('Cannot post happenings to valstep: ' + err);return;
reject('Sending happenings to valstep caused error: ' + err); return;
}
}

Expand Down Expand Up @@ -295,18 +295,24 @@ export class ValStep extends EventEmitter {
return !this.initialValues.some(value2 => value1.sameValue(value2));
}

static async storeError(err: ValStepError): Promise<string> {
let tempDir = await atmp.dir(0o644);
static async storeError(err: ValStepError, targetDirectory: Uri, valStepPath: string): Promise<string> {
let targetDir = targetDirectory.fsPath;
let caseDir = 'valstep-' + new Date().toISOString().split(':').join('-');
let casePath = path.join(targetDir, caseDir);
afs.mkdirIfDoesNotExist(casePath, 0o644);

const domainFile = "domain.pddl";
const problemFile = "problem.pddl";
const inputFile = "happenings.valsteps";
await afs.writeFile(path.join(tempDir, domainFile), err.domain.getText(), {encoding: "utf-8"});
await afs.writeFile(path.join(tempDir, problemFile), err.problem.getText(), {encoding: "utf-8"});
await afs.writeFile(path.join(tempDir, inputFile), err.valStepInput, {encoding: "utf-8"});
await afs.writeFile(path.join(casePath, domainFile), err.domain.getText(), { encoding: "utf-8" });
await afs.writeFile(path.join(casePath, problemFile), err.problem.getText(), { encoding: "utf-8" });
await afs.writeFile(path.join(casePath, inputFile), err.valStepInput, { encoding: "utf-8" });

let command = `:: The purpose of this batch file is to be able to reproduce the valstep error
type ${inputFile} | ${Util.q(valStepPath)} ${domainFile} ${problemFile}`;

let command = `type ${inputFile} | valstep ${domainFile} ${problemFile}`;
await afs.writeFile(path.join(tempDir, "run.cmd"), command, {encoding: "utf-8"});
return tempDir;
await afs.writeFile(path.join(casePath, "run.cmd"), command, { encoding: "utf-8" });
return casePath;
}
}

Expand Down
4 changes: 4 additions & 0 deletions client/src/planning/PlanFunctionEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ export class PlanFunctionEvaluator {
return this.valueSeqPath && this.valStepPath ? true : false;
}

getValStepPath(): string {
return this.valStepPath;
}

async evaluate(): Promise<Map<Variable, GroundedFunctionValues>> {
let domainFile = await Util.toPddlFile("domain", this.plan.domain.getText());
let problemFile = await Util.toPddlFile("problem", this.plan.problem.getText());
Expand Down
32 changes: 24 additions & 8 deletions client/src/planning/PlanReportGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,15 @@ export class PlanReportGenerator {
if (planIndex === selectedPlan) { className += " planSelector-selected"; }

let normalizedCost = plan.cost / maxCost * 100;
let costRounded = plan.cost ? plan.cost.toFixed(DIGITS) : NaN;
let tooltip = `Plan #${planIndex}
Metric value / cost: ${plan.cost}
Makespan: ${plan.makespan}
States evaluated: ${plan.statesEvaluated}`;

return `
<div class="${className}" plan="${planIndex}" onclick="showPlan(${planIndex})"><span>${plan.cost}</span>
<div class="planMetricBar" style="height: ${normalizedCost}px"></div>
<div class="${className}" plan="${planIndex}" onclick="showPlan(${planIndex})"><span>${costRounded}</span>
<div class="planMetricBar" style="height: ${normalizedCost}px" title="${tooltip}"></div>
</div>`;
}

Expand Down Expand Up @@ -175,7 +180,7 @@ ${objectsHtml}
});
} catch (err) {
console.log(err);
this.handleValStepError(err);
this.handleValStepError(err, evaluator.getValStepPath());
}
}
else {
Expand All @@ -192,13 +197,24 @@ ${lineCharts}
`;
}

private async handleValStepError(err: any) {
private async handleValStepError(err: any, valStepPath: string): Promise<void> {
if (err instanceof ValStepError) {
try {
let choice = await window.showErrorMessage("ValStep failed to evaluate the plan values.", "Show", "Ignore");
if (choice === "Show") {
let path = await ValStep.storeError(err);
env.openExternal(Uri.file(path));
const exportCase = "Export valstep case...";
let choice = await window.showErrorMessage("ValStep failed to evaluate the plan values.", exportCase, "Ignore");
if (choice === exportCase) {
let targetPathUris = await window.showOpenDialog({
canSelectFolders: true, canSelectFiles: false,
defaultUri: Uri.file(path.dirname(err.domain.fileUri)),
openLabel: 'Select target folder'
});
if (!targetPathUris) { return; }
let targetPath = targetPathUris[0];
let outputPath = await ValStep.storeError(err, targetPath, valStepPath);
let success = await env.openExternal(Uri.file(outputPath));
if (!success) {
window.showErrorMessage(`Files for valstep bug report: ${outputPath}.`);
}
}
}
catch (err1) {
Expand Down
127 changes: 127 additions & 0 deletions client/src/planning/PlannerAsyncService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/* --------------------------------------------------------------------------------------------
* Copyright (c) Jan Dolejsi. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */
'use strict';

// import * as request from 'request';
import { Uri, workspace } from 'vscode';
import { PlannerResponseHandler } from './PlannerResponseHandler';
import { Plan } from '../../../common/src/Plan';
import { DomainInfo, ProblemInfo } from '../../../common/src/parser';
import { PddlPlanParser } from '../../../common/src/PddlPlanParser';
import { Authentication } from '../../../common/src/Authentication';
import { PlannerService } from './PlannerService';
import { PlannerConfigurationSelector } from './PlannerConfigurationSelector';

/** Wraps the `/request` planning web service interface. */
export class PlannerAsyncService extends PlannerService {

timeout = 60; //this default is overridden by info from the configuration!
asyncMode = false;

constructor(plannerPath: string, private plannerConfiguration: Uri, useAuthentication: boolean, authentication: Authentication) {
super(plannerPath, useAuthentication, authentication);
}

getTimeout(): number {
return this.timeout;
}

createUrl(): string {
return this.plannerPath + '?async=' + this.asyncMode;
}

async createRequestBody(domainFileInfo: DomainInfo, problemFileInfo: ProblemInfo): Promise<any> {
let configuration = await this.getConfiguration();
if (!configuration) { return null; }

configuration.planFormat = 'JSON';
if ("timeout" in configuration) {
this.timeout = configuration.timeout;
}

return {
'domain': {
'name': domainFileInfo.name,
'format': 'PDDL',
'content': domainFileInfo.getText()
},
'problem': {
'name': problemFileInfo.name,
'format': 'PDDL',
'content': problemFileInfo.getText()
},
'configuration': configuration
};
}

processServerResponseBody(responseBody: any, planParser: PddlPlanParser, parent: PlannerResponseHandler, resolve: (plans: Plan[]) => void, reject: (error: Error) => void): void {
let _timedout = false;
let response_status: string = responseBody['status']['status'];
if (["STOPPED", "SEARCHING_BETTER_PLAN"].includes(response_status)) {
_timedout = responseBody['status']['reason'] === "TIMEOUT";
if (responseBody['plans'].length > 0) {
let plansJson = responseBody['plans'];
plansJson.forEach((plan: any) => this.parsePlan(plan, planParser));

let plans = planParser.getPlans();
if (plans.length > 0) { parent.handleOutput(plans[0].getText() + '\n'); }
else { parent.handleOutput('No plan found.'); }

resolve(plans);
return;
}
else {
// todo: no plan found yet. Poll again later.
resolve([]);
return;
}
}
else if (response_status === "FAILED") {
let error = responseBody['status']['error']['message'];
reject(new Error(error));
return;
}
else if (["NOT_INITIALIZED", "INITIATING", "SEARCHING_INITIAL_PLAN"].includes(response_status)) {
_timedout = true;
let error = `After timeout ${this.timeout} the status is ${response_status}`;
reject(new Error(error));
return;
}

console.log(_timedout);
}

parsePlan(plan: any, planParser: PddlPlanParser): void {
let _makespan: number = plan['makespan'];
let _metric: number = plan['metricValue'];
let search_performance_info = plan['searchPerformanceInfo'];
let _states_evaluated: number = search_performance_info['statesEvaluated'];
let _elapsedTimeInSeconds = parseFloat(search_performance_info['timeElapsed']) / 1000;
let planSteps = JSON.parse(plan['content']);
this.parsePlanSteps(planSteps, planParser);

planParser.onPlanFinished();

console.log("Not implemented further." + _makespan + _metric + _states_evaluated + _elapsedTimeInSeconds + planSteps);
}

async getConfiguration(): Promise<any> {
if (this.plannerConfiguration.toString() === PlannerConfigurationSelector.DEFAULT.toString()) {
return this.createDefaultConfiguration();
}
else {
let configurationDoc = await workspace.openTextDocument(this.plannerConfiguration);
let configurationString = configurationDoc.getText();
return JSON.parse(configurationString);
}
}

createDefaultConfiguration(): any {
return {
"planFormat": "JSON",
"timeout": this.timeout
};
}
}
Loading

0 comments on commit 585b2f0

Please sign in to comment.