import { Plan , PlanStep } from "pddl-workspace" ; import { createPlanView , JsonDomainVizConfiguration , PlanView } from "pddl-gantt" ; import { SearchTree } from "./tree" ; import { StateChart } from "./charts" ; import { getElementByIdOrThrow , State } from "./utils" ; import { FinalStateData , PlanData } from 'model' ; /** VS Code stub, so we can work with it in a type safe way. */ interface VsCodeApi { postMessage ( payload ) : void ; } declare function postCommand ( command ) ; declare const acquireVsCodeApi ; declare const vscode ; declare function onLoad ( ) ; let searchTree ; window . addEventListener ( 'message' , event => { const message = event . data ; switch ( message . command ) { case 'stateAdded' : add ( message . state , false ) ; break ; case 'stateUpdated' : update ( message . state ) ; break ; case 'debuggerState' : showDebuggerOn ( message . state . running === 'on' , message . state . port ) ; break ; case 'showStatePlan' : showStatePlan ( message . state ) ; break ; case 'visualizeFinalState' : showFinalState ( message . state ) ; break ; case 'clear' : clearStates ( ) ; break ; case 'showAllStates' : showAllStates ( message . state ) ; break ; case 'showPlan' : showPlan ( message . state ) ; break ; case 'stateLog' : showStateLogButton ( message . state ) ; break ; default : console . log ( "Unexpected message: " + message . command ) ; } } ) ; param g param id param parentId param actionName param earliestTime param landmarks returns function createMockState ( g , id , parentId , actionName , earliestTime , landmarks ? ) { return { g : g , id : id , origId : id . toString ( ) , parentId : parentId , actionName : actionName , earliestTime : earliestTime , isGoal : false , landmarks : landmarks , totalMakespan : undefined } ; } const mockStates = [ createMockState ( 0 , 10 , undefined , undefined , 0 , 1 ) , createMockState ( 1 , 11 , 10 , 'drive start' , .1 , 2 ) , createMockState ( 1 , 12 , 10 , 'load start' , .1 , 2 ) , createMockState ( 2 , 13 , 11 , 'drive end' , 2 , 3 ) , createMockState ( 3 , 14 , 13 , 'unload start' , 2.1 , 3 ) , ] ; /** All states displayed. todo: should this be a map?*/ const states = [ ] ; /** Selected state ID or null if no state was selected yet. */ let selectedStateId ; getElementByIdOrThrow ( "addMock" ) . onclick = ( ) : void => { if ( mockStates . length === 0 ) { return ; } const newState = mockStates . shift ( ) ; if ( newState ) { add ( newState , false ) ; } } ; getElementByIdOrThrow ( "setVisitedOrWorseMock" ) . onclick = ( ) : void => { if ( selectedStateId === null ) { return ; } const state = states [ selectedStateId ] ; state . wasVisitedOrIsWorse = true ; update ( state ) ; } ; getElementByIdOrThrow ( "evaluateMock" ) . onclick = ( ) : void => { if ( selectedStateId === null ) { return ; } const state = states [ selectedStateId ] ; if ( ! state ) { console . log ( 'Selected state does not exist!' ) ; return ; } let h = 5 ; let totalMakespan = 5 ; let earliestTime = 0 ; if ( state . parentId !== undefined ) { const parentState = states [ state . parentId ] ; h = parentState . h ?? 2 - Math . floor ( Math . random ( ) * 2 ) ; totalMakespan = parentState . totalMakespan ?? 1.5 + Math . floor ( Math . random ( ) * 1.5 ) ; earliestTime = parentState . earliestTime + Math . random ( ) * 1.5 ; } state . h = h ; state . totalMakespan = totalMakespan ; state . earliestTime = earliestTime ; update ( state ) ; } ; getElementByIdOrThrow ( "deadEndMock" ) . onclick = ( ) : void => { if ( selectedStateId === null ) { return ; } const state = states [ selectedStateId ] ; if ( ! state ) { console . log ( 'Selected state does not exist!' ) ; return ; } state . h = Number . POSITIVE_INFINITY ; state . totalMakespan = Number . POSITIVE_INFINITY ; state . isDeadEnd = true ; update ( state ) ; } ; getElementByIdOrThrow ( "clearStatesMock" ) . onclick = ( ) : void => { clearStates ( ) ; } ; getElementByIdOrThrow ( "planMock" ) . onclick = ( ) : void => { if ( selectedStateId === null ) { return ; } let state = states [ selectedStateId ] ; const planStates = [ ] ; while ( state ) { state . isPlan = true ; planStates . push ( state ) ; const parentId = state . parentId ; state = parentId !== undefined && parentId !== null ? states [ parentId ] : undefined ; } showPlan ( planStates ) ; } ; let stateChart ; let planViz ; param newState param batch returns { void } function add ( newState , batch ) { searchTree . addStateToTree ( newState , batch ) ; stateChart . addStateToChart ( newState , batch ) ; states [ newState . id ] = newState ; } param state function update ( state ) { stateChart . updateStateOnChart ( state ) ; searchTree . updateStateOnTree ( state ) ; if ( selectedStateId === state . id ) { stateChart . selectChartRow ( state . id ) ; } } param states function showPlan ( states ) { searchTree . showPlanOnTree ( states ) ; } param states function showAllStates ( states ) { clearStates ( ) ; for ( const state of states ) { add ( state , true ) ; } endBatch ( ) ; } function endBatch ( ) { stateChart . endChartBatch ( ) ; searchTree . endTreeBatch ( ) ; } param stateId function onStateSelected ( stateId ) { if ( selectedStateId === stateId ) { return ; } selectedStateId = stateId ; stateChart . selectChartRow ( stateId ) ; searchTree . selectTreeNode ( stateId ) ; vscode ?. postMessage ( { command : 'stateSelected' , stateId : stateId } ) ; if ( ! vscode ) { const statePlan = new Plan ( [ new PlanStep ( .5 , "hello world " + stateId , true , 1 , 1 ) ] ) ; showStatePlan ( { plan : statePlan , width : 300 } ) ; } } document . body . onload = ( ) : void => initialize ( ) ; function initialize ( ) { searchTree = new SearchTree ( ) ; searchTree . network . on ( 'selectNode' , function ( nodeEvent ) { if ( nodeEvent . nodes . length > 0 ) { onStateSelected ( nodeEvent . nodes [ 0 ] ) ; } } ) ; searchTree . network . on ( 'deselectNode' , function ( nodeEvent ) { if ( nodeEvent . nodes . length > 0 ) { onStateSelected ( nodeEvent . nodes [ 0 ] ) ; } else { onStateSelected ( null ) ; } } ) ; window . document . addEventListener ( 'keydown' , function ( event ) { navigate ( event ) ; } ) ; window . onresize = function ( ) : void { stateChart . unsubscribeChartEvents ( ) ; stateChart . reSizeChart ( ) ; stateChart . subscribeToChartEvents ( ) ; } ; if ( ! vscode ) { showDebuggerOn ( false ) ; } stateChart = new StateChart ( onStateSelected ) ; stateChart . subscribeToChartEvents ( ) ; planViz = createPlanView ( "statePlan" , { displayWidth : 400 , epsilon : 1e-3 , disableLinePlots : true , onActionSelected : ( actionName ) => vscode ?. postMessage ( { "command" : "revealAction" , "action" : actionName } ) , onHelpfulActionSelected : ( helpfulAction ) => navigateToChildOfSelectedState ( helpfulAction ) , onFinalStateVisible : planView => requestFinalState ( planView ) , } ) ; getElementByIdOrThrow ( "mockMenu" ) . style . visibility = vscode ? 'collapse' : 'visible' ; onLoad ( ) ; } function clearStates ( ) { console . log ( 'clearing all states' ) ; searchTree . clearTree ( ) ; stateChart . clearChart ( ) ; } const START_DEBUGGER_BUTTON_ID = "startDebuggerButton" ; const STOP_DEBUGGER_BUTTON_ID = "stopDebuggerButton" ; const CLEAR_DEBUGGER_BUTTON_ID = "restartDebuggerButton" ; getElementByIdOrThrow ( START_DEBUGGER_BUTTON_ID ) . onclick = ( ) : void => startSearchDebugger ( ) ; function startSearchDebugger ( ) { showDebuggerOn ( true ) ; postCommand ( 'startDebugger' ) ; } getElementByIdOrThrow ( STOP_DEBUGGER_BUTTON_ID ) . onclick = ( ) : void => stopSearchDebugger ( ) ; function stopSearchDebugger ( ) { showDebuggerOn ( false ) ; postCommand ( 'stopDebugger' ) ; } getElementByIdOrThrow ( CLEAR_DEBUGGER_BUTTON_ID ) . onclick = ( ) : void => restartSearchDebugger ( ) ; function restartSearchDebugger ( ) { postCommand ( 'reset' ) ; showStatePlan ( { plan : new Plan ( [ ] ) , width : 300 } ) ; clearStates ( ) ; } param on param port function showDebuggerOn ( on , port ? ) { enableButton ( ! on , START_DEBUGGER_BUTTON_ID ) ; enableButton ( on , STOP_DEBUGGER_BUTTON_ID ) ; enableButton ( on , CLEAR_DEBUGGER_BUTTON_ID ) ; if ( port ) { getElementByIdOrThrow ( STOP_DEBUGGER_BUTTON_ID ) . title = "Search engine listener is running on port " + port + ". Click here to stop it." ; } } param enable param buttonId function enableButton ( enable , buttonId ) { const icon = getElementByIdOrThrow ( buttonId ) ; if ( enable ) { icon . classList . remove ( 'disabled' ) ; } else { icon . classList . add ( 'disabled' ) ; } } function showStatePlan ( data ) { const clonedPlan = Plan . clone ( data . plan ) ; const configuration = JsonDomainVizConfiguration . withCustomVisualizationScript ( data . domainVisualizationConfiguration , data . customDomainVisualizationScript ) ; planViz . showPlan ( clonedPlan , configuration ) ; } const shapeMap = new Map < string , string > ( ) ; shapeMap . set ( 'h' , 'hexagon' ) ; shapeMap . set ( 'b' , 'box' ) ; shapeMap . set ( 'd' , 'diamond' ) ; shapeMap . set ( 's' , 'star' ) ; shapeMap . set ( 't' , 'triangle' ) ; shapeMap . set ( 'h' , 'hexagon' ) ; shapeMap . set ( 'q' , 'square' ) ; shapeMap . set ( 'e' , 'ellipse' ) ; function navigate ( e ) { e = e || window . event ; let newSelectedStateId ; switch ( e . key ) { case "ArrowLeft" : newSelectedStateId = e . shiftKey ? stateChart . navigateChart ( - 1 ) : searchTree . navigateTreeSiblings ( - 1 ) ; onStateSelected ( newSelectedStateId ) ; if ( newSelectedStateId !== null ) { e . cancelBubble = true ; } break ; case "ArrowRight" : newSelectedStateId = e . shiftKey ? stateChart . navigateChart ( + 1 ) : searchTree . navigateTreeSiblings ( + 1 ) ; onStateSelected ( newSelectedStateId ) ; if ( newSelectedStateId !== null ) { e . cancelBubble = true ; } break ; case "ArrowUp" : newSelectedStateId = searchTree . navigateTreeUp ( ) ; onStateSelected ( newSelectedStateId ) ; if ( newSelectedStateId !== null ) { e . cancelBubble = true ; } break ; case "ArrowDown" : newSelectedStateId = searchTree . navigateTreeDown ( ) ; onStateSelected ( newSelectedStateId ) ; if ( newSelectedStateId !== null ) { e . cancelBubble = true ; } break ; case "f" : searchTree . toggleAutoFit ( ) ; break ; case "F" : searchTree . fitTree ( ) ; break ; } if ( e . key in shapeMap ) { searchTree . changeSelectedNodeShape ( shapeMap . get ( e . key ) ! ) ; } if ( Number . isFinite ( parseInt ( e . key ) ) ) { const digit = parseInt ( e . key ) ; findingStateWithDigit ( digit ) ; } } let stateIdToFind = 0 ; /** Timeout for keyboard typing state ID. */ let stateFindingTimeout ; param digit function findingStateWithDigit ( digit ) { stateIdToFind = stateIdToFind * 10 + digit ; if ( stateFindingTimeout ) { clearTimeout ( stateFindingTimeout ) ; } stateFindingTimeout = setTimeout ( ( ) => findState ( ) , 1000 ) ; } function findState ( ) { try { onStateSelected ( stateIdToFind ) ; } catch ( ex ) { console . log ( "Cannot find find state with id " + stateIdToFind + ". Error: " + ex ) ; } stateIdToFind = 0 ; } param actionName todo export function navigateToChildOfSelectedState ( actionName ) { if ( selectedStateId ) { const newSelectedStateId = searchTree . navigateToChildState ( selectedStateId , actionName ) ; if ( newSelectedStateId !== null ) { onStateSelected ( newSelectedStateId ) ; } } } getElementByIdOrThrow ( "toggleStateLogButton" ) . onclick = ( ) : void => toggleStateLog ( ) ; function toggleStateLog ( ) { postCommand ( 'toggleStateLog' ) ; } param logFilePath function showStateLogButton ( logFilePath ) { const button = getElementByIdOrThrow ( "toggleStateLogButton" ) ; if ( logFilePath ) { button . style . color = "green" ; button . title = "State selection synchronized with log file file: " + logFilePath ; } else { button . style . color = "red" ; button . title = "State log file synchronization disabled. Click here to re-enable." ; } } function requestFinalState ( _planView ) { vscode ?. postMessage ( { 'command' : 'finalStateDataRequest' , 'stateId' : selectedStateId } ) ; } function showFinalState ( data ) { if ( data . planIndex === selectedStateId ) { planViz . showFinalState ( data . finalState ) ; } }