From da861719c6433a1fc9346da958f0907e52d578ce Mon Sep 17 00:00:00 2001 From: Georgios Andreadis Date: Thu, 21 Sep 2017 10:20:50 +0200 Subject: Show experiment and trace data on left-hand sidebar --- .../simulation/ExperimentMetadataComponent.js | 12 ++++++ .../simulation/SimulationSidebarComponent.js | 21 +++++++++++ .../simulation/SimulationSidebarComponent.sass | 8 ++++ .../sidebars/simulation/TaskComponent.js | 31 ++++++++++++++++ .../sidebars/simulation/TraceComponent.js | 17 +++++++++ src/components/timeline/Timeline.js | 15 -------- src/components/timeline/TimelineComponent.js | 43 ++++++++++++++++++++++ .../simulation/ExperimentMetadataContainer.js | 29 +++++++++++++++ .../sidebars/simulation/TaskContainer.js | 23 ++++++++++++ .../sidebars/simulation/TraceContainer.js | 22 +++++++++++ src/containers/timeline/TimelineContainer.js | 37 +++++++++++++++++++ .../timeline/TimelineControlsContainer.js | 4 +- src/pages/App.js | 9 ++++- src/reducers/construction-mode.js | 3 ++ src/reducers/interaction-level.js | 7 ++++ src/reducers/modals.js | 2 + src/reducers/simulation-mode.js | 9 +++++ src/sagas/experiments.js | 28 +++++++++++++- 18 files changed, 301 insertions(+), 19 deletions(-) create mode 100644 src/components/sidebars/simulation/ExperimentMetadataComponent.js create mode 100644 src/components/sidebars/simulation/SimulationSidebarComponent.js create mode 100644 src/components/sidebars/simulation/SimulationSidebarComponent.sass create mode 100644 src/components/sidebars/simulation/TaskComponent.js create mode 100644 src/components/sidebars/simulation/TraceComponent.js delete mode 100644 src/components/timeline/Timeline.js create mode 100644 src/components/timeline/TimelineComponent.js create mode 100644 src/containers/sidebars/simulation/ExperimentMetadataContainer.js create mode 100644 src/containers/sidebars/simulation/TaskContainer.js create mode 100644 src/containers/sidebars/simulation/TraceContainer.js create mode 100644 src/containers/timeline/TimelineContainer.js diff --git a/src/components/sidebars/simulation/ExperimentMetadataComponent.js b/src/components/sidebars/simulation/ExperimentMetadataComponent.js new file mode 100644 index 00000000..26383d6a --- /dev/null +++ b/src/components/sidebars/simulation/ExperimentMetadataComponent.js @@ -0,0 +1,12 @@ +import React from "react"; + +const ExperimentMetadataComponent = ({experimentName, pathName, traceName, schedulerName}) => ( +
+

{experimentName}

+

Path: {pathName}

+

Trace: {traceName}

+

Scheduler: {schedulerName}

+
+); + +export default ExperimentMetadataComponent; diff --git a/src/components/sidebars/simulation/SimulationSidebarComponent.js b/src/components/sidebars/simulation/SimulationSidebarComponent.js new file mode 100644 index 00000000..7da064c4 --- /dev/null +++ b/src/components/sidebars/simulation/SimulationSidebarComponent.js @@ -0,0 +1,21 @@ +import React from "react"; +import ExperimentMetadataContainer from "../../../containers/sidebars/simulation/ExperimentMetadataContainer"; +import TraceContainer from "../../../containers/sidebars/simulation/TraceContainer"; +import Sidebar from "../Sidebar"; +import "./SimulationSidebarComponent.css"; + +const SimulationSidebarComponent = () => { + return ( + +
+ +
+
+ +
+
+
+ ); +}; + +export default SimulationSidebarComponent; diff --git a/src/components/sidebars/simulation/SimulationSidebarComponent.sass b/src/components/sidebars/simulation/SimulationSidebarComponent.sass new file mode 100644 index 00000000..82af97fa --- /dev/null +++ b/src/components/sidebars/simulation/SimulationSidebarComponent.sass @@ -0,0 +1,8 @@ +.simulation-sidebar-container + display: flex + height: 100% + max-height: 100% + +.trace-container + flex: 1 + overflow-y: scroll diff --git a/src/components/sidebars/simulation/TaskComponent.js b/src/components/sidebars/simulation/TaskComponent.js new file mode 100644 index 00000000..9a26e720 --- /dev/null +++ b/src/components/sidebars/simulation/TaskComponent.js @@ -0,0 +1,31 @@ +import React from "react"; +import {convertSecondsToFormattedTime} from "../../../util/date-time"; + +const TaskComponent = ({task, flopsLeft}) => { + let stateInfo; + + if (flopsLeft === task.totalFlopCount) { + stateInfo =

Waiting

; + } else if (flopsLeft > 0) { + stateInfo = ( +

+ + Running ({task.totalFlopCount - flopsLeft} / {task.totalFlopCount} FLOPS) +

+ ); + } else { + stateInfo =

Completed

; + } + + return ( +
  • +
    +
    {task.totalFlopCount} FLOPS
    + Starts: {convertSecondsToFormattedTime(task.startTick)} +
    + {stateInfo} +
  • + ); +}; + +export default TaskComponent; diff --git a/src/components/sidebars/simulation/TraceComponent.js b/src/components/sidebars/simulation/TraceComponent.js new file mode 100644 index 00000000..498fe5bf --- /dev/null +++ b/src/components/sidebars/simulation/TraceComponent.js @@ -0,0 +1,17 @@ +import React from "react"; +import TaskContainer from "../../../containers/sidebars/simulation/TaskContainer"; + +const TraceComponent = ({jobs}) => ( +
    +

    Trace

    + {jobs.map(job => ( + + ))} +
    +); + +export default TraceComponent; diff --git a/src/components/timeline/Timeline.js b/src/components/timeline/Timeline.js deleted file mode 100644 index a2a858eb..00000000 --- a/src/components/timeline/Timeline.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from "react"; -import TimelineControlsContainer from "../../containers/timeline/TimelineControlsContainer"; -import TimelineLabelsContainer from "../../containers/timeline/TimelineLabelsContainer"; -import "./Timeline.css"; - -const Timeline = ({currentTick, lastSimulatedTick}) => ( -
    -
    - - -
    -
    -); - -export default Timeline; diff --git a/src/components/timeline/TimelineComponent.js b/src/components/timeline/TimelineComponent.js new file mode 100644 index 00000000..b400a378 --- /dev/null +++ b/src/components/timeline/TimelineComponent.js @@ -0,0 +1,43 @@ +import React from "react"; +import TimelineControlsContainer from "../../containers/timeline/TimelineControlsContainer"; +import TimelineLabelsContainer from "../../containers/timeline/TimelineLabelsContainer"; +import "./Timeline.css"; + +class TimelineComponent extends React.Component { + componentDidMount() { + this.interval = setInterval(() => { + if (!this.props.isPlaying) { + return; + } + + if (this.props.currentTick < this.props.lastSimulatedTick) { + for (let i in this.props.sections.reverse()) { + if (this.props.currentTick + 1 >= this.props.sections[i].startTick) { + if (this.props.currentDatacenterId !== this.props.sections[i].datacenterId) { + this.props.setCurrentDatacenter(this.props.sections[i].datacenterId); + } + break; + } + } + this.props.incrementTick(); + } + }, 1000); + } + + componentWillUnmount() { + clearInterval(this.interval); + } + + render() { + return ( +
    +
    + + +
    +
    + ); + } +} + +export default TimelineComponent; diff --git a/src/containers/sidebars/simulation/ExperimentMetadataContainer.js b/src/containers/sidebars/simulation/ExperimentMetadataContainer.js new file mode 100644 index 00000000..55323f11 --- /dev/null +++ b/src/containers/sidebars/simulation/ExperimentMetadataContainer.js @@ -0,0 +1,29 @@ +import {connect} from "react-redux"; +import ExperimentMetadataComponent from "../../../components/sidebars/simulation/ExperimentMetadataComponent"; + +const mapStateToProps = state => { + if (!state.objects.experiment[state.currentExperimentId]) { + return { + experimentName: "Loading experiment", + pathName: "", + traceName: "", + schedulerName: "", + } + } + + const path = state.objects.path[state.objects.experiment[state.currentExperimentId].pathId]; + const pathName = path.name ? path.name : "Path " + path.id; + + return { + experimentName: state.objects.experiment[state.currentExperimentId].name, + pathName, + traceName: state.objects.trace[state.objects.experiment[state.currentExperimentId].traceId].name, + schedulerName: state.objects.scheduler[state.objects.experiment[state.currentExperimentId].schedulerName].name, + }; +}; + +const ExperimentMetadataContainer = connect( + mapStateToProps +)(ExperimentMetadataComponent); + +export default ExperimentMetadataContainer; diff --git a/src/containers/sidebars/simulation/TaskContainer.js b/src/containers/sidebars/simulation/TaskContainer.js new file mode 100644 index 00000000..df06b5b8 --- /dev/null +++ b/src/containers/sidebars/simulation/TaskContainer.js @@ -0,0 +1,23 @@ +import {connect} from "react-redux"; +import TaskComponent from "../../../components/sidebars/simulation/TaskComponent"; + +const mapStateToProps = (state, ownProps) => { + let flopsLeft = state.objects.task[ownProps.taskId].totalFlopCount; + + if (state.states.task[state.currentTick] && state.states.task[state.currentTick][ownProps.taskId]) { + flopsLeft = state.states.task[state.currentTick][ownProps.taskId].flopsLeft; + } else if (state.objects.task[ownProps.taskId].startTick < state.currentTick) { + flopsLeft = 0; + } + + return { + task: state.objects.task[ownProps.taskId], + flopsLeft, + }; +}; + +const TaskContainer = connect( + mapStateToProps +)(TaskComponent); + +export default TaskContainer; diff --git a/src/containers/sidebars/simulation/TraceContainer.js b/src/containers/sidebars/simulation/TraceContainer.js new file mode 100644 index 00000000..6539823d --- /dev/null +++ b/src/containers/sidebars/simulation/TraceContainer.js @@ -0,0 +1,22 @@ +import {connect} from "react-redux"; +import TraceComponent from "../../../components/sidebars/simulation/TraceComponent"; + +const mapStateToProps = state => { + if (!state.objects.experiment[state.currentExperimentId] || + !state.objects.trace[state.objects.experiment[state.currentExperimentId].traceId].jobIds) { + return { + jobs: [] + }; + } + + return { + jobs: state.objects.trace[state.objects.experiment[state.currentExperimentId].traceId].jobIds + .map(id => state.objects.job[id]), + }; +}; + +const TraceContainer = connect( + mapStateToProps +)(TraceComponent); + +export default TraceContainer; diff --git a/src/containers/timeline/TimelineContainer.js b/src/containers/timeline/TimelineContainer.js new file mode 100644 index 00000000..32756b6d --- /dev/null +++ b/src/containers/timeline/TimelineContainer.js @@ -0,0 +1,37 @@ +import {connect} from "react-redux"; +import {incrementTick} from "../../actions/simulation/tick"; +import {setCurrentDatacenter} from "../../actions/topology/building"; +import TimelineComponent from "../../components/timeline/TimelineComponent"; + +const mapStateToProps = state => { + let sections = []; + if (state.currentExperimentId !== -1) { + const sectionIds = state.objects.path[state.objects.experiment[state.currentExperimentId].pathId].sectionIds; + + if (sectionIds) { + sections = sectionIds.map(sectionId => state.objects.section[sectionId]); + } + } + + return { + isPlaying: state.isPlaying, + currentTick: state.currentTick, + lastSimulatedTick: state.lastSimulatedTick, + currentDatacenterId: state.currentDatacenterId, + sections, + }; +}; + +const mapDispatchToProps = dispatch => { + return { + incrementTick: () => dispatch(incrementTick()), + setCurrentDatacenter: id => dispatch(setCurrentDatacenter(id)) + }; +}; + +const TimelineContainer = connect( + mapStateToProps, + mapDispatchToProps +)(TimelineComponent); + +export default TimelineContainer; diff --git a/src/containers/timeline/TimelineControlsContainer.js b/src/containers/timeline/TimelineControlsContainer.js index e5c89060..1afd336a 100644 --- a/src/containers/timeline/TimelineControlsContainer.js +++ b/src/containers/timeline/TimelineControlsContainer.js @@ -5,7 +5,9 @@ const mapStateToProps = state => { let sectionTicks = []; if (state.currentExperimentId !== -1) { const sectionIds = state.objects.path[state.objects.experiment[state.currentExperimentId].pathId].sectionIds; - sectionTicks = sectionIds.map(sectionId => state.objects.section[sectionId].startTick); + if (sectionIds) { + sectionTicks = sectionIds.map(sectionId => state.objects.section[sectionId].startTick); + } } return { diff --git a/src/pages/App.js b/src/pages/App.js index 87c139ec..26ded58c 100644 --- a/src/pages/App.js +++ b/src/pages/App.js @@ -7,7 +7,7 @@ import {openSimulationSucceeded} from "../actions/simulations"; import {resetCurrentDatacenter} from "../actions/topology/building"; import LoadingScreen from "../components/map/LoadingScreen"; import AppNavbar from "../components/navigation/AppNavbar"; -import Timeline from "../components/timeline/Timeline"; +import SimulationSidebarComponent from "../components/sidebars/simulation/SimulationSidebarComponent"; import MapStage from "../containers/map/MapStage"; import DeleteMachineModal from "../containers/modals/DeleteMachineModal"; import DeleteRackModal from "../containers/modals/DeleteRackModal"; @@ -15,6 +15,7 @@ import DeleteRoomModal from "../containers/modals/DeleteRoomModal"; import EditRackNameModal from "../containers/modals/EditRackNameModal"; import EditRoomNameModal from "../containers/modals/EditRoomNameModal"; import TopologySidebar from "../containers/sidebars/topology/TopologySidebar"; +import TimelineContainer from "../containers/timeline/TimelineContainer"; import KeymapConfiguration from "../shortcuts/keymap"; const shortcutManager = new ShortcutManager(KeymapConfiguration); @@ -56,7 +57,11 @@ class AppComponent extends React.Component { {this.props.inSimulation ? - : + : + undefined + } + {this.props.inSimulation ? + : undefined } diff --git a/src/reducers/construction-mode.js b/src/reducers/construction-mode.js index 3e0b7542..e97c817e 100644 --- a/src/reducers/construction-mode.js +++ b/src/reducers/construction-mode.js @@ -1,4 +1,5 @@ import {combineReducers} from "redux"; +import {OPEN_EXPERIMENT_SUCCEEDED} from "../actions/experiments"; import { CANCEL_NEW_ROOM_CONSTRUCTION_SUCCEEDED, FINISH_NEW_ROOM_CONSTRUCTION, @@ -12,6 +13,7 @@ export function currentRoomInConstruction(state = -1, action) { return action.roomId; case CANCEL_NEW_ROOM_CONSTRUCTION_SUCCEEDED: case FINISH_NEW_ROOM_CONSTRUCTION: + case OPEN_EXPERIMENT_SUCCEEDED: return -1; default: return state; @@ -23,6 +25,7 @@ export function inRackConstructionMode(state = false, action) { case START_RACK_CONSTRUCTION: return true; case STOP_RACK_CONSTRUCTION: + case OPEN_EXPERIMENT_SUCCEEDED: return false; default: return state; diff --git a/src/reducers/interaction-level.js b/src/reducers/interaction-level.js index a3f3de7f..282e5096 100644 --- a/src/reducers/interaction-level.js +++ b/src/reducers/interaction-level.js @@ -1,12 +1,19 @@ +import {OPEN_EXPERIMENT_SUCCEEDED} from "../actions/experiments"; import { GO_DOWN_ONE_INTERACTION_LEVEL, GO_FROM_BUILDING_TO_ROOM, GO_FROM_RACK_TO_MACHINE, GO_FROM_ROOM_TO_RACK } from "../actions/interaction-level"; +import {OPEN_SIMULATION_SUCCEEDED} from "../actions/simulations"; export function interactionLevel(state = {mode: "BUILDING"}, action) { switch (action.type) { + case OPEN_EXPERIMENT_SUCCEEDED: + case OPEN_SIMULATION_SUCCEEDED: + return { + mode: "BUILDING" + }; case GO_FROM_BUILDING_TO_ROOM: return { mode: "ROOM", diff --git a/src/reducers/modals.js b/src/reducers/modals.js index 9f73c6ec..3e9f0327 100644 --- a/src/reducers/modals.js +++ b/src/reducers/modals.js @@ -1,4 +1,5 @@ import {combineReducers} from "redux"; +import {OPEN_EXPERIMENT_SUCCEEDED} from "../actions/experiments"; import {CLOSE_NEW_EXPERIMENT_MODAL, OPEN_NEW_EXPERIMENT_MODAL} from "../actions/modals/experiments"; import {CLOSE_DELETE_PROFILE_MODAL, OPEN_DELETE_PROFILE_MODAL} from "../actions/modals/profile"; import {CLOSE_NEW_SIMULATION_MODAL, OPEN_NEW_SIMULATION_MODAL} from "../actions/modals/simulations"; @@ -21,6 +22,7 @@ function modal(openAction, closeAction) { case openAction: return true; case closeAction: + case OPEN_EXPERIMENT_SUCCEEDED: return false; default: return state; diff --git a/src/reducers/simulation-mode.js b/src/reducers/simulation-mode.js index 60084824..b13ecbcc 100644 --- a/src/reducers/simulation-mode.js +++ b/src/reducers/simulation-mode.js @@ -2,11 +2,14 @@ import {OPEN_EXPERIMENT_SUCCEEDED} from "../actions/experiments"; import {CHANGE_LOAD_METRIC} from "../actions/simulation/load-metric"; import {SET_PLAYING} from "../actions/simulation/playback"; import {GO_TO_TICK, SET_LAST_SIMULATED_TICK} from "../actions/simulation/tick"; +import {OPEN_SIMULATION_SUCCEEDED} from "../actions/simulations"; export function currentExperimentId(state = -1, action) { switch (action.type) { case OPEN_EXPERIMENT_SUCCEEDED: return action.experimentId; + case OPEN_SIMULATION_SUCCEEDED: + return -1; default: return state; } @@ -16,6 +19,8 @@ export function currentTick(state = 0, action) { switch (action.type) { case GO_TO_TICK: return action.tick; + case OPEN_EXPERIMENT_SUCCEEDED: + return 0; default: return state; } @@ -34,6 +39,8 @@ export function isPlaying(state = false, action) { switch (action.type) { case SET_PLAYING: return action.playing; + case OPEN_EXPERIMENT_SUCCEEDED: + return false; default: return state; } @@ -43,6 +50,8 @@ export function lastSimulatedTick(state = -1, action) { switch (action.type) { case SET_LAST_SIMULATED_TICK: return action.tick; + case OPEN_EXPERIMENT_SUCCEEDED: + return -1; default: return state; } diff --git a/src/sagas/experiments.js b/src/sagas/experiments.js index cf92e97f..29f9a211 100644 --- a/src/sagas/experiments.js +++ b/src/sagas/experiments.js @@ -1,15 +1,23 @@ import {call, put, select} from "redux-saga/effects"; import {addPropToStoreObject, addToStore} from "../actions/objects"; import {deleteExperiment, getExperiment} from "../api/routes/experiments"; -import {addExperiment, getExperimentsOfSimulation} from "../api/routes/simulations"; +import {getTasksOfJob} from "../api/routes/jobs"; +import {addExperiment, getExperimentsOfSimulation, getSimulation} from "../api/routes/simulations"; +import {getJobsOfTrace} from "../api/routes/traces"; import {fetchAndStoreAllSchedulers, fetchAndStoreAllTraces, fetchAndStorePathsOfSimulation} from "./objects"; import {fetchAllDatacentersOfExperiment} from "./topology"; export function* onOpenExperimentSucceeded(action) { try { + const simulation = yield call(getSimulation, action.simulationId); + yield put(addToStore("simulation", simulation)); + const experiment = yield call(getExperiment, action.experimentId); yield put(addToStore("experiment", experiment)); + yield fetchExperimentSpecifications(); + yield fetchWorkloadOfTrace(experiment.traceId); + yield fetchAllDatacentersOfExperiment(experiment); } catch (error) { console.error(error); @@ -43,6 +51,24 @@ function* fetchExperimentSpecifications() { } } +function* fetchWorkloadOfTrace(traceId) { + try { + const jobs = yield call(getJobsOfTrace, traceId); + for (let i in jobs) { + const job = jobs[i]; + const tasks = yield call(getTasksOfJob, job.id); + job.taskIds = tasks.map(task => task.id); + for (let j in tasks) { + yield put(addToStore("task", tasks[j])); + } + yield put(addToStore("job", job)); + } + yield put(addPropToStoreObject("trace", traceId, {jobIds: jobs.map(job => job.id)})) + } catch (error) { + console.error(error); + } +} + export function* onAddExperiment(action) { try { const currentSimulationId = yield select(state => state.currentSimulationId); -- cgit v1.2.3