Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v2.18.0 - multiple plan metric line plots #65

Merged
merged 6 commits into from
Jun 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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