Skip to content

Commit

Permalink
Merge pull request #65 from jan-dolejsi/dev_2.17.4
Browse files Browse the repository at this point in the history
v2.18.0 - multiple plan metric line plots
  • Loading branch information
jan-dolejsi authored Jun 17, 2020
2 parents d42b68a + 54b31a9 commit c311858
Show file tree
Hide file tree
Showing 18 changed files with 124 additions and 40 deletions.
1 change: 0 additions & 1 deletion .vscodeignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ src/**
tsconfig.json
*.cmd
.vscode-test/**
val/**
.vscodeignore
package-lock.json
.eslintrc.js
Expand Down
32 changes: 31 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
# PDDL support - What's new?

## 2.18.0

### Features

### Line plots for multiple metric expressions

If your planner supports multiple `(:metric ...)` expressions in the problem file (VAL actually does),
you can use it to get some ad-hoc expressions displayed on a line plot below the plan.
This is very useful, to debug numerically-rich domains.

![Plan metric plots](https://raw.githubusercontent.com/wiki/jan-dolejsi/vscode-pddl/img/plan_metric_plots.jpg)

### Other improvements

- Auto completion for (:constraints ) includes the nested (and ), which I always forget
- Val std-error stream is also now presented in the _Problems_ pane.
- Support for DAY (and WEEK) time resolution in plans.
- To minimize the refresh of plan visualization, the plans are _no longer_ re-painted when upon the planner exit.

### Fixes

- Fixed bug, where the test results/outcomes were not being displayed on the tree, if the tree was first-time-expanded _during_ the execution of the tests.
- Fixed regression on the visual search debugger related to selection of nodes on the tree.
- Fixed response to failing plan validation. Instead of opening the _Problems_ pane, we open the _Output_ pane, where the detailed VAL output is printed.
- Small fix for the Overview Page, when it is closed before the current configuration is posted to it.
- Async service call handles multiple plans and xml plan format
- Fixed bug that caused the extension to hang in an endless loop, while resolving a PDDL symbol references (while hover-over)
- Step up to target ES2019

## 2.17.3

Escaping spaces in VAL paths on MacOS.
Expand Down Expand Up @@ -1008,7 +1037,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.17.1...HEAD
[Unreleased]: https://github.com/jan-dolejsi/vscode-pddl/compare/v2.18.0...HEAD
[2.16.0]:https://github.com/jan-dolejsi/vscode-pddl/compare/v2.17.1...v2.18.0
[2.16.0]:https://github.com/jan-dolejsi/vscode-pddl/compare/v2.16.0...v2.17.1
[2.15.7]:https://github.com/jan-dolejsi/vscode-pddl/compare/v2.15.7...v2.16.0
[2.15.7]:https://github.com/jan-dolejsi/vscode-pddl/compare/v2.15.6...v2.15.7
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,15 @@ If one of the rules above is not satisfied, the editor will not naturally associ

![domain/problem/plann associations](https://raw.githubusercontent.com/wiki/jan-dolejsi/vscode-pddl/img/PDDL_explicit_domain-problem-plan_associations.gif)

#### Line plots for multiple metric expressions

If your planner supports multiple `(:metric ...)` expressions in the problem file (VAL actually does),
you can use it to get some ad-hoc expressions displayed on a line plot below the plan.
This is very useful, to debug numerically-rich domains.

![Plan metric plots](https://raw.githubusercontent.com/wiki/jan-dolejsi/vscode-pddl/img/plan_metric_plots.jpg)


#### Running the planner interactively

See configuration setting `pddlPlanner.executionTarget` to select where is the planner executable started. You can either direct planner executable output to a _Terminal_ window instead of the _Output window_. This can be configured on the _Overview page_.
Expand Down
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions 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.17.3",
"version": "2.18.0",
"publisher": "jan-dolejsi",
"engines": {
"vscode": "^1.44.0",
Expand Down Expand Up @@ -973,14 +973,14 @@
"test": "npm run test:unit && npm run test:integration"
},
"dependencies": {
"ai-planning-val": "^2.1.7",
"ai-planning-val": "^2.2.0",
"await-notify": "^1.0.1",
"body-parser": "^1.19.0",
"events": "^3.1.0",
"express": "^4.17.1",
"jsonc-parser": "^2.2.1",
"open": "^7.0.2",
"pddl-workspace": "^3.1.0",
"pddl-workspace": "^3.2.0",
"request": "^2.88.2",
"semver": "^7.1.3",
"tree-kill": "^1.2.2",
Expand Down
2 changes: 1 addition & 1 deletion src/completion/AbstractCompletionItemProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export class AbstractCompletionItemProvider {
}

protected addConstraintsDocumentation(): void {
this.addSuggestionDocumentation(':constraints', 'Constraints', 'Constraints.... you may want to stay away from those.');
this.addSuggestionDocumentation(':constraints', 'Constraints', 'Constraints that all plans must satisfy.');
}

protected addRequirementsDocumentation(): void {
Expand Down
2 changes: 1 addition & 1 deletion src/completion/DomainCompletionItemProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ export class DomainCompletionItemProvider extends AbstractCompletionItemProvider
case parser.PddlStructure.PREDICATES:
case parser.PddlStructure.FUNCTIONS:
case parser.PddlStructure.CONSTRAINTS:
return this.createSnippetCompletionItem(suggestion, "(" + suggestion.sectionName + " \n\t$0\n)", range, context, index);
return this.createSnippetCompletionItem(suggestion, "(" + suggestion.sectionName + " (and\n\t$0\n))", range, context, index);

case parser.PddlStructure.ACTION:
return this.createSnippetCompletionItem(suggestion, ["(:action ${1:action_name}",
Expand Down
2 changes: 1 addition & 1 deletion src/completion/ProblemCompletionItemProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export class ProblemCompletionItemProvider extends AbstractCompletionItemProvide
case parser.PddlStructure.OBJECTS:
case parser.PddlStructure.INIT:
case parser.PddlStructure.CONSTRAINTS:
return this.createSnippetCompletionItem(suggestion, "(" + suggestion.sectionName + " \n\t$0\n)", range, context, index);
return this.createSnippetCompletionItem(suggestion, "(" + suggestion.sectionName + " (and\n\t$0\n))", range, context, index);
case parser.PddlStructure.GOAL:
return this.createSnippetCompletionItem(suggestion, "(" + suggestion.sectionName + " (and\n\t$0\n))", range, context, index);
case parser.PddlStructure.METRIC:
Expand Down
16 changes: 13 additions & 3 deletions src/diagnostics/PlanValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ export class PlanValidator {
try {
const outcome = await this.validatePlanDocument(planDocument);
if (outcome.getError()) {
commands.executeCommand('workbench.actions.view.problems');
// do not open the _Problems_ pane, unless you can direct the error there
// commands.executeCommand('workbench.actions.view.problems');
throw new Error(outcome.getError());
}
} catch (ex) {
Expand Down Expand Up @@ -157,7 +158,7 @@ export class PlanValidator {
this.output.appendLine(child.stderr.toString());
}

outcome = this.analyzeOutput(planInfo, child.error, output);
outcome = this.analyzeOutput(planInfo, child.stderr.toString(), child.error, output);
onSuccess(outcome.getDiagnostics());
}

Expand Down Expand Up @@ -185,7 +186,7 @@ export class PlanValidator {
return ".";
}

analyzeOutput(planInfo: PlanInfo, error: Error | undefined, output: string): PlanValidationOutcome {
analyzeOutput(planInfo: PlanInfo, stderr: string, error: Error | undefined, output: string): PlanValidationOutcome {
if (error) {
return PlanValidationOutcome.failed(planInfo, error);
}
Expand All @@ -212,6 +213,10 @@ export class PlanValidator {
return PlanValidationOutcome.valid(planInfo);
}

if (stderr?.trim()) {
return PlanValidationOutcome.otherError(planInfo, stderr.trim());
}

return PlanValidationOutcome.unknown(planInfo);
}

Expand Down Expand Up @@ -319,6 +324,11 @@ class PlanValidationOutcome {
return new PlanValidationOutcome(planInfo, diagnostics);
}

static otherError(planInfo: PlanInfo, error: string): PlanValidationOutcome {
const diagnostics = [new Diagnostic(createRangeFromLine(0), `${error}. Run the 'PDDL: Validate plan' command for more information.`, DiagnosticSeverity.Error)];
return new PlanValidationOutcome(planInfo, diagnostics, error);
}

static unknown(planInfo: PlanInfo): PlanValidationOutcome {
const diagnostics = [new Diagnostic(createRangeFromLine(0), "Unknown error. Run the 'PDDL: Validate plan' command for more information.", DiagnosticSeverity.Warning)];
return new PlanValidationOutcome(planInfo, diagnostics, "Unknown error.");
Expand Down
2 changes: 1 addition & 1 deletion src/init/OverviewPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ export class OverviewPage {
updateValAlert: await this.val.isNewValVersionAvailable()
// todo: workbench.editor.revealIfOpen
};
return this.webViewPanel.webview.postMessage(message);
return this.webViewPanel.webview?.postMessage(message) ?? false;
}

private toWireWorkspaceFolder(workspaceFolder: WorkspaceFolder): WireWorkspaceFolder {
Expand Down
18 changes: 17 additions & 1 deletion src/planning/PlanReportGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,23 @@ ${objectsHtml}

functionValues.forEach((values, liftedVariable) => {
const chartDivId = `chart_${planIndex}_${liftedVariable.declaredName}`;
lineCharts += ` <div id="${chartDivId}" style="width: ${this.options.displayWidth + 100}px; height: ${Math.round(this.options.displayWidth / 2)}px"></div>\n`;
lineCharts += this.createLineChartDiv(chartDivId);
let chartTitleWithUnit = values.legend.length > 1 ? liftedVariable.name : liftedVariable.getFullName();
if (liftedVariable.getUnit()) { chartTitleWithUnit += ` [${liftedVariable.getUnit()}]`; }
lineChartScripts += ` drawChart('${chartDivId}', '${chartTitleWithUnit}', '', ${JSON.stringify(values.legend)}, ${JSON.stringify(values.values)}, ${this.options.displayWidth});\n`;
});

// add one plot for declared metric
for (let metricIndex = 0; metricIndex < plan.problem.getMetrics().length; metricIndex++) {
const metric = plan.problem.getMetrics()[metricIndex];

const metricValues = await evaluator.evaluateExpression(metric.getExpression());
const chartDivId = `chart_${planIndex}_metric${metricIndex}`;
lineCharts += this.createLineChartDiv(chartDivId);
const chartTitleWithUnit = metric.getDocumentation()[metric.getDocumentation().length - 1];
lineChartScripts += ` drawChart('${chartDivId}', '${chartTitleWithUnit}', '', ['${/*unit?*/""}'], ${JSON.stringify(metricValues.values)}, ${this.options.displayWidth});\n`;
}

} catch (err) {
console.error(err);
const valStepPath = evaluator.getValStepPath();
Expand All @@ -228,6 +240,10 @@ ${lineCharts}
`;
}

private createLineChartDiv(chartDivId: string): string {
return ` <div id="${chartDivId}" style="width: ${this.options.displayWidth + 100}px; height: ${Math.round(this.options.displayWidth / 2)}px"></div>\n`;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
private async handleValStepError(err: any, valStepPath: string): Promise<void> {
if (err instanceof ValStepError) {
Expand Down
27 changes: 18 additions & 9 deletions src/planning/PlannerAsyncService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,18 @@ export class PlannerAsyncService extends PlannerService {
return 1 / 1000;
case "HOUR":
return 60 * 60;
case "DAY":
return 24 * 60 * 60;
case "WEEK":
return 7 * 24 * 60 * 60;
case "SECOND":
default:
return 1;
}
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
processServerResponseBody(responseBody: any, planParser: parser.PddlPlannerOutputParser, callbacks: planner.PlannerResponseHandler, resolve: (plans: Plan[]) => void, reject: (error: Error) => void): void {
async processServerResponseBody(responseBody: any, planParser: parser.PddlPlannerOutputParser, callbacks: planner.PlannerResponseHandler, resolve: (plans: Plan[]) => void, reject: (error: Error) => void): Promise<void> {
let _timedOut = false;
const responseStatus: string = responseBody['status']['status'];
if (["STOPPED", "SEARCHING_BETTER_PLAN"].includes(responseStatus)) {
Expand All @@ -82,15 +86,20 @@ export class PlannerAsyncService extends PlannerService {
const plansJson = responseBody['plans'];
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
plansJson.forEach((plan: any) => this.parsePlan(plan, planParser));
const parserPromises = plansJson.map((plan: any) => this.parsePlan(plan, planParser));
await Promise.all(parserPromises);
}
catch (err) {
reject(err);
}

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

resolve(plans);
return;
Expand All @@ -117,7 +126,7 @@ export class PlannerAsyncService extends PlannerService {
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
parsePlan(plan: any, planParser: parser.PddlPlannerOutputParser): void {
async parsePlan(plan: any, planParser: parser.PddlPlannerOutputParser): Promise<void> {
const makespan: number = plan['makespan'];
const metric: number = plan['metricValue'];
const searchPerformanceInfo = plan['searchPerformanceInfo'];
Expand All @@ -127,19 +136,19 @@ export class PlannerAsyncService extends PlannerService {
planParser.setPlanMetaData(makespan, metric, statesEvaluated, elapsedTimeInSeconds, this.planTimeScale);

const planFormat: string | undefined = plan['format'];
if (planFormat && planFormat.toLowerCase() === 'json') {
if (planFormat?.toLowerCase() === 'json') {
const planSteps = JSON.parse(plan['content']);
this.parsePlanSteps(planSteps, planParser);
planParser.onPlanFinished();
}
else if (planFormat && planFormat.toLowerCase() === 'tasks') {
else if (planFormat?.toLowerCase() === 'tasks') {
const planText = plan['content'];
planParser.appendLine(planText);
planParser.onPlanFinished();
}
else if (planFormat && planFormat.toLowerCase() === 'xplan') {
else if (planFormat?.toLowerCase() === 'xplan') {
const planText = plan['content'];
planParser.appendLine(planText); // the underlying
await planParser.appendXplan(planText); // must await the underlying async xml parsing
}
else {
throw new Error('Unsupported plan format: ' + planFormat);
Expand Down
6 changes: 4 additions & 2 deletions src/planning/planning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -484,8 +484,10 @@ export class Planning implements planner.PlannerResponseHandler {
}

visualizePlans(plans: Plan[]): void {
this.plans = plans;
this.planView.setPlannerOutput(plans, !this.isSearchDebugger());
if (this.plans !== plans) {
this.plans = plans;
this.planView.setPlannerOutput(plans, !this.isSearchDebugger());
}
}

static q(path: string): string {
Expand Down
2 changes: 1 addition & 1 deletion src/ptest/PTestExplorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export class PTestExplorer {
this.subscribe(instrumentOperationAsVsCodeCommand('pddl.tests.problemSaveAs', () => this.saveProblemAs().catch(showError)));

this.subscribe(instrumentOperationAsVsCodeCommand(PTEST_REVEAL, nodeUri =>
this.pTestViewer.reveal(this.pTestTreeDataProvider.findNodeByResource(nodeUri), { select: true, expand: true }))
this.pTestViewer.reveal(this.pTestTreeDataProvider.findNodeByResourceOrThrow(nodeUri), { select: true, expand: true }).then(() => { return; }, showError))
);

this.manifestGenerator = new ManifestGenerator(this.codePddlWorkspace.pddlWorkspace, this.context);
Expand Down
9 changes: 7 additions & 2 deletions src/ptest/PTestTreeDataProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,16 @@ export class PTestTreeDataProvider implements TreeDataProvider<PTestNode> {
setTestOutcome(test: Test, testOutcome: TestOutcome): void {
this.testResults.set(test.getUriOrThrow().toString(), testOutcome);
const node = this.findNodeByResource(test.getUriOrThrow());
this._onDidChange.fire(node);
// the node may not exist, if the tree hasn't been expanded yet
node && this._onDidChange.fire(node);
}

findNodeByResourceOrThrow(resource: Uri): PTestNode {
return assertDefined(this.findNodeByResourceOrThrow(resource), `No node for ${resource.toString()}`);
}

findNodeByResource(resource: Uri): PTestNode {
return assertDefined(this.treeNodeCache.get(resource.toString()), `No node for ${resource.toString()}`);
return this.treeNodeCache.get(resource.toString());
}

cache(node: PTestNode): PTestNode {
Expand Down
6 changes: 4 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
"noUnusedParameters": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"target": "es6",
"target": "ES2019",
"lib": [
"es2019"
],
"module": "commonjs",
"moduleResolution": "node",
"forceConsistentCasingInFileNames": true,
"rootDir": "src",
"outDir": "out",
"lib": [ "es6" ],
"sourceMap": true
},
"exclude": [
Expand Down
Loading

0 comments on commit c311858

Please sign in to comment.