'use strict' ; import { TextDocument , SaveDialogOptions , Uri , window } from 'vscode' ; import { PlanExporter } from './PlanExporter' ; import { parser } from 'pddl-workspace' ; import { exportToAndShow } from './ExportUtil' ; /**
 * Converts plan to happenings while preserving comments.
 */ export class PlanHappeningsExporter { private readonly decimals : number ; constructor ( private planDocument , private epsilon ) { this . decimals = - Math . log10 ( epsilon ) ; this . computeHappeningsText ( this . planDocument . getText ( ) ) ; } public async export ( ) { const defaultPlanHappeningsPath = PlanExporter . replaceExtension ( this . planDocument . uri . fsPath , '.happenings' ) ; const options = { saveLabel : "Save plan happenings as..." , filters : { "Plan Happenings" : [ "happenings" ] } , defaultUri : Uri . file ( defaultPlanHappeningsPath ) } ; try { const uri = await window . showSaveDialog ( options ) ; if ( uri === undefined ) { return ; } await exportToAndShow ( this . happeningsText , uri ) ; } catch ( ex ) { window . showErrorMessage ( `Cannot export plan to happenings: ${ ex }` ) ; } } comments = '' ; happeningsText = '' ; enqueuedHappenings : Happening [ ] = [ ] ; makespan = 0 ; actionCounter : Map < string , number > = new Map ( ) ; computeHappeningsText ( planText ) { const planParser = new parser . PddlPlanParser ( ) ; const planBuilder = new parser . PddlPlanBuilder ( this . epsilon ) ; planText . split ( '\n' ) . forEach ( ( line , index ) => { if ( line . match ( /^\s*;/ ) ) { this . comments += line + '\n' ; } else if ( line . match ( /^\s*$/ ) ) { this . comments += '\n' ; } else { if ( this . comments . length ) { this . flushComments ( ) ; } const happening = this . parseStepAndEnqueueEnd ( line , index , planParser , planBuilder ) ; if ( happening ) { this . flushHappeningsBefore ( happening . time ) ; this . outputHappening ( happening ) ; } else { console . log ( "Error parsing happening: " + line ) ; } } } ) ; this . enqueuedHappenings . forEach ( happening => this . outputHappening ( happening ) ) ; this . enqueuedHappenings = [ ] ; this . flushComments ( ) ; } parseStepAndEnqueueEnd ( line , lineIndex , planParser , planBuilder ) { const planStep = planParser . parse ( line , lineIndex , planBuilder ) ; if ( planStep ) { const actionName = planStep . getFullActionName ( ) ; const count = this . getActionCount ( actionName ) ; const thisHappening = new Happening ( planStep . getStartTime ( ) , actionName , planStep . isDurative ? HappeningType . Start : HappeningType . Instantaneous , count ) ; if ( planStep . isDurative ) { const endTime = planStep . getStartTime ( ) + ( planStep . getDuration ( ) ?? 0 ) ; const endHappening = new Happening ( endTime , actionName , HappeningType . End , count ) ; this . enqueue ( endHappening ) ; } return thisHappening ; } else { this . happeningsText += `; Warning: line did not parse: ${ line }` ; return null ; } } getActionCount ( action ) { let prevCount = - 1 ; const actionCount = this . actionCounter . get ( action ) ; if ( actionCount !== undefined ) { prevCount = actionCount ; } const newCount = prevCount + 1 ; this . actionCounter . set ( action , newCount ) ; return newCount ; } enqueue ( endHappening ) { this . enqueuedHappenings . push ( endHappening ) ; this . enqueuedHappenings = this . enqueuedHappenings . sort ( this . compareHappenings ) ; } flushHappeningsBefore ( time ) { while ( this . enqueuedHappenings . length > 0 && this . enqueuedHappenings [ 0 ] . time <= time ) { this . outputHappening ( this . enqueuedHappenings [ 0 ] ) ; this . enqueuedHappenings . splice ( 0 , 1 ) ; } } flushComments ( ) { this . happeningsText += this . comments ; this . comments = '' ; } outputHappening ( happening ) { let happeningQualifier = '' ; switch ( happening . type ) { case HappeningType . Start : happeningQualifier = "start " ; break ; case HappeningType . End : happeningQualifier = "end " ; break ; } let countString = '' ; if ( happening . count > 0 ) { countString = ` #${ happening . count + 1 }` ; } this . happeningsText += `${ this . formatTime ( happening . time ) }: ${ happeningQualifier }(${ happening . name })${ countString }\n` ; } formatTime ( time ) { return Number ( time ) . toFixed ( this . decimals ) ; } compareHappenings ( h1 , h2 ) { if ( h1 . time > h2 . time ) { return 1 ; } else if ( h1 . time < h2 . time ) { return - 1 ; } return 0 ; } } class Happening { constructor ( public time , public name , public type , public count ) { } } enum HappeningType { Start , End , Instantaneous }