pddl-planning-service-client
Version:
PDDL Planning Service clients
167 lines • 7.35 kB
JavaScript
/* --------------------------------------------------------------------------------------------
* Copyright (c) Jan Dolejsi. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PlannerAsyncService = void 0;
const pddl_workspace_1 = require("pddl-workspace");
const PlannerService_1 = require("./PlannerService");
const HOUR = "HOUR";
const DEFAULT_PLAN_TIME_UNIT_HOUR = HOUR;
/** Wraps the `/request` planning web service interface. */
class PlannerAsyncService extends PlannerService_1.PlannerService {
constructor(plannerUrl, asyncPlannerConfiguration, providerConfiguration) {
super(plannerUrl, asyncPlannerConfiguration, providerConfiguration);
this.asyncPlannerConfiguration = asyncPlannerConfiguration;
this.timeout = PlannerAsyncService.DEFAULT_TIMEOUT; //this default is overridden by info from the configuration!
this.asyncMode = false;
this.planTimeScale = 1;
this.lastPlanPrinted = -1;
}
getTimeout() {
return this.timeout;
}
createUrl() {
return this.plannerPath + '?async=' + this.asyncMode;
}
async createRequestBody(domainFileInfo, problemFileInfo) {
const configuration = this.asyncPlannerConfiguration;
if (!configuration) {
return null;
}
configuration.planFormat = configuration.planFormat ?? 'JSON';
if (configuration.timeout !== undefined) {
this.timeout = configuration.timeout;
}
this.planTimeScale = PlannerAsyncService.toPlanTimeScale(configuration.planTimeUnit ?? DEFAULT_PLAN_TIME_UNIT_HOUR);
let body = {
domain: {
name: domainFileInfo.name,
format: 'PDDL',
content: domainFileInfo.getText()
},
problem: {
name: problemFileInfo.name,
format: 'PDDL',
content: problemFileInfo.getText()
},
configuration: configuration
};
if (this.asyncPlannerConfiguration.searchDebuggerEnabled) {
if (this.providerConfiguration.configuration.searchDebuggerSupport === pddl_workspace_1.planner.SearchDebuggerSupportType.HttpCallback) {
if (!this.plannerPath.match(/http:\/\/(localhost|127\.0\.0\.1)[:\/]/)) {
throw new Error(`Search debugger HTTP Callback is only supported for servers running on localhost.`);
}
if (this.asyncPlannerConfiguration.searchDebuggerPort) {
body = Object.assign(body, {
'callbacks': [
{
'type': 'STATES',
'url': 'http://localhost:' + this.asyncPlannerConfiguration.searchDebuggerPort,
}
],
});
}
else {
throw new Error(`Search debugger port not provided.`);
}
}
}
return body;
}
static toPlanTimeScale(planTimeUnit) {
switch (planTimeUnit) {
case "MINUTE":
return 60;
case "MILLISECOND":
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;
}
}
async processServerResponseBody(_origUrl, responseBody, planParser, callbacks) {
// todo: the output returned may be cumulative, print only the new part
callbacks.handleOutput(responseBody.output);
const responseStatus = responseBody.status.status;
if (["STOPPED", "SEARCHING_BETTER_PLAN"].includes(responseStatus)) {
if (responseBody.status.reason === "TIMEOUT") {
console.log(`Planning request timed out.`);
}
if (responseBody.plans.length > 0) {
const plansJson = responseBody.plans;
const parserPromises = plansJson.map(plan => this.parsePlan(plan, planParser));
await Promise.all(parserPromises);
const plans = planParser.getPlans();
for (let index = this.lastPlanPrinted + 1; index < plans.length; index++) {
callbacks.handlePlan(plans[index]);
this.lastPlanPrinted = index;
}
if (plans.length === 0) {
callbacks.handleOutput('No plan found.');
}
return plans;
}
else {
// todo: no plan found yet. Poll again later.
return [];
}
}
else if (responseStatus === "FAILED") {
const error = responseBody.status.error.message;
throw new Error(error);
}
else if (["NOT_INITIALIZED", "INITIATING", "SEARCHING_INITIAL_PLAN"].includes(responseStatus)) {
const error = `After timeout ${this.timeout} the status is ${responseStatus}`;
throw new Error(error);
}
else {
throw new Error(`Planner service returned unexpected status: ${responseStatus}.`);
}
}
async parsePlan(plan, planParser) {
const makespan = plan.makespan;
const metric = plan.metricValue;
const searchPerformanceInfo = plan.searchPerformanceInfo;
const statesEvaluated = searchPerformanceInfo.statesEvaluated;
const elapsedTimeInSeconds = parseFloat(searchPerformanceInfo.timeElapsed) / 1000;
const planTimeUnit = plan.timeUnit;
planTimeUnit && console.log("Plan time unit: " + planTimeUnit);
const planTimeScale = (planTimeUnit && PlannerAsyncService.toPlanTimeScale(planTimeUnit)) ?? this.planTimeScale;
planParser.setPlanMetaData(makespan, metric, statesEvaluated, elapsedTimeInSeconds, planTimeScale);
const planFormat = plan.format;
if (planFormat?.toLowerCase() === 'json') {
const planSteps = JSON.parse(plan.content);
this.convertPlanSteps(planSteps, planParser);
planParser.onPlanFinished();
}
else if (planFormat?.toLowerCase() === 'tasks') {
const planText = plan.content;
planParser.appendLine(planText);
planParser.onPlanFinished();
}
else if (planFormat?.toLowerCase() === 'xplan') {
const planText = plan.content;
await planParser.appendXplan(planText); // must await the underlying async xml parsing
}
else {
throw new Error('Unsupported plan format: ' + planFormat);
}
}
static createDefaultConfiguration(timeout) {
return {
planFormat: "JSON",
timeout: timeout
};
}
}
exports.PlannerAsyncService = PlannerAsyncService;
PlannerAsyncService.DEFAULT_TIMEOUT = 60;
//# sourceMappingURL=PlannerAsyncService.js.map