'use strict' ; import { workspace , window , EventEmitter , Event , Disposable } from 'vscode' ; import * as process from 'child_process' ; import treeKill = require ( 'tree-kill' ) ; import { ProblemInfo , DomainInfo , utils , parser , planner , Plan } from 'pddl-workspace' ; import { Util } from 'ai-planning-val' ; /** Planner implemented as an executable process, which outputs through VS Code facilities. */ export class PlannerExecutable extends planner . Planner implements Disposable { private child : process . ChildProcess | undefined ; private _exited = new EventEmitter < number > ( ) ; static readonly DEFAULT_SYNTAX = "$(planner) $(options) $(domain) $(problem)" ; private readonly plannerSyntax : string ; private readonly plannerOptions : string ; constructor ( plannerPath , private configuration , providerConfiguration ) { super ( plannerPath , configuration , providerConfiguration ) ; this . plannerSyntax = configuration . plannerSyntax ?? providerConfiguration . configuration . syntax ?? PlannerExecutable . DEFAULT_SYNTAX ; this . plannerOptions = configuration . options ?? '' ; } get onExited ( ) : Event < number > { return this . _exited . event ; } dispose ( ) { this . _exited . dispose ( ) ; } get requiresKeyboardInput ( ) : boolean { return false ; } get supportsSearchDebugger ( ) : boolean { return true ; } async plan ( domainFileInfo , problemFileInfo , planParser , callbacks ) { const domainFilePath = await Util . toPddlFile ( "domain" , domainFileInfo . getText ( ) ) ; const problemFilePath = await Util . toPddlFile ( "problem" , problemFileInfo . getText ( ) ) ; let command = this . plannerSyntax . replace ( '$(planner)' , utils . Util . q ( this . plannerPath ) ) . replace ( '$(options)' , this . plannerOptions ) . replace ( '$(domain)' , utils . Util . q ( domainFilePath ) ) . replace ( '$(problem)' , utils . Util . q ( problemFilePath ) ) ; command += ' ' + callbacks . providePlannerOptions ( { domain : domainFileInfo , problem : problemFileInfo } ) . join ( ' ' ) ; callbacks . handleOutput ( command + '\n' ) ; const thisPlanner = this ; if ( workspace . getConfiguration ( "pddlPlanner" ) . get ( "executionTarget" ) === "Terminal" ) { return new Promise < Plan [ ] > ( ( resolve ) => { const terminal = window . createTerminal ( { name : "Planner output" , cwd : thisPlanner . configuration . workingDirectory } ) ; terminal . sendText ( command , true ) ; terminal . show ( true ) ; const plans = [ ] ; resolve ( plans ) ; } ) ; } return new Promise < Plan [ ] > ( function ( resolve , reject ) { thisPlanner . child = process . exec ( command , { cwd : thisPlanner . configuration . workingDirectory } , ( error ) => { planParser . onPlanFinished ( ) ; if ( error && ! thisPlanner . child ?. killed && ! thisPlanner . planningProcessKilled ) { reject ( error ) ; } const plans = planParser . getPlans ( ) ; resolve ( plans ) ; thisPlanner . child = undefined ; } ) ; thisPlanner . child . stdout ?. on ( 'data' , ( data ) => { const dataString = data . toString ( ) ; callbacks . handleOutput ( dataString ) ; planParser . appendBuffer ( dataString ) ; } ) ; thisPlanner . child . stderr ?. on ( 'data' , ( data ) => callbacks . handleOutput ( "Error: " + data ) ) ; thisPlanner . child . on ( "close" , ( code , signal ) => { if ( code ) { console . log ( "Exit code: " + code ) ; } if ( signal ) { console . log ( "Exit Signal: " + signal ) ; } thisPlanner . _exited . fire ( code ) ; } ) ; } ) ; } /**
     * When the UI button is pressed, the planner is forced to stop.
     */ stop ( ) { if ( this . child ) { super . stop ( ) ; treeKill ( this . child . pid ) ; } } } /** Creates instances of the PlannerExecutable, so other extensions could wrap them. */ export class PlannerExecutableFactory { param plannerPath param plannerRunConfiguration param providerConfiguration returns createPlannerExecutable ( plannerPath , plannerRunConfiguration , providerConfiguration ) { return new PlannerExecutable ( plannerPath , plannerRunConfiguration , providerConfiguration ) ; } }